From d03b452cbbfb9100132b6af5f3c86050d3672dda Mon Sep 17 00:00:00 2001 From: Anton Tarasenko Date: Wed, 6 Jul 2022 02:54:06 +0700 Subject: [PATCH] Add `OnDamage` signal for health component --- .../Frontend/Health/AHealthComponent.uc | 38 +++++++++++ .../Health/Events/Health_OnDamage_Signal.uc | 38 +++++++++++ .../Health/Events/Health_OnDamage_Slot.uc | 40 ++++++++++++ .../KF1Frontend/Health/KF1_HealthComponent.uc | 64 +++++++++++++++++-- 4 files changed, 176 insertions(+), 4 deletions(-) create mode 100644 sources/Gameplay/BaseClasses/Frontend/Health/Events/Health_OnDamage_Signal.uc create mode 100644 sources/Gameplay/BaseClasses/Frontend/Health/Events/Health_OnDamage_Slot.uc diff --git a/sources/Gameplay/BaseClasses/Frontend/Health/AHealthComponent.uc b/sources/Gameplay/BaseClasses/Frontend/Health/AHealthComponent.uc index 5221a45..f7d4235 100644 --- a/sources/Gameplay/BaseClasses/Frontend/Health/AHealthComponent.uc +++ b/sources/Gameplay/BaseClasses/Frontend/Health/AHealthComponent.uc @@ -21,6 +21,44 @@ class AHealthComponent extends AcediaObject abstract; +var protected Health_OnDamage_Signal onDamageSignal; + +protected function Constructor() +{ + onDamageSignal = Health_OnDamage_Signal( + _.memory.Allocate(class'Health_OnDamage_Signal')); +} + +protected function Finalizer() +{ + _.memory.Free(onDamageSignal); + onDamageSignal = none; +} + +/** + * Signal that will be emitted whenever trading time starts. + * + * [Signature] + * void (EPawn target, EPawn instigator, HashTable damageData) + * + * @param target Pawn that took damage. + * @param instigator Pawn responsible for dealing damage. + * @param damageData Data set related to damage. Exact stored values can + * differ based on the implementation, but any implementation must support + * at least 4 values: "damage" - `int` value representing amount of damage + * `target will be dealt, "originalDamage" - originally intended damage for + * `target`, before other event handlers altered "damage" value (you can + * modify this value, but you shouldn't), "hitLocation" - `Vector` that + * describes the point of contact with whatever dealt this damage and + * "momentum" - `Vector` value describing momentum transferred to `target` + * as a result of the damage dealt. + */ +/* SIGNAL */ +public final function Health_OnDamage_Slot OnDamage(AcediaObject receiver) +{ + return Health_OnDamage_Slot(onDamageSignal.NewSlot(receiver)); +} + defaultproperties { } \ No newline at end of file diff --git a/sources/Gameplay/BaseClasses/Frontend/Health/Events/Health_OnDamage_Signal.uc b/sources/Gameplay/BaseClasses/Frontend/Health/Events/Health_OnDamage_Signal.uc new file mode 100644 index 0000000..f1729a2 --- /dev/null +++ b/sources/Gameplay/BaseClasses/Frontend/Health/Events/Health_OnDamage_Signal.uc @@ -0,0 +1,38 @@ +/** + * Signal class for `AHealthComponent` on damage events. + * Copyright 2022 Anton Tarasenko + *------------------------------------------------------------------------------ + * This file is part of Acedia. + * + * Acedia is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3 of the License, or + * (at your option) any later version. + * + * Acedia is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Acedia. If not, see . + */ +class Health_OnDamage_Signal extends Signal; + +public final function Emit(EPawn target, EPawn instigator, HashTable damageData) +{ + local Slot nextSlot; + StartIterating(); + nextSlot = GetNextSlot(); + while (nextSlot != none) + { + Health_OnDamage_Slot(nextSlot).connect(target, instigator, damageData); + nextSlot = GetNextSlot(); + } + CleanEmptySlots(); +} + +defaultproperties +{ + relatedSlotClass = class'Health_OnDamage_Slot' +} \ No newline at end of file diff --git a/sources/Gameplay/BaseClasses/Frontend/Health/Events/Health_OnDamage_Slot.uc b/sources/Gameplay/BaseClasses/Frontend/Health/Events/Health_OnDamage_Slot.uc new file mode 100644 index 0000000..4ca47a2 --- /dev/null +++ b/sources/Gameplay/BaseClasses/Frontend/Health/Events/Health_OnDamage_Slot.uc @@ -0,0 +1,40 @@ +/** + * Slot class for `AHealthComponent` on damage events. + * Copyright 2022 Anton Tarasenko + *------------------------------------------------------------------------------ + * This file is part of Acedia. + * + * Acedia is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3 of the License, or + * (at your option) any later version. + * + * Acedia is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Acedia. If not, see . + */ +class Health_OnDamage_Slot extends Slot; + +delegate connect(EPawn target, EPawn instigator, HashTable damageData) +{ + DummyCall(); +} + +protected function Constructor() +{ + connect = none; +} + +protected function Finalizer() +{ + super.Finalizer(); + connect = none; +} + +defaultproperties +{ +} \ No newline at end of file diff --git a/sources/Gameplay/KF1Frontend/Health/KF1_HealthComponent.uc b/sources/Gameplay/KF1Frontend/Health/KF1_HealthComponent.uc index de7b923..c0ed1c8 100644 --- a/sources/Gameplay/KF1Frontend/Health/KF1_HealthComponent.uc +++ b/sources/Gameplay/KF1Frontend/Health/KF1_HealthComponent.uc @@ -49,12 +49,15 @@ class KF1_HealthComponent extends AHealthComponent */ var private const config bool replaceBloatAndSirenDamageTypes; +var private const int TDAMAGE, TORIGINAL_DAMAGE, THIT_LOCATION, TMOMENTUM; + var private LoggerAPI.Definition infoReplacingDamageTypes, errNoServerLevelCore; var private LoggerAPI.Definition infoRestoringReplacingDamageTypes; public function PseudoConstructor() { local LevelCore core; + _.unreal.gameRules.OnNetDamage(self).connect = OnNetDamageHandler; if (!replaceBloatAndSirenDamageTypes) { return; @@ -75,6 +78,7 @@ public function PseudoConstructor() protected function Finalizer() { + super.Finalizer(); _.unreal.gameRules.OnNetDamage(self).Disconnect(); _.unreal.gameRules.OnScoreKill(self).Disconnect(); if (replaceBloatAndSirenDamageTypes) { @@ -135,10 +139,55 @@ private function int OnNetDamageHandler( out Vector momentum, class damageType) { - if (damageType != class'Dummy_DamTypeVomit') return damage; - if (ZombieBloatBase(injured) != none) return 0; - if (ZombieFleshpoundBase(injured) != none) return 0; + damage = EmitDamageSignal( + originalDamage, + damage, + injured, + instigatedBy, + hitLocation, + momentum, + damageType); + if (damageType != class'Dummy_DamTypeVomit') { + return damage; + } + if (ZombieBloatBase(injured) != none) { + return 0; + } + if (ZombieFleshpoundBase(injured) != none) { + return 0; + } + return damage; +} +private function int EmitDamageSignal( + int originalDamage, + int damage, + Pawn injured, + Pawn instigatedBy, + Vector hitLocation, + out Vector momentum, + class damageType) +{ + local HashTable damageData; + local EPawn target, instigator; + + if (injured != none) { + target = class'EKFPawn'.static.Wrap(injured); + } + if (instigatedBy != none) { + instigator = class'EKFPawn'.static.Wrap(instigatedBy); + } + damageData = _.collections.EmptyHashTable(); + damageData.SetInt(T(TDAMAGE), damage); + damageData.SetInt(T(TORIGINAL_DAMAGE), originalDamage); + damageData.SetVector(T(THIT_LOCATION), hitLocation); + damageData.SetVector(T(TMOMENTUM), momentum, true); + onDamageSignal.Emit(target, instigator, damageData); + damage = damageData.GetInt(T(TDAMAGE), damage); + momentum = damageData.GetVector(T(TMOMENTUM), momentum); + _.memory.Free(damageData); + _.memory.Free(instigator); + _.memory.Free(target); return damage; } @@ -171,10 +220,17 @@ private function UpdateBileAchievement(Controller killer, Controller killed) } } - defaultproperties { replaceBloatAndSirenDamageTypes = true + TDAMAGE = 0 + stringConstants(0) = "damage" + TORIGINAL_DAMAGE = 1 + stringConstants(1) = "originalDamage" + THIT_LOCATION = 2 + stringConstants(2) = "hitLocation" + TMOMENTUM = 3 + stringConstants(3) = "momentum" infoReplacingDamageTypes = (l=LOG_Info,m="Replacing bloat's and siren's damage types to dummy ones.") infoRestoringReplacingDamageTypes = (l=LOG_Info,m="Restoring bloat and siren's damage types to their original values.") errNoServerLevelCore = (l=LOG_Error,m="Server level core is missing. Either this isn't a server or Acedia was wrongly initialized. Bloat and siren damage type will not be replaced.")