Arma Reforger Explorer 1.7.0.54
Arma Reforger Code Explorer by Zeroy - Thanks to MisterOutofTime
Loading...
Searching...
No Matches
SCR_BaseDamageHealSupportStationComponent.c
Go to the documentation of this file.
1[ComponentEditorProps(category: "GameScripted/SupportStation", description: "")]
3{
4 [Attribute("SOUND_VEHICLE_REPAIR_PARTIAL", desc: "Sound effect played when heal is updated and not done. Broadcast to players. Leave empty if no sfx", category: "Heal/Repair Support Station")]
6
7 [Attribute(desc: "Sound effect played when Damage over Time is removed (Other than fire). Broadcast to players. Leave empty if no sfx", category: "Heal/Repair Support Station")]
9
10 protected ref SCR_AudioSourceConfiguration m_OnHealUpdateAudioSourceConfiguration;
11 protected ref SCR_AudioSourceConfiguration m_OnDoTRemovedAudioSourceConfiguration;
12
13 //------------------------------------------------------------------------------------------------
18 SCR_AudioSourceConfiguration CreateSoundAudioConfig(string soundEventName)
19 {
20 //~ Does not have audio assigned
22 return null;
23
24 //~ Create Audio source
25 SCR_AudioSourceConfiguration audioSourceConfiguration = new SCR_AudioSourceConfiguration();
26 audioSourceConfiguration.m_sSoundProject = m_sOnUseSoundEffectFile;
27 audioSourceConfiguration.m_sSoundEventName = soundEventName;
28
29 if (!CanMoveWithPhysics())
30 audioSourceConfiguration.m_eFlags = SCR_Enum.SetFlag(audioSourceConfiguration.m_eFlags, EAudioSourceConfigurationFlag.Static);
31 else
32 audioSourceConfiguration.m_eFlags = SCR_Enum.RemoveFlag(audioSourceConfiguration.m_eFlags, EAudioSourceConfigurationFlag.Static);
33
34 return audioSourceConfiguration;
35 }
36
37 //------------------------------------------------------------------------------------------------
47
48 //------------------------------------------------------------------------------------------------
58}
59
60class SCR_BaseDamageHealSupportStationComponent : SCR_BaseSupportStationComponent
61{
62 [Attribute("500", desc: "Max damage healed each execute. If you hold the action it will heal each time the action ends", category: "Heal/Repair Support Station", params: "0.01 inf")]
63 protected float m_iDamageHealedEachExecution;
65 [Attribute("100", desc: "Supply cost for ever 'Damage Healed Each Execution' damage healed. If the left over damage is less then it will of course be cheaper with a min value of 1", category: "Heal/Repair Support Station", params: "0 inf")]
66 protected int m_iSupplyCostDamageHealed;
68 [Attribute("1", desc: "The max percentage that this support station can heal scaled. 1 == 100%, This is not calculated as a whole but for each hitZone. Aka if hitZone A is less than this percentage then the heal action can be executed even if total health is more than this %", params: "0.01 1", category: "Heal/Repair Support Station")]
69 protected float m_fMaxHealScaled;
71 //~ Hotfixed
72 [Attribute("0.05", desc: "Hotfix: If server then this value is added on top of the m_fMaxHealScaled. This is because there is a potential desync of around 5% between client and server. EG: client can only heal up to 20% but server up to 25% so if client things the health is 19% and server things it is 21% the heal is still executed up to 25%", params: "0 0.10", category: "Heal/Repair Support Station")]
73 protected float m_fServerAddedMaxHealScaled;
74
75 [Attribute(desc: "The DPS types that cost supplies to be healed/repaired. It will still remove the DPS effect even if it is not included here. It simply does not cost extra", uiwidget: UIWidgets.SearchComboBox, enumType:EDamageType, category: "Heal/Repair Support Station")]
76 protected ref array<EDamageType> m_aDoTTypesHealed;
78 [Attribute("100", desc: "Supplies needed to heal conditions such as bleeding. Ignored if 0 or m_aDoTTypesHealed is empty. Fire uses own supply calculation", params: "0 inf 1", category: "Heal/Repair Support Station")]
79 protected int m_iSuppliesPerDoTHealed;
80
81 [Attribute(ESupportStationReasonInvalid.HEAL_MAX_HEALABLE_HEALTH_REACHED_EMERGENCY.ToString(), desc: "Invalid reason when healing is done but full health not reached. For repair vehicle this is HEAL_MAX_HEALABLE_HEALTH_REACHED_FIELD for repair wrench only this is HEAL_MAX_HEALABLE_HEALTH_REACHED_EMERGENCY", uiwidget: UIWidgets.SearchComboBox, enumType:ESupportStationReasonInvalid, category: "Heal/Repair Support Station")]
82 protected ESupportStationReasonInvalid m_eMaxHealDone;
83
84 //~ Damage that can be healed if there are less supplies than the entire damage that can be healed
85 protected float m_fMaxDamageToHealSupplyCap = -1;
86
87 //------------------------------------------------------------------------------------------------
88 //~ Hotfix to prevent the heal action to be stuck because of the potential 5% desycn of health between server and client
89 protected float GetMaxHealScaled()
90 {
91 if (m_fMaxHealScaled >= 1 || Replication.IsClient())
92 return m_fMaxHealScaled;
93 else
94 return Math.Clamp(m_fMaxHealScaled + m_fServerAddedMaxHealScaled, m_fMaxHealScaled, 1);
95 }
96
97 //------------------------------------------------------------------------------------------------
98 override bool IsValid(IEntity actionOwner, IEntity actionUser, SCR_BaseUseSupportStationAction action, vector actionPosition, out ESupportStationReasonInvalid reasonInvalid, out int supplyAmount)
99 {
101 {
102 Debug.Error2("SCR_BaseDamageHealSupportStationComponent", "'IsValid' fails as the damage heal support station is executed with a non SCR_BaseDamageHealSupportStationAction action! This is will break the support station!");
103 return false;
104 }
105
106 if (!super.IsValid(actionOwner, actionUser, action, actionPosition, reasonInvalid, supplyAmount))
107 return false;
108
109 EDamageType activeDot;
110 array<HitZone> hitZones = {};
111 float damageToHeal = GetDamageOrStateToHeal(actionOwner, actionUser, SCR_BaseDamageHealSupportStationAction.Cast(action), activeDot, hitZones);
112
113 if (activeDot == -1 && damageToHeal <= 0)
114 {
115 foreach (HitZone hitzone : hitZones)
116 {
117 //~ No damage can be healed yet a hitzone is not full heath
118 if (hitzone.GetDamageState() != EDamageState.UNDAMAGED)
119 {
120 reasonInvalid = m_eMaxHealDone;
121 return false;
122 }
123 }
124
125 //~ Entity has no damage
126 reasonInvalid = ESupportStationReasonInvalid.HEAL_ENTITY_UNDAMAGED;
127 return false;
128 }
129
130 return true;
131 }
132
133 //------------------------------------------------------------------------------------------------
134 protected float GetDamageOrStateToHeal(IEntity actionOwner, IEntity actionUser, notnull SCR_BaseDamageHealSupportStationAction action, out EDamageType activeDoT, out notnull array<HitZone> hitZones)
135 {
136 action.GetHitZonesToHeal(hitZones);
137
138 activeDoT = -1;
139
140 //~ Has DoT so this gets priority over healing damage (Excluding fire)
141 foreach (EDamageType DoT : m_aDoTTypesHealed)
142 {
143 foreach (HitZone hitZone : hitZones)
144 {
145 if (hitZone.GetDamageOverTime(DoT))
146 {
147 activeDoT = DoT;
148 return 1;
149 }
150 }
151 }
152
153 //~ Get amount healed (Cap to damage healed each execute)
154 return Math.Clamp(action.GetActionDamageManager().GetHitZonesDamage(GetMaxHealScaled(), hitZones), 0, m_iDamageHealedEachExecution);
155 }
156
157 //------------------------------------------------------------------------------------------------
158 protected override int GetSupplyAmountAction(IEntity actionOwner, IEntity actionUser, SCR_BaseUseSupportStationAction action)
159 {
160 m_fMaxDamageToHealSupplyCap = -1;
161
162 if (!AreSuppliesEnabled())
163 return 0;
164
165 EDamageType activeDoT;
166 array<HitZone> hitZones = {};
167
168 float damageToHeal = GetDamageOrStateToHeal(actionOwner, actionUser, SCR_BaseDamageHealSupportStationAction.Cast(action), activeDoT, hitZones);
169
170 //~ Has DoT, this is always healed first
171 if (activeDoT != -1)
172 {
173 return m_iSuppliesPerDoTHealed + m_iBaseSupplyCostOnUse;
174 }
175
176 //~ No damage to heal
177 if (damageToHeal <= 0)
178 return 0;
179
180 //~ Calculate the supply cost
181 float damageHealCost = (damageToHeal / m_iDamageHealedEachExecution) * m_iSupplyCostDamageHealed;
182
183 //~ Check if there aren't enough supplies for the heal action how much damage can be healed instead. The available supplies should be at least >= than baseSupplyCost + 1
184 float availableSupplies = GetMaxAvailableSupplies();
185 if (availableSupplies > 0 && damageHealCost > 0 && (damageHealCost + m_iBaseSupplyCostOnUse) > availableSupplies && availableSupplies >= m_iBaseSupplyCostOnUse + 1)
186 {
187 //~ Calculate how many can be healed depending on how many supplies there are
188 m_fMaxDamageToHealSupplyCap = (m_iDamageHealedEachExecution / m_iSupplyCostDamageHealed) * availableSupplies;
189 return availableSupplies;
190 }
191 //~ If there aren't enough supplies show the cost of the action for the min cost it can be executed.
192 else if (availableSupplies < m_iBaseSupplyCostOnUse + 1)
193 {
194 return m_iBaseSupplyCostOnUse + 1;
195 }
196
197 return Math.Ceil(damageHealCost + m_iBaseSupplyCostOnUse);
198 }
199
200 //------------------------------------------------------------------------------------------------
201 override void OnExecutedServer(notnull IEntity actionOwner, notnull IEntity actionUser, notnull SCR_BaseUseSupportStationAction action)
202 {
203 //~ Consume supplies
204 if (AreSuppliesEnabled())
205 {
206 //~ Failed to consume supplies, meaning there weren't enough supplies for the action. Also sets m_fMaxDamageToHealSupplyCap GetSupplyCostAction to make sure the system can still heal even if there is a limited amount of supplies
207 if (!OnConsumeSuppliesServer(GetSupplyAmountAction(actionOwner, actionUser, action)))
208 return;
209 }
210
212
213 //~ Get damage to heal
214 EDamageType activeDoT;
215 array<HitZone> hitZones = {};
216 float damageToHeal = GetDamageOrStateToHeal(actionOwner, actionUser, damageHealAction, activeDoT, hitZones);
217
218 //~ There are not enough supplies to heal full damage. So heal to amount supplies allow it
219 if (m_fMaxDamageToHealSupplyCap > 0 && m_fMaxDamageToHealSupplyCap < damageToHeal)
220 damageToHeal = m_fMaxDamageToHealSupplyCap;
221
222 //~ Get broadcast ids
223 RplId ownerId;
224 RplId userId;
225 int playerId;
227 FindEntityIds(actionOwner, actionUser, ownerId, userId, playerId);
228
229 //~ Remove the active Damage over Time
230 if (activeDoT != -1)
231 {
232 healState = SCR_EDamageSupportStationHealState.DOT_REMOVED;
233
234 foreach (HitZone hitZone : hitZones)
235 {
236 hitZone.SetDamageOverTime(activeDoT, 0);
237 }
238
239 //~ Execute default logics
240 OnExecuteDamageSystem(actionOwner, actionUser, healState, SCR_BaseDamageHealSupportStationAction.Cast(action), 0);
241 Rpc(OnExecuteDamageSystemBroadcast, ownerId, userId, healState, action.GetActionID(), 0);
242 return;
243 }
244
245 SCR_DamageManagerComponent damageManager = damageHealAction.GetActionDamageManager();
246 if (!damageManager)
247 return;
248
249 float maxHealScaled = GetMaxHealScaled();
250
251 //~ No DoT so get hitZones to heal
252 damageManager.HealHitZones(damageToHeal, true, maxHealScaled, hitZones);
253
254 //~ The support station can still heal more
255 if (damageManager.GetHitZonesDamage(maxHealScaled, hitZones) > 0)
256 {
257 healState = SCR_EDamageSupportStationHealState.HEAL_UPDATE;
258 }
259 else
260 {
261 //~ Entity health is full health
262 if (maxHealScaled >= 1)
263 healState = SCR_EDamageSupportStationHealState.HEAL_DONE;
264 //~ The support station reached the max health
265 else
266 healState = SCR_EDamageSupportStationHealState.HEAL_DONE_NOT_FULL_HEAL;
267 }
268
269 //~ Get the current percentage of healing and if all hitZones are healed to the max
270 float healthScaled = damageHealAction.GetActionDamageManager().GetSingleHitZonesHealthScaled(hitZones);
271
272 OnExecuteDamageSystem(actionOwner, actionUser, healState, SCR_BaseDamageHealSupportStationAction.Cast(action), healthScaled);
273 Rpc(OnExecuteDamageSystemBroadcast, ownerId, userId, healState, action.GetActionID(), healthScaled);
274 }
275
276 //------------------------------------------------------------------------------------------------
277 protected override void OnExecute(IEntity actionOwner, IEntity actionUser, int playerId, SCR_BaseUseSupportStationAction action)
278 {
279 //~ Clear on execute so it does not call on complete as it has a custom on execute
280 }
281
282 //------------------------------------------------------------------------------------------------
283 [RplRpc(RplChannel.Reliable, RplRcver.Broadcast)]
284 protected void OnExecuteDamageSystemBroadcast(RplId ownerId, RplId userId, SCR_EDamageSupportStationHealState healState, int actionId, float healthScaled)
285 {
286 IEntity actionOwner, actionUser;
287 GetEntitiesFromID(ownerId, userId, actionOwner, actionUser);
288
290 if (actionOwner)
291 {
293
294 if (actionManager)
295 action = SCR_BaseUseSupportStationAction.Cast(actionManager.FindAction(actionId));
296 }
297
298 OnExecuteDamageSystem(actionOwner, actionUser, healState, SCR_BaseDamageHealSupportStationAction.Cast(action), healthScaled);
299 }
300
301 //------------------------------------------------------------------------------------------------
302 protected void OnExecuteDamageSystem(IEntity actionOwner, IEntity actionUser, SCR_EDamageSupportStationHealState healState, SCR_BaseDamageHealSupportStationAction action, float healthScaled)
303 {
304 //~ On succesfully executed
305 OnSuccessfullyExecuted(actionOwner, actionUser, action);
306
307 if (!actionOwner)
308 return;
309
310 //~ Play sound effect
312 if (classData)
313 {
314 switch (healState)
315 {
316 case SCR_EDamageSupportStationHealState.HEAL_UPDATE :
317 {
318 PlaySoundEffect(classData.GetOnHealUpdateAudioConfig(), actionOwner, action);
319 break;
320 }
321 case SCR_EDamageSupportStationHealState.HEAL_DONE_NOT_FULL_HEAL :
322 {
323 PlaySoundEffect(GetOnUseAudioConfig(), actionOwner, action);
324 break;
325 }
327 {
328 PlaySoundEffect(GetOnUseAudioConfig(), actionOwner, action);
329
330 //~ Play done voice event (if any) if character is fully healed
331 SCR_DamageManagerComponent damageManager = SCR_DamageManagerComponent.GetDamageManager(actionOwner);
332 if (damageManager)
333 {
334 if (damageManager.GetState() == EDamageState.UNDAMAGED)
335 PlayCharacterVoiceEvent(actionOwner);
336 }
337
338 break;
339 }
340 case SCR_EDamageSupportStationHealState.DOT_REMOVED :
341 {
342 PlaySoundEffect(classData.GetOnDoTRemovedAudioConfig(), actionOwner, action);
343 break;
344 }
345 }
346 }
347
348 //~ Do not send notification
350 return;
351
352 SendDamageSupportStationNotification(actionOwner, actionUser, action, healState, healthScaled);
353 }
354
355 //------------------------------------------------------------------------------------------------
356 protected void SendDamageSupportStationNotification(IEntity actionOwner, IEntity actionUser, SCR_BaseUseSupportStationAction action, SCR_EDamageSupportStationHealState healState, float healthScaled)
357 {
358 //~ Override in inherited classes
359 }
360}
361
enum EAIGroupCombatMode ComponentEditorProps(category:"GameScripted/AI", description:"Component for utility AI system for groups")
@ HEAL_UPDATE
To notify the system that health was updated but healing is not done.
@ BLOOD_DONE_NOT_FULL_HEAL
To notify the system that blood amount was updated and healing is done, but the entity is not full bl...
@ FIRE_EXTINGUISH_DONE
To notify the system that the fire extinguish was updated but the fire state was removed.
@ FIRE_EXTINGUISH_UPDATE
To notify the system that the fire extinguish was updated but the fire state is not removed.
@ BLOOD_DONE
To notify the system that blood amount was updated and healing is done.
@ BLOOD_UPDATE
To notify the system that blood amount was updated but healing is not done.
@ HEAL_DONE_NOT_FULL_HEAL
To notify the system that health was updated and healing is done. But the entity is not full heal aka...
@ HEAL_DONE
To notify the system that health was updated and healing is done.
@ DOT_REMOVED
To notify the system that a Damage over time effect was removed.
override int GetSupplyAmountAction(IEntity actionOwner, IEntity actionUser, SCR_BaseUseSupportStationAction action)
float GetDamageOrStateToHeal(IEntity actionOwner, IEntity actionUser, notnull SCR_BaseDamageHealSupportStationAction action, out EDamageType activeDoT, out notnull array< HitZone > hitZones)
override void OnExecute(IEntity actionOwner, IEntity actionUser, int playerId, SCR_BaseUseSupportStationAction action)
void OnExecuteDamageSystemBroadcast(RplId ownerId, RplId userId, SCR_EDamageSupportStationHealState healState, int actionId, float healthScaled)
void OnExecuteDamageSystem(IEntity actionOwner, IEntity actionUser, SCR_EDamageSupportStationHealState healState, SCR_BaseDamageHealSupportStationAction action, float healthScaled)
void SendDamageSupportStationNotification(IEntity actionOwner, IEntity actionUser, SCR_BaseUseSupportStationAction action, SCR_EDamageSupportStationHealState healState, float healthScaled)
override void OnExecutedServer(notnull IEntity actionOwner, notnull IEntity actionUser, notnull SCR_BaseUseSupportStationAction action)
bool GetSendNotificationOnUse()
void PlayCharacterVoiceEvent(IEntity character)
bool OnConsumeSuppliesServer(int amount)
void FindEntityIds(IEntity owner, IEntity user, out RplId ownerId, out RplId userId, out int playerId)
void GetEntitiesFromID(RplId ownerId, RplId userId, out IEntity owner, out IEntity user)
int GetMaxAvailableSupplies()
void OnSuccessfullyExecuted(IEntity actionOwner, IEntity actionUser, SCR_BaseUseSupportStationAction action)
void PlaySoundEffect(SCR_AudioSourceConfiguration audioConfig, notnull IEntity soundOwner, SCR_BaseUseSupportStationAction action)
SCR_AudioSourceConfiguration GetOnUseAudioConfig()
SCR_CharacterSoundComponentClass GetComponentData()
UI Textures DeployMenu Briefing conflict_HintBanner_1_UI desc
Definition Debug.c:13
proto external Managed FindComponent(typename typeName)
Definition Math.c:13
Main replication API.
Definition Replication.c:14
Replication item identifier.
Definition RplId.c:14
SCR_AudioSourceConfiguration CreateSoundAudioConfig(string soundEventName)
static bool IsEmptyOrWhiteSpace(string input)
IEntity GetOwner()
Owner entity of the fuel tank.
SCR_FieldOfViewSettings Attribute
EDamageType
Definition EDamageType.c:13
EDamageState
void RplRpc(RplChannel channel, RplRcver rcver, RplCondition condition=RplCondition.None, string customConditionName="")
Definition EnNetwork.c:95
RplRcver
Definition RplRcver.c:59
RplChannel
Communication channel. Reliable is guaranteed to be delivered. Unreliable not.
Definition RplChannel.c:14