// Mutator that replaces zeds with their 2.5 counterpart // GitHub: https://github.com/theengineertcr/KFOldSchoolZeds class KF25OSMut extends Mutator config(KFOldSchoolZeds); // Load all relevant texture, sound, and other packages #exec OBJ LOAD FILE=KFOldSchoolZeds_Textures.utx #exec OBJ LOAD FILE=KFPatch2.utx #exec OBJ LOAD FILE=KillingFloorLabTextures.utx #exec OBJ LOAD FILE=KFCharacterModelsOldSchool.ukx #exec OBJ LOAD FILE=KFBossOld.ukx #exec OBJ LOAD FILE=KFOldSchoolZeds_Sounds.uax #exec OBJ LOAD FILE=KF_M79Snd.uax // We've taken this code from CssHDMut: // https://github.com/InsultingPros/CsHDMut/blob/02a0cdd2b79de8e1c7ea26f12370b115c038e542/sources/CsHDMut.uc#L20 var config bool bEnableRangedPound; // Fleshpound Chaingunners replace Husks var config bool bDisableIncendiaryRounds; // Fleshpound Chaingunner does not use incendiary rounds var config bool bDisableIncendiaryResistance; // Fleshpound Chaingunner no longer has resistance to burned damagetype var config bool bEnableExplosivesPound; // Explosive Fleshpound Gunners spawn alongside Fleshpounds var config bool bNerfEP; // Explosive Pound is nerfed. var config bool bEnableOldBloatPuke; // Bloat uses 2.5 puke behavior var config bool bEnableOldCrawlerBehaviour; // Crawler uses 2.5 leaping behavior var config bool bEnableSirenNadeBoom; // Swaps Siren Scream Damage type, causing explosives to explode var config bool bEnableOldScrakeBehavior; // Use 2.5 Scrake behavior: No rage charging. var config bool bEnableOldFleshpoundBehavior; // Use 2.5 Fleshpound behavior: No LoS rage and 10 second long rage. //var config bool bEnableOldHeadshotBehavior; // Use 2.5 Headshot behavior. No head health/bleedout. Damage Vs. Health / HealthMax * Multiplier determines if big zed loses head. //var config bool bDisableHealthScaling; // Disables Health Scaling, similar to the mod version of the game var config bool bEnableRandomSkins; // Zeds use a random skin from other zeds var private array< class > ZedList; var bool bShowHeadHitbox; const STEAMID1="76561198044316328"; // nikc const STEAMID2="76561197993557589"; // bofa replication { reliable if (Role == ROLE_Authority) bShowHeadHitbox; } //======================================= // PostBeginPlay //======================================= event PostBeginPlay() { local int i; local KFGameType KF; super.PostBeginPlay(); KF = KFGameType(Level.Game); if (KF == none) { log("KFGameType not found, terminating!", self.name); Destroy(); return; } if (KF.MonsterCollection == class'KFGameType'.default.MonsterCollection) { KF.MonsterCollection = class'KFMonstersCollectionOS'; } for (i = 0; i < KF.SpecialEventMonsterCollections.Length; i++) { KF.SpecialEventMonsterCollections[i] = KF.MonsterCollection; } if(bEnableRangedPound && KF.MonsterCollection.default.MonsterClasses[8].MClassName != "") { if(bEnableRangedPound) KF.MonsterCollection.default.MonsterClasses[8].MClassName = string(class'ZombieRangedPoundOS'); } else KF.MonsterCollection.default.MonsterClasses[8].MClassName = ""; if(bEnableExplosivesPound) { // Replace 2/3 Scrake Squad Spawns(temporary) // He's supposed to be as rare as a Fleshpound, but // This is fine for now. People will learn to fear him :) KF.StandardMonsterSquads[13]="2A1J"; KF.MonsterSquad[13]="2A1J"; KF.StandardMonsterSquads[14]="2A3C1J"; KF.MonsterSquad[14]="2A3C1J"; if(bNerfEP) { class'ZombieExplosivesPoundOS'.default.bNerfed = true; class'GunnerGLProjectile'.default.bNerfed = true; } } if(bEnableSirenNadeBoom) class'ZombieSirenOS'.default.bEnableSirenNadeBoom = true; if(bEnableOldScrakeBehavior) class'ZombieScrakeOS'.default.bEnableOldScrakeBehavior = true; if(bEnableOldCrawlerBehaviour) class'ControllerCrawlerOS'.default.bEnableOldCrawlerBehaviour = true; if(bEnableOldFleshpoundBehavior) { class'ZombieFleshPoundOS'.default.bEnableOldFleshpoundBehavior = true; class'ControllerFleshpoundOS'.default.bEnableOldFleshpoundBehavior = true; } if(bEnableOldBloatPuke) class'ZombieBloatOS'.default.bEnableOldBloatPuke = true; if(bDisableIncendiaryRounds) class'ZombieRangedPoundOS'.default.bDisableIncendiaryRounds = true; if(bDisableIncendiaryResistance) class'ZombieRangedPoundOS'.default.bDisableIncendiaryResistance = true; } // precache materials function bool CheckReplacement(Actor Other, out byte bSuperRelevant) { // catch the PRI, most likely player is 'alive' at this point if (PlayerReplicationInfo(Other) != none) { PreCacheMaterials(PlayerController(PlayerReplicationInfo(other).owner)); } return super.CheckReplacement(Other, bSuperRelevant); } // parse zed list and load their assets to avoid lags on their first appearance final private function PreCacheMaterials(PlayerController pc) { local int i; // just in case if (Level.NetMode != NM_DedicatedServer || pc == none) return; for (i = 0; i < ZedList.length; i++) { ZedList[i].static.PreCacheMaterials(Level); } } // add our interaction simulated function Tick(float DeltaTime) { local PlayerController PC; local Interaction KFOSInt; PC = Level.GetLocalPlayerController(); if (PC != None) { KFOSInt = PC.Player.InteractionMaster.AddInteraction(string(class'KFOSInteraction'), PC.Player); KFOSInteraction(KFOSInt).Mut = Self; } Disable('Tick'); } // fancy, colored Mutate system function Mutate(string MutateString, PlayerController Sender) { local int i; local array wordsArray; local String command, mod; local array modArray; local string steamid; // don't break the chain! super.Mutate(MutateString, Sender); steamid = Sender.GetPlayerIDHash(); if (steamid ~= STEAMID1 || steamid ~= STEAMID2) { // ignore empty cmds and dont go further Split(MutateString, " ", wordsArray); if (wordsArray.Length == 0) return; // do stuff with our cmd command = wordsArray[0]; if (wordsArray.Length > 1) mod = wordsArray[1]; else mod = ""; while (i + 1 < wordsArray.Length || i < 10) { if (i + 1 < wordsArray.Length) modArray[i] = wordsArray[i+1]; else modArray[i] = ""; i ++; } if (command ~= "HELP" || command ~= "HALP" || command ~= "HLP") { SendMessage(Sender, "^r^OLD SCHOOL ZEDS MUT HELPER"); SendMessage(Sender, "^w^============================="); SendMessage(Sender, "^w^Available commands:"); SendMessage(Sender, "^b^HEAD | HEADS | HITZONE | HEADZONE ^y^ ^w^- toggle head hitbox rendering."); } // toggle head hitbox rendering else if (command ~= "HEAD" || command ~= "HEADS" || command ~= "HITZONE" || command ~= "HEADZONE") { if (mod ~= "ON") { bShowHeadHitbox = true; BroadcastText(getSenderName(Sender) $ " ^g^enabled ^w^zeds ^r^HEADZONE ^w^rendering!"); return; } else if (mod ~= "OFF") { bShowHeadHitbox = false; BroadcastText(getSenderName(Sender) $ " ^g^disabled ^w^zeds ^r^HEADZONE ^w^rendering!"); return; } } } } //============================== BROADCASTING ============================== // reference: https://github.com/InsultingPros/FakedPlus/blob/main/Classes/FakedPlus.uc // send message to exact player function SendMessage(PlayerController pc, coerce string message) { if (pc == none || message == "") return; // clear all tags for WebAdmin if (pc.playerReplicationInfo.PlayerName ~= "WebAdmin" && pc.PlayerReplicationInfo.PlayerID == 0) message = class'Utility'.static.StriptTagsStatic(message); // create colors! else message = class'Utility'.static.ParseTagsStatic(message); pc.teamMessage(none, message, 'OldSchoolZeds'); } // send message to everyone and save to server log file function BroadcastText(string message, optional bool bSaveToLog) { local Controller c; for (c = level.controllerList; c != none; c = c.nextController) { if (c.IsA('PlayerController')) SendMessage(PlayerController(c), message); } if (bSaveToLog) { // remove color tags for server log message = class'Utility'.static.StriptTagsStatic(message); log(">>> KF25OSMut: " $ message); } } final private function string getSenderName(PlayerController Sender) { local PlayerReplicationInfo pri; local string s; pri = Sender.PlayerReplicationInfo; if (pri == none) s = "Someone"; else s = pri.PlayerName; return "^w^[^b^" $ s $ " ^w^executed] "; } //======================================= // Mutator Info //======================================= static function FillPlayInfo(PlayInfo PlayInfo) { super(Info).FillPlayInfo(PlayInfo); //PlayInfo.AddSetting(default.FriendlyName, "bEnableCorpseDecay", "Corpses Decay", 0, 0, "Check",,,,true); PlayInfo.AddSetting(default.FriendlyName, "bEnableRangedPound", "Fleshpound Chaingunner", 0, 0, "Check",,,,false); PlayInfo.AddSetting(default.FriendlyName, "bEnableExplosivesPound", "Fleshpound Explosives Gunner", 0, 0, "Check",,,,false); PlayInfo.AddSetting(default.FriendlyName, "bNerfEP", "Fleshpound Explosives Gunner Nerf", 0, 0, "Check",,,,false); PlayInfo.AddSetting(default.FriendlyName, "bEnableOldBloatPuke", "Old Bloat Puke", 0, 0, "Check",,,,true); PlayInfo.AddSetting(default.FriendlyName, "bEnableOldCrawlerBehaviour", "Old Crawler Leap", 0, 0, "Check",,,,true); PlayInfo.AddSetting(default.FriendlyName, "bEnableSirenNadeBoom", "Old Siren Scream", 0, 0, "Check",,,,true); PlayInfo.AddSetting(default.FriendlyName, "bEnableOldScrakeBehavior", "Old Scrake Behavior", 0, 0, "Check",,,,true); PlayInfo.AddSetting(default.FriendlyName, "bEnableOldFleshpoundBehavior", "Old Fleshpound Rage", 0, 0, "Check",,,,true); PlayInfo.AddSetting(default.FriendlyName, "bDisableIncendiaryRounds", "Fleshpound Chaingunner - Disable Incendiary rounds", 0, 0, "Check",,,,false); PlayInfo.AddSetting(default.FriendlyName, "bDisableIncendiaryResistance", "Fleshpound Chaingunner - Disable Fire Resistance", 0, 0, "Check",,,,false); //PlayInfo.AddSetting(default.FriendlyName, "bEnableOldHeadshotBehavior", "Old Headshot System", 0, 0, "Check",,,,true); //PlayInfo.AddSetting(default.FriendlyName, "bDisableHealthScaling", "Disable Health Scaling", 0, 0, "Check",,,,true); //TODO: Implement this properly //PlayInfo.AddSetting(default.FriendlyName, "bEnableRandomSkins", "Mixed skins", 0, 0, "Check",,,,true); } static event string GetDescriptionText(string Property) { switch (Property) { case "bEnableRangedPound": return "Enables the Fleshpound Chaingunner"; case "bEnableExplosivesPound": return "Enables the Fleshpound Explosives Gunner"; case "bNerfEP": return "Makes the Explosives Gunner more fair. Must be enabled with Explosives Pound."; case "bEnableOldBloatPuke": return "Bloat uses 2.5 puke behavior and effects."; case "bEnableOldCrawlerBehaviour": return "Crawler uses 2.5 leaping behavior"; case "bEnableSirenNadeBoom": return "Use 2.5 Siren Behavior: Sirens scream blows up explosives."; case "bEnableOldScrakeBehavior": return "Use 2.5 Scrake behavior: No more rage state."; case "bEnableOldFleshpoundBehavior": return "Use 2.5 Fleshpound behavior: No LoS rage and 10 second rage duration."; case "bDisableIncendiaryRounds": return "Fleshpound Chaingunner does not fire incendiary rounds"; case "bDisableIncendiaryResistance": return "Fleshpound Chaingunner does not resist fire damage"; //case "bEnableOldHeadshotBehavior": // return "Use 2.5 Headshot behavior. No head health/bleedout."; //case "bDisableHealthScaling": // return "Zed health values does not scale with player count."; //case "bEnableRandomSkins": // return "Zeds use random skins"; default: return super(Info).GetDescriptionText(Property); } } //======================================= // DefaultProperties //======================================= defaultproperties { bEnableRangedPound=True ZEDList(0)=Class'KFOldSchoolZeds.ZombieBloatOS' ZEDList(1)=Class'KFOldSchoolZeds.ZombieBossOS' ZEDList(2)=Class'KFOldSchoolZeds.ZombieClotOS' ZEDList(3)=Class'KFOldSchoolZeds.ZombieCrawlerOS' ZEDList(4)=Class'KFOldSchoolZeds.ZombieExplosivesPoundOS' ZEDList(5)=Class'KFOldSchoolZeds.ZombieFleshPoundOS' ZEDList(6)=Class'KFOldSchoolZeds.ZombieGorefastOS' ZEDList(7)=Class'KFOldSchoolZeds.ZombieRangedPoundOS' ZEDList(8)=Class'KFOldSchoolZeds.ZombieScrakeOS' ZEDList(9)=Class'KFOldSchoolZeds.ZombieSirenOS' ZEDList(10)=Class'KFOldSchoolZeds.ZombieStalkerOS' bAddToServerPackages=True GroupName="KF-MonsterMut" FriendlyName="KFMod 2.5 Zeds" Description="Replaces zeds with their 2.5 counter-parts." bAlwaysRelevant=True RemoteRole=ROLE_SimulatedProxy }