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