From f4c2eeabc6fecf03ba57f4c561e935c0995b2771 Mon Sep 17 00:00:00 2001 From: Robert Fritzen Date: Wed, 20 Dec 2017 11:39:43 -0600 Subject: [PATCH] Shifter Zombies, Other Misc Changes Upgraded the shifter zombie type to the new AI script style, added in additional flags to zombieCore.cs and moved the zombie moan functioning to the zombieCore file for easier modification across all types. --- README.md | 5 + scripts/TWM2/MultiUseDatablocks.cs | 18 +++ scripts/TWM2/Zombie/ZombieCore.cs | 62 ++++---- scripts/TWM2/Zombie/ZombieTypes/DemonLord.cs | 13 +- scripts/TWM2/Zombie/ZombieTypes/Normal.cs | 13 +- scripts/TWM2/Zombie/ZombieTypes/Rapier.cs | 11 +- scripts/TWM2/Zombie/ZombieTypes/Ravager.cs | 11 +- scripts/TWM2/Zombie/ZombieTypes/Shifter.cs | 140 +++++++++++-------- 8 files changed, 153 insertions(+), 120 deletions(-) diff --git a/README.md b/README.md index 6662059..dfb7df1 100644 --- a/README.md +++ b/README.md @@ -94,6 +94,11 @@ PLEASE NOTE: I've moved all old changelogs into the version_history folder. This * Replaced the standard lunge with a fire lunge which creates a firey explosion on impact * Reduced the hit damage of the demon lord from 0.8 to 0.5 * Demon Lords, like the regular demons will no longer infect on collision, but set the player on fire instead + * Shifter + * The change to the shifter teleportation in 3.91 made these zombies ridiculously overpowered, they will be tuned down + * Increased the maximum targeting range of the teleport attack from 200m to 400m + * Increased the cooldown of the teleport attack from 7 seconds to 12.5 seconds + * Shifter zombies will now have to "lock down" for a 1.5 seconds before teleporting, during this time they will be easily targetable * Added Boss Proficiency * Hidden challenges embedded in boss fights that award additional experience for completing tough feats * For example: Defeat the shade lord without dying by the elemental shades diff --git a/scripts/TWM2/MultiUseDatablocks.cs b/scripts/TWM2/MultiUseDatablocks.cs index 58ee9aa..90c89ae 100644 --- a/scripts/TWM2/MultiUseDatablocks.cs +++ b/scripts/TWM2/MultiUseDatablocks.cs @@ -22,6 +22,24 @@ datablock AudioProfile(ZombieHOWL) { preload = true; }; +datablock AudioProfile(ZombieDeathSound1) { + filename = "voice/Derm3/avo.deathcry_01.wav"; + description = AudioClose3d; + preload = true; +}; + +datablock AudioProfile(ZombieDeathSound2) { + filename = "voice/Derm2/avo.deathcry_01.wav"; + description = AudioClose3d; + preload = true; +}; + +datablock AudioProfile(ZombieDeathSound3) { + filename = "voice/Derm1/avo.deathcry_01.wav"; + description = AudioClose3d; + preload = true; +}; + //********************************************** // PARTICLE DATABLOCKS //********************************************** diff --git a/scripts/TWM2/Zombie/ZombieCore.cs b/scripts/TWM2/Zombie/ZombieCore.cs index d048038..94e2a59 100644 --- a/scripts/TWM2/Zombie/ZombieCore.cs +++ b/scripts/TWM2/Zombie/ZombieCore.cs @@ -85,8 +85,21 @@ $Zombie::DemonZombieFireBombMaxRange = 250; //$Zombie::RapierUpwardScaling: How fast a rapier zombie will ascend when holding a player $Zombie::RapierUpwardScaling = 750; +//$Zombie::DemonLord_FireLunge_Thrust: The velocity scalar of the thrust associated with the fire lunge attack +$Zombie::DemonLord_FireLunge_Thrust = 4000; +//$Zombie::DemonLord_FireLunge_MinimumRange: The minimum range following the burst of speed to trigger the fire explosion +$Zombie::DemonLord_FireLunge_MinimumRange = 10; //$Zombie::DemonLord_FirestormTrigger: How long in miliseconds between the firestorm charge up and the attack itself $Zombie::DemonLord_FirestormTrigger = 1000; +//$Zombie::DemonLord_MinionSpawnChance: The chance scalar associated with the minion summon attack (The larger this number, the less likely) +$Zombie::DemonLord_MinionSpawnChance = 120; + +//$Zombie::Shifter_Teleport_MaximumRange: The maximum range at which a shifter is allowed to teleport from +$Zombie::Shifter_Teleport_MaximumRange = 400; +//$Zombie::Shifter_Teleport_PrepTime: The amount of time (in ms) that it takes for the shifter to lock down before teleporting +$Zombie::Shifter_Teleport_PrepTime = 1500; +//$Zombie::Shifter_Teleport_Cooldown: The cooldown between each teleport +$Zombie::Shifter_Teleport_Cooldown = 12500; //MISC Globals, Do not edit. $Zombie::killpoints = 5; @@ -124,9 +137,31 @@ function TWM2Lib_Zombie_Core(%functionName, %arg1, %arg2, %arg3, %arg4) { %final = %speed * %multiplier; return %final; + //playzaudio(%zombie, %chanceToPlay, %chanceHowl): Plays the moaning sounds associated with zombies + case "playzaudio": + if(!isObject(%arg1) || %arg1.getState() $= "dead") { + return; + } + if(!isSet(%arg2)) { + %arg2 = 50; + } + if(!isSet(%arg3)) { + %arg3 = 35; + } + %chance = (getrandom() * %arg2); + if(%chance >= (%arg2 - 1)) { + %chance = (getRandom() * %arg3); + if(%chance <= (%arg3 - 1)) { + serverPlay3d("ZombieMoan", %arg1.getWorldBoxCenter()); + } + else { + serverPlay3d("ZombieHOWL", %arg1.getWorldBoxCenter()); + } + } + //zsetrandommove(%zombie): Activated when the zombie needs to begin random movement case "zsetrandommove": - if(!isObject(%arg1)) { + if(!isObject(%arg1) || arg1.getState() $= "dead") { return; } %rx = getRandom(-10, 10); @@ -649,27 +684,4 @@ function TWM2Lib_Zombie_Core(%functionName, %arg1, %arg2, %arg3, %arg4) { %zombie.getDatablock().AI(%zombie); return %zombie; } -} - -//----------------------------------------------------------- -//DEATH -datablock AudioProfile(ZombieDeathSound1) -{ - filename = "voice/Derm3/avo.deathcry_01.wav"; - description = AudioClose3d; - preload = true; -}; - -datablock AudioProfile(ZombieDeathSound2) -{ - filename = "voice/Derm2/avo.deathcry_01.wav"; - description = AudioClose3d; - preload = true; -}; - -datablock AudioProfile(ZombieDeathSound3) -{ - filename = "voice/Derm1/avo.deathcry_01.wav"; - description = AudioClose3d; - preload = true; -}; +} \ No newline at end of file diff --git a/scripts/TWM2/Zombie/ZombieTypes/DemonLord.cs b/scripts/TWM2/Zombie/ZombieTypes/DemonLord.cs index f6b1755..5f90113 100644 --- a/scripts/TWM2/Zombie/ZombieTypes/DemonLord.cs +++ b/scripts/TWM2/Zombie/ZombieTypes/DemonLord.cs @@ -161,9 +161,8 @@ function DemonMotherZombieArmor::AIRoutine(%datablock, %zombie) { } } else if(%dist > 100) { - %rand = getrandom(1,120); - //please, dont ask why i choose this number, it just popped in my head - if(%rand == 94) { + %rand = getRandom(1, $Zombie::DemonLord_MinionSpawnChance); + if(%rand == (($Zombie::DemonLord_MinionSpawnChance / 2) + 1)) { %datablock.AttackFunction(%zombie, "SpawnZombies"); } else { @@ -253,7 +252,7 @@ function DemonMotherZombieArmor::AttackFunction(%datablock, %zombie, %attackFunc if(%zombie.chargeCount == 0) { TWM2Lib_Zombie_Core("zombieGetFacingDirection", %zombie, %target.getPosition()); %vector = vectorNormalize(vectorSub(%target.getPosition(), %zombie.getPosition())); - %vector = vectorscale(%vector, 4000); + %vector = vectorscale(%vector, $Zombie::DemonLord_FireLunge_Thrust); %x = Getword(%vector, 0); %y = Getword(%vector, 1); %z = Getword(%vector, 2); @@ -272,7 +271,7 @@ function DemonMotherZombieArmor::AttackFunction(%datablock, %zombie, %attackFunc %zombie.attackFunction = %datablock.schedule(300, 0, "AttackFunction", %zombie, %attackFunction, %target); } else { - if(vectorDist(%zombie.getPosition(), %target.getPosition()) < 10) { + if(vectorDist(%zombie.getPosition(), %target.getPosition()) < $Zombie::DemonLord_FireLunge_MinimumRange) { %p = new TracerProjectile() { dataBlock = napalmSubExplosion; initialDirection = "0 0 -10"; @@ -328,7 +327,7 @@ function DemonMotherZombieArmor::AttackFunction(%datablock, %zombie, %attackFunc if(%zombie.chargecount $= "") { %zombie.chargecount = 0; } - if(%zombie.chargecount <= 9){ + if(%zombie.chargecount <= 9) { TWM2Lib_Zombie_Core("zombieGetFacingDirection", %zombie, %target.getPosition()); %zombie.setvelocity("0 0 10"); %zombie.chargecount++; @@ -346,7 +345,7 @@ function DemonMotherZombieArmor::AttackFunction(%datablock, %zombie, %attackFunc %zombie.chargecount++; %zombie.attackFunction = %datablock.schedule(400, 0, "AttackFunction", %zombie, %attackFunction, %target); } - else if(%zombie.chargecount >= 11){ + else if(%zombie.chargecount >= 11) { %zombie.startFade(500, 0, false); %zombie.setPosition(%zombie.attackpos); %zombie.setvelocity(vectorscale(%zombie.attackdir, 25)); diff --git a/scripts/TWM2/Zombie/ZombieTypes/Normal.cs b/scripts/TWM2/Zombie/ZombieTypes/Normal.cs index 8f7952d..2514254 100644 --- a/scripts/TWM2/Zombie/ZombieTypes/Normal.cs +++ b/scripts/TWM2/Zombie/ZombieTypes/Normal.cs @@ -65,7 +65,7 @@ function ZombieArmor::armorCollisionFunction(%datablock, %zombie, %colPlayer) { function ZombieArmor::AI(%datablock, %zombie) { //Normal zombies do not employ any "AI" other than target and move, fork off to main move function - %datablock.Move(%zombie); + %zombie.moveloop = %datablock.Move(%zombie); } function ZombieArmor::Move(%datablock, %zombie) { @@ -80,16 +80,7 @@ function ZombieArmor::Move(%datablock, %zombie) { if(%zombie.hastarget != 1) { %zombie.hastarget = 1; } - %chance = (getrandom() * 20); - if(%chance >= 19) { - %chance = (getRandom() * 12); - if(%chance <= 11) { - serverPlay3d("ZombieMoan", %zombie.getWorldBoxCenter()); - } - else { - serverPlay3d("ZombieHOWL", %zombie.getWorldBoxCenter()); - } - } + TWM2Lib_Zombie_Core("playZAudio", %zombie, 100, 40); %vector = TWM2Lib_Zombie_Core("zombieGetFacingDirection", %zombie, %closestClient.getPosition()); if(Game.CheckModifier("SuperLunge") == 1) { diff --git a/scripts/TWM2/Zombie/ZombieTypes/Rapier.cs b/scripts/TWM2/Zombie/ZombieTypes/Rapier.cs index f358299..0a92558 100644 --- a/scripts/TWM2/Zombie/ZombieTypes/Rapier.cs +++ b/scripts/TWM2/Zombie/ZombieTypes/Rapier.cs @@ -128,16 +128,7 @@ function RapierZombieArmor::move(%datablock, %zombie) { %Zombie.mountImage(ZWingaltImage, 3); %Zombie.mountImage(ZWingaltImage2, 4); } - %chance = (getrandom() * 20); - if(%chance >= 19) { - %chance = (getRandom() * 12); - if(%chance <= 11) { - serverPlay3d("ZombieMoan", %zombie.getWorldBoxCenter()); - } - else { - serverPlay3d("ZombieHOWL", %zombie.getWorldBoxCenter()); - } - } + TWM2Lib_Zombie_Core("playZAudio", %zombie, 20, 12); if(%zombie.iscarrying == 1) { %vector = vectorscale(%zombie.getForwardVector(), (%zombie.speed / 2)); %vector = getword(%vector, 0)@" "@getword(%vector, 1)@" "@($Zombie::RapierUpwardScaling * 1.5); diff --git a/scripts/TWM2/Zombie/ZombieTypes/Ravager.cs b/scripts/TWM2/Zombie/ZombieTypes/Ravager.cs index 9ccc13e..8769e3f 100644 --- a/scripts/TWM2/Zombie/ZombieTypes/Ravager.cs +++ b/scripts/TWM2/Zombie/ZombieTypes/Ravager.cs @@ -132,16 +132,7 @@ function RavagerZombieArmor::Move(%datablock, %zombie) { %pos = %zombie.getWorldBoxCenter(); %upVec = "250"; //moanStuff - %chance = (getrandom() * 50); - if(%chance >= 49) { - %chance = (getRandom() * 12); - if(%chance <= 11) { - serverPlay3d("ZombieMoan", %zombie.getWorldBoxCenter()); - } - else { - serverPlay3d("ZombieHOWL", %zombie.getWorldBoxCenter()); - } - } + TWM2Lib_Zombie_Core("playZAudio", %zombie, 250, 40); //Determine target location if(%zombie.ambushing) { %vector = TWM2Lib_Zombie_Core("zombieGetFacingDirection", %zombie, %zombie.ambushPosition); diff --git a/scripts/TWM2/Zombie/ZombieTypes/Shifter.cs b/scripts/TWM2/Zombie/ZombieTypes/Shifter.cs index 9d8c558..8631d24 100644 --- a/scripts/TWM2/Zombie/ZombieTypes/Shifter.cs +++ b/scripts/TWM2/Zombie/ZombieTypes/Shifter.cs @@ -1,3 +1,5 @@ +$TWM2::ArmorHasCollisionFunction[ShifterZombieArmor] = true; + datablock PlayerData(ShifterZombieArmor) : LightMaleHumanArmor { runForce = 60.20 * 90; runEnergyDrain = 0.0; @@ -34,63 +36,87 @@ datablock PlayerData(ShifterZombieArmor) : LightMaleHumanArmor { max[Grenade] = 0; }; -function ShifterZombiemovetotarget(%zombie){ - if(!isobject(%Zombie)) - return; - if(%Zombie.getState() $= "dead") - return; - %pos = %zombie.getworldboxcenter(); - %closestClient = ZombieLookForTarget(%zombie); - %closestDistance = getWord(%closestClient,1); - %closestClient = getWord(%closestClient,0).Player; - if(%closestDistance <= $zombie::detectDist){ - if(%zombie.hastarget != 1){ - %zombie.hastarget = 1; +function ShifterZombieArmor::armorCollisionFunction(%datablock, %zombie, %colPlayer) { + if(!isObject(%zombie) || %zombie.getState() $= "dead") { + return; } - %chance = (getrandom() * 20); - if(%chance >= 19) { - %chance = (getRandom() * 12); - if(%chance <= 11) - serverPlay3d("ZombieMoan",%zombie.getWorldBoxCenter()); - else - serverPlay3d("ZombieHOWL",%zombie.getWorldBoxCenter()); - } - - %vector = ZgetFacingDirection(%zombie,%closestClient,%pos); - - %fmultiplier = $Zombie::ForwardSpeed; - if(%closestDistance <= $zombie::lungDist && %zombie.canjump == 1) - %fmultiplier = (%fmultiplier * 4); - %vector = vectorscale(%vector, %Fmultiplier); - %upvec = "150"; - - //tis shift timez :) - if(%closestDistance > 200 || (%zombie.getVelocity() == 0 && !%zombie.RecentShift)) { - %zombie.setMoveState(true); - %zombie.startFade(1500, 0, true); - %zombie.schedule(1600, "SetPosition", VectorAdd(%closestClient.getPosition(), vectorAdd("0 0 3", TWM2Lib_MainControl("getRandomPosition", "5\t1")))); - %zombie.startFade(1750, 0, false); - %zombie.schedule(2000, setMoveState, false); - %zombie.RecentShift = 1; - Schedule(10500, 0, "eval", ""@%zombie@".RecentShift=0;"); - } - - if(%closestDistance <= $zombie::lungDist && %zombie.canjump == 1){ - %upvec = %upvec * 2; - %zombie.canjump = 0; - schedule(1500, %zombie, "Zsetjump", %zombie); + if(!isObject(%colPlayer) || %colPlayer.getState() $= "dead") { + return; } - %x = Getword(%vector,0); - %y = Getword(%vector,1); - %z = Getword(%vector,2); - if(%z >= 600) - %upvec = (%upvec * 5); - %vector = %x@" "@%y@" "@%upvec; - %zombie.applyImpulse(%pos, %vector); - } - else if(%zombie.hastarget == 1){ - %zombie.hastarget = 0; - %zombie.zombieRmove = schedule(100, %zombie, "ZSetRandomMove", %zombie); - } - %zombie.moveloop = schedule(500, %zombie, "ShifterZombiemovetotarget", %zombie); + //Check to make sure we're not hitting another zombie / boss + if(%colPlayer.isBoss || %colPlayer.isZombie || %colPlayer.rapierShield) { + return; + } + //Damage. + %causeInfect = %zombie.damage_infectOnTouch; + %baseDamage = %zombie.damage_amountOnTouch; + %multiplier = %zombie.damage_alreadyInfectedMultiplier; + + %total = %colPlayer.infected ? (%baseDamage * %multiplier) : %baseDamage; + + %pushVector = vectorscale(%colPlayer.getvelocity(), 100); + %colPlayer.applyimpulse(%colPlayer.getposition(), %pushVector); + if(%causeInfect) { + %colPlayer.Infected = 1; + %colPlayer.InfectedLoop = schedule(10, %colPlayer, "TWM2Lib_Zombie_Core", "InfectLoop", %colPlayer, "impact"); + } + %colPlayer.damage(0, %colPlayer.getPosition(), %total, $DamageType::Zombie); } + +function ShifterZombieArmor::AI(%datablock, %zombie) { + //Shifter zombies do not employ any "AI" other than target and move, fork off to main move function + %zombie.moveloop = %datablock.Move(%zombie); +} + +function ShifterZombieArmor::Move(%datablock, %zombie) { + if(!isObject(%zombie) || %zombie.getState() $= "dead") { + return; + } + %pos = %zombie.getWorldBoxCenter(); + %closestClient = TWM2Lib_Zombie_Core("lookForTarget", %zombie); + %closestDistance = getWord(%closestClient, 1); + %closestClient = getWord(%closestClient, 0).Player; + if(%closestDistance <= $zombie::detectDist) { + if(%zombie.hastarget != 1) { + %zombie.hastarget = 1; + } + TWM2Lib_Zombie_Core("playZAudio", %zombie, 100, 40); + %vector = TWM2Lib_Zombie_Core("zombieGetFacingDirection", %zombie, %closestClient.getPosition()); + + if(%closestDistance <= $Zombie::LungeDistance && %zombie.canjump == 1) { + %vector = vectorScale(%vector, 4); + } + %vector = vectorScale(%vector, %zombie.speed); + %upvec = "150"; + + //tis shift timez :) + if(%closestDistance > $Zombie::Shifter_Teleport_MaximumRange || (%zombie.getVelocity() == 0 && !%zombie.RecentShift)) { + %zombie.setMoveState(true); + %zombie.startFade($Zombie::Shifter_Teleport_PrepTime, 0, true); + %zombie.schedule($Zombie::Shifter_Teleport_PrepTime + 100, "SetPosition", VectorAdd(%closestClient.getPosition(), vectorAdd("0 0 3", TWM2Lib_MainControl("getRandomPosition", "5\t1")))); + %zombie.startFade($Zombie::Shifter_Teleport_PrepTime + 250, 0, false); + %zombie.schedule($Zombie::Shifter_Teleport_PrepTime + 350, setMoveState, false); + TWM2Lib_Zombie_Core("setZFlag", %zombie, "recentShift", 1); + schedule($Zombie::Shifter_Teleport_Cooldown, 0, TWM2Lib_Zombie_Core, "setZFlag", %zombie, "recentShift", 0); + } + + if(%closestDistance <= $Zombie::LungeDistance && %zombie.canjump == 1) { + %upvec *= 2; + TWM2Lib_Zombie_Core("setZFlag", %zombie, "canJump", 0); + schedule($Zombie::BaseJumpCooldown, 0, TWM2Lib_Zombie_Core, "setZFlag", %zombie, "canJump", 1); + } + %x = Getword(%vector, 0); + %y = Getword(%vector, 1); + %z = Getword(%vector, 2); + if(%z >= 600) { + %upvec = (%upvec * 5); + } + %vector = %x@" "@%y@" "@%upvec; + %zombie.applyImpulse(%pos, %vector); + } + else if(%zombie.hastarget == 1) { + %zombie.hastarget = 0; + %zombie.zombieRmove = schedule(%zombie.updateTimeFrequency, %zombie, "TWM2Lib_Zombie_Core", "zRandomMoveLoop", %zombie); + } + %zombie.moveloop = %datablock.schedule(%zombie.updateTimeFrequency, %datablock, "Move", %zombie); +} \ No newline at end of file