diff --git a/sources/Gameplay/BaseClasses/Frontend/Health/AHealthComponent.uc b/sources/Gameplay/BaseClasses/Frontend/Health/AHealthComponent.uc new file mode 100644 index 0000000..5221a45 --- /dev/null +++ b/sources/Gameplay/BaseClasses/Frontend/Health/AHealthComponent.uc @@ -0,0 +1,26 @@ +/** + * Subset of functionality for dealing with everything related to pawns' + * health. + * 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 AHealthComponent extends AcediaObject + abstract; + +defaultproperties +{ +} \ No newline at end of file diff --git a/sources/Gameplay/BaseClasses/KillingFloor/Frontend/KFFrontend.uc b/sources/Gameplay/BaseClasses/KillingFloor/Frontend/KFFrontend.uc index 711efc9..1c5be1b 100644 --- a/sources/Gameplay/BaseClasses/KillingFloor/Frontend/KFFrontend.uc +++ b/sources/Gameplay/BaseClasses/KillingFloor/Frontend/KFFrontend.uc @@ -23,18 +23,26 @@ class KFFrontend extends BaseFrontend var private config class tradingClass; var public ATradingComponent trading; +var private config class healthClass; +var public AHealthComponent health; + protected function Constructor() { super.Constructor(); if (tradingClass != none) { trading = ATradingComponent(_.memory.Allocate(tradingClass)); } + if (healthClass != none) { + health = AHealthComponent(_.memory.Allocate(healthClass)); + } } protected function Finalizer() { _.memory.Free(trading); + _.memory.Free(health); trading = none; + health = none; } /** @@ -53,5 +61,6 @@ public function EItemTemplateInfo GetItemTemplateInfo(BaseText templateName) defaultproperties { - tradingClass = none + tradingClass = none + healthClass = none } \ No newline at end of file diff --git a/sources/Gameplay/KF1Frontend/Health/DummyDamageTypes/Dummy_DamTypeVomit.uc b/sources/Gameplay/KF1Frontend/Health/DummyDamageTypes/Dummy_DamTypeVomit.uc new file mode 100644 index 0000000..a6c42e6 --- /dev/null +++ b/sources/Gameplay/KF1Frontend/Health/DummyDamageTypes/Dummy_DamTypeVomit.uc @@ -0,0 +1,26 @@ +/** + * Dummy replacer for bloat's `DamTypeVomit` damage type to avoid TWI's moronic + * "This stuff cuts thru all the B.S" bullshit. + * 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 Dummy_DamTypeVomit extends DamTypeVomit + abstract; + +defaultproperties +{ +} \ No newline at end of file diff --git a/sources/Gameplay/KF1Frontend/Health/DummyDamageTypes/Dummy_SirenScreamDamage.uc b/sources/Gameplay/KF1Frontend/Health/DummyDamageTypes/Dummy_SirenScreamDamage.uc new file mode 100644 index 0000000..dd14265 --- /dev/null +++ b/sources/Gameplay/KF1Frontend/Health/DummyDamageTypes/Dummy_SirenScreamDamage.uc @@ -0,0 +1,26 @@ +/** + * Dummy replacer for bloat's `SirenScreamDamage` damage type to avoid TWI's + * moronic "This stuff cuts thru all the B.S" bullshit. + * 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 Dummy_SirenScreamDamage extends SirenScreamDamage + abstract; + +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 new file mode 100644 index 0000000..4b3ffa6 --- /dev/null +++ b/sources/Gameplay/KF1Frontend/Health/KF1_HealthComponent.uc @@ -0,0 +1,187 @@ +/** + * `AHealthComponent`'s implementation for `KF1_Frontend`. + * 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 KF1_HealthComponent extends AHealthComponent + dependson(ConnectionService) + config(AcediaSystem); + +var private const config bool replaceBloatAndSirenDamageTypes; + +var private LoggerAPI.Definition infoReplacingDamageTypes, errNoServerLevelCore; +var private LoggerAPI.Definition infoRestoringReplacingDamageTypes; + +public function PresudoConstructor() +{ + local LevelCore core; + _.unreal.gameRules.OnNetDamage(self).connect = OnNetDamageHandler; + if (!replaceBloatAndSirenDamageTypes) { + return; + } + _.logger.Auto(infoReplacingDamageTypes); + core = class'ServerLevelCore'.static.GetInstance(); + if (core != none) + { + ReplaceDamageTypes(core); + _.unreal.gameRules.OnScoreKill(self).connect = UpdateBileAchievement; + core.OnShutdown(self).connect = RestoreDamageTypes; + } + else { + _.logger.Auto(errNoServerLevelCore); + } +} + +protected function Finalizer() +{ + _.unreal.gameRules.OnNetDamage(self).Disconnect(); + if (replaceBloatAndSirenDamageTypes) { + RestoreDamageTypes(); + } +} + +private final function ReplaceDamageTypes(LevelCore core) +{ + local KFBloatVomit nextVomit; + local ZombieSirenBase nextSiren; + + class'KFBloatVomit'.default.myDamageType = class'Dummy_DamTypeVomit'; + class'ZombieSirenBase'.default.screamDamageType = + class'Dummy_SirenScreamDamage'; + class'ZombieSirenBase'.default.screamDamageType = + class'Dummy_SirenScreamDamage'; + class'ZombieSiren_STANDARD'.default.screamDamageType = + class'Dummy_SirenScreamDamage'; + class'ZombieSiren_HALLOWEEN'.default.screamDamageType = + class'Dummy_SirenScreamDamage'; + class'ZombieSiren_XMas'.default.screamDamageType = + class'Dummy_SirenScreamDamage'; + class'ZombieSiren_CIRCUS'.default.screamDamageType = + class'Dummy_SirenScreamDamage'; + foreach core.AllActors(class'KFBloatVomit', nextVomit) { + nextVomit.myDamageType = class'Dummy_DamTypeVomit'; + } + foreach core.AllActors(class'ZombieSirenBase', nextSiren) { + nextSiren.screamDamageType = class'Dummy_SirenScreamDamage'; + } +} + +private final function RestoreDamageTypes() +{ + _.logger.Auto(infoRestoringReplacingDamageTypes); + class'KFBloatVomit'.default.myDamageType = class'DamTypeVomit'; + class'ZombieSirenBase'.default.screamDamageType = class'SirenScreamDamage'; + class'ZombieSirenBase'.default.screamDamageType = class'SirenScreamDamage'; + class'ZombieSiren_STANDARD'.default.screamDamageType = + class'SirenScreamDamage'; + class'ZombieSiren_HALLOWEEN'.default.screamDamageType = + class'SirenScreamDamage'; + class'ZombieSiren_XMas'.default.screamDamageType = class'SirenScreamDamage'; + class'ZombieSiren_CIRCUS'.default.screamDamageType = + class'SirenScreamDamage'; +} + +private function int OnNetDamageHandler( + int originalDamage, + int damage, + Pawn injured, + Pawn instigatedBy, + Vector hitLocation, + out Vector momentum, + class damageType) +{ + if (damageType == class'Dummy_DamTypeVomit') { + damage = ReapplyNativeBileAdjustments(originalDamage, damage, injured); + } + if (damageType == class'Dummy_SirenScreamDamage') { + damage = ReapplyNativeScreamAdjustments(damage, injured, hitLocation); + } + return damage; +} + +private function int ReapplyNativeBileAdjustments( + int originalDamage, + int damage, + Pawn injured) +{ + local KFPlayerReplicationInfo kfPRI; + + if (ZombieBloatBase(injured) != none) { + return 0; + } + if (ZombieFleshpoundBase(injured) != none) { + return 0; + } + if (injured != none && injured.controller != none) + { + kfPRI = KFPlayerReplicationInfo( + injured.controller.playerReplicationInfo); + } + return damage; +} + +private function int ReapplyNativeScreamAdjustments( + int damage, + Pawn injured, + Vector hitLocation) +{ + local KFPawn injuredPawn; + + injuredPawn = KFPawn(injured); + if (injuredPawn != none) + { + // TODO: `PlayHit()` is not informed about siren's damage!!! + // TODO: Neither are bloody projectiles! + injuredPawn.lastHitDamType = class'SirenScreamDamage'; + } + return damage; +} + +private function UpdateBileAchievement(Controller killer, Controller killed) +{ + local int i; + local KFMonster killedMonster; + local ConnectionService service; + local KFSteamStatsAndAchievements kfSteamStats; + local array activeConnections; + + killedMonster = KFMonster(killed.pawn); + if (killedMonster == none) return; + if (killedMonster.lastDamagedByType != class'Dummy_DamTypeVomit') return; + service = ConnectionService(class'ConnectionService'.static.Require()); + if (service == none) return; + + activeConnections = service.GetActiveConnections(); + for (i = 0; i < activeConnections.length; i += 1) + { + kfSteamStats = KFSteamStatsAndAchievements(activeConnections[i] + .controllerReference + .steamStatsAndAchievements); + if (kfSteamStats != none) { + kfSteamStats.KilledEnemyWithBloatAcid(); + } + } +} + + +defaultproperties +{ + replaceBloatAndSirenDamageTypes = true + 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.") +} \ No newline at end of file diff --git a/sources/Gameplay/KF1Frontend/KF1_Frontend.uc b/sources/Gameplay/KF1Frontend/KF1_Frontend.uc index bf395cd..99c8022 100644 --- a/sources/Gameplay/KF1Frontend/KF1_Frontend.uc +++ b/sources/Gameplay/KF1Frontend/KF1_Frontend.uc @@ -2,7 +2,7 @@ * Frontend implementation for classic `KFGameType` that changes as little as * possible and only on request from another mod, otherwise not altering * gameplay at all. - * Copyright 2021 Anton Tarasenko + * Copyright 2021-2022 Anton Tarasenko *------------------------------------------------------------------------------ * This file is part of Acedia. * @@ -35,4 +35,5 @@ defaultproperties { templatesClass = class'KF1_TemplatesComponent' tradingClass = class'KF1_TradingComponent' + healthClass = class'KF1_HealthComponent' } \ No newline at end of file