rott/kf_sources/KFOldSchoolZeds/Classes/KF25OSMut.uc
2026-07-14 20:27:09 +07:00

369 lines
14 KiB
Ucode

// 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<KFMonsterOS> > 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<String> wordsArray;
local String command, mod;
local array<String> 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^<ON / OFF> ^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
}