diff --git a/README.md b/README.md index e9972bb..114180c 100644 --- a/README.md +++ b/README.md @@ -99,6 +99,11 @@ PLEASE NOTE: I've moved all old changelogs into the version_history folder. This * 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 + * Sniper + * The sniper zombie is now armed with two new weapons. + * The first is an acid sniper rifle which infects players on striking + * The second is a rapid fire sidearm that the sniper will use when targets move too close + * Reduced the health of Sniper Zombies from 40 (4.0) to 25 (2.5) * 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/InfectionGame.cs b/scripts/InfectionGame.cs index 1896f37..b2412fe 100644 --- a/scripts/InfectionGame.cs +++ b/scripts/InfectionGame.cs @@ -513,6 +513,7 @@ $ZArmor[1, 2] = "Rav\t0\tRavenger Zombie\tAbility: Jet Key To Speed Forward/Lung $ZArmor[2, 2] = "Lord\t0\tZombie Lord\tAbilities: Jet Key To Fire, Mine Key To Lift"; $ZArmor[3, 2] = "Demon\t0\tDemon Zombie\tAbility: Jet Key To Throw Fireball"; $ZArmor[4, 2] = "Rap\t1\tAir Rapier Zombie\tAbility: Jet Key Flies"; +$ZArmor[5, 2] = "VolRav\t1\tVolatile Ravenger Zombie\tAbility: Jet Key To Detonate C4"; $ZArmor[0, 3] = "Norm\t0\tNormal Zombie\tAbility: Jet Key To Lunge"; $ZArmor[1, 3] = "Rav\t0\tRavenger Zombie\tAbility: Jet Key To Speed Forward/Lunge"; @@ -521,7 +522,7 @@ $ZArmor[3, 3] = "Demon\t0\tDemon Zombie\tAbility: Jet Key To Throw Fireball"; $ZArmor[4, 3] = "Rap\t0\tAir Rapier Zombie\tAbility: Jet Key Flies"; $ZArmor[5, 3] = "VolRav\t0\tVolatile Ravenger Zombie\tAbility: Jet Key To Detonate C4"; $ZArmor[6, 3] = "UDem\t1\tUltra Demon Zombie\tAbility: Jet Key To Fire, Mine To Charge Forward"; -$ZArmor[7, 3] = "Wraith\t1\tWraith Spec-Ops Zombie\tAbility: Jet Key To Charge, Armed with MP26-CMDO"; +//$ZArmor[7, 3] = "Wraith\t1\tWraith Spec-Ops Zombie\tAbility: Jet Key To Charge, Armed with MP26-CMDO"; $ZArmor[0, 4] = "Norm\t0\tNormal Zombie\tAbility: Jet Key To Lunge"; $ZArmor[1, 4] = "Rav\t0\tRavenger Zombie\tAbility: Jet Key To Speed Forward/Lunge"; @@ -530,8 +531,8 @@ $ZArmor[3, 4] = "Demon\t0\tDemon Zombie\tAbility: Jet Key To Throw Fireball"; $ZArmor[4, 4] = "Rap\t0\tAir Rapier Zombie\tAbility: Jet Key Flies"; $ZArmor[5, 4] = "VolRav\t0\tVolatile Ravenger Zombie\tAbility: Jet Key To Detonate C4"; $ZArmor[6, 4] = "UDem\t0\tUltra Demon Zombie\tAbility: Jet Key To Fire, Mine To Charge Forward"; -$ZArmor[7, 4] = "Wraith\t0\tWraith Spec-Ops Zombie\tAbility: Jet Key To Charge, Armed with MP26-CMDO"; -$ZArmor[8, 4] = "Rog\t1\tGeneral Rog\tAbilities: Jet Key To fire zMG42, Shielded, Armed with BoV"; +//$ZArmor[7, 4] = "Flareguide\t0\tFist of Vengeance Flareguide\tAbility: Armed with the Bursting NLS-22 Rifle, Grenade to Vanish"; +//$ZArmor[8, 4] = "Rog\t1\tGeneral Rog\tAbilities: Jet Key To fire zMG42, Shielded, Armed with BoV"; //Score Hud $ScoreHudMaxVisible = 19; diff --git a/scripts/TWM2/Zombie/PlayerZombieFunctions.cs b/scripts/TWM2/Zombie/PlayerZombieFunctions.cs index d86e919..ad588e9 100644 --- a/scripts/TWM2/Zombie/PlayerZombieFunctions.cs +++ b/scripts/TWM2/Zombie/PlayerZombieFunctions.cs @@ -1,3 +1,24 @@ +//********************************************** +//********************************************** +// Player Zombie Functioning +// +// Updated TWM2 3.9.2 +// Cleaned up all of the functions and converted it into a library style +// system to reduce the function count +//********************************************** +//********************************************** + +function TWM2Lib_Zombie_PlayerFunctions(%functionName, %arg1, %arg2, %arg3, %arg4) { + switch$(strlwr(%functionName)) { + case "zombieattackimpulse": + + case "makepersonzombie": + + default: + error("TWM2Lib_Zombie_PlayerFunctions(): Unknown function name "@%functionName@" sent to call."); + } +} + function makePersonHumanFZomb(%trans, %client){ %client.player.delete(); %player = new Player() { diff --git a/scripts/TWM2/Zombie/ZombieCore.cs b/scripts/TWM2/Zombie/ZombieCore.cs index 8bd9af5..3e3802d 100644 --- a/scripts/TWM2/Zombie/ZombieCore.cs +++ b/scripts/TWM2/Zombie/ZombieCore.cs @@ -105,6 +105,22 @@ $Zombie::Shifter_Teleport_Cooldown = 12500; //$Zombie::Summoner_Cooldown: The cooldown of the summoner zombie's summon ability $Zombie::Summoner_Cooldown = 25000; +//$Zombie::Sniper_MaximumEngageRange: The maximum range at which the sniper zombie will fire at. +// NOTE: If the target is outside of this range, the sniper will begin "consideration" to look for a new target +$Zombie::Sniper_MaximumEngageRange = 450; +//$Zombie::Sniper_PreferedDistanceFromTarget: The range at which the sniper zombie will move up to try to reach +$Zombie::Sniper_PreferedDistanceFromTarget = 300; +//$Zombie::Sniper_SidearmRange: The range at which the sniper zombie engages CQC combat with it's sidearm +$Zombie::Sniper_SidearmRange = 50; +//$Zombie::Sniper_RifleCooldown: The reload time of the sniper zombie's rifle (MS) +$Zombie::Sniper_RifleCooldown = 7000; +//$Zombie::Sniper_SidearmBurstCount: The amount of shots in a sidearm burst +$Zombie::Sniper_SidearmBurstCount = 10; +//$Zombie::Sniper_SidearmBurstTime: The amount of time to fire off the full burst +$Zombie::Sniper_SidearmBurstTime = 600; +//$Zombie::Sniper_SidearmCooldown: The reload time of the sniper zombie's sidearm (MS) +$Zombie::Sniper_SidearmCooldown = 5000; + //MISC Globals, Do not edit. $Zombie::killpoints = 5; $Zombie::RogThread = "cel1"; @@ -173,7 +189,7 @@ function TWM2Lib_Zombie_Core(%functionName, %arg1, %arg2, %arg3, %arg4) { %vec = %rx @ " " @ %ry @ " " @ 0; %arg1.direction = vectorNormalize(%vec); %arg1.Mnum = getRandom(1, 20); - %arg1.zombieRmove = schedule($Zombie::SpeedUpdateTime, %arg1, "TWM2Lib_Zombie_Core", "zRandomMoveLoop", %arg1); + %arg1.zombieRmove = schedule(%arg1.updateTimeFrequency, %arg1, "TWM2Lib_Zombie_Core", "zRandomMoveLoop", %arg1); //zrandommoveloop(%zombie): Moves the zombies around in a random direction case "zrandommoveloop": @@ -193,10 +209,10 @@ function TWM2Lib_Zombie_Core(%functionName, %arg1, %arg2, %arg3, %arg4) { %vector = vectorScale(%vec, %speed); %arg1.applyImpulse(%arg1.direction, %vector); %arg1.Mnum -= 1; - %arg1.zombieRmove = schedule($Zombie::SpeedUpdateTime, %arg1, "TWM2Lib_Zombie_Core", "zRandomMoveLoop", %arg1); + %arg1.zombieRmove = schedule(%arg1.updateTimeFrequency, %arg1, "TWM2Lib_Zombie_Core", "zRandomMoveLoop", %arg1); } else { - %arg1.zombieRmove = schedule($Zombie::SpeedUpdateTime, %arg1, "TWM2Lib_Zombie_Core", "zRandomMoveLoop", %arg1); + %arg1.zombieRmove = schedule(%arg1.updateTimeFrequency, %arg1, "TWM2Lib_Zombie_Core", "zRandomMoveLoop", %arg1); } //cureInfection(%player): Cures the zombie infection @@ -304,6 +320,8 @@ function TWM2Lib_Zombie_Core(%functionName, %arg1, %arg2, %arg3, %arg4) { %arg1.firingWeapon = %arg3; case "canfireweapon": %arg1.canFireWeapon = %arg3; + case "canaltfire": + %arg1.canAltFire = %arg3; } //lookForTarget(%zombie, [%pilot], [%groundVeh]): Identify the closest target, and the distance to that target @@ -560,6 +578,8 @@ function TWM2Lib_Zombie_Core(%functionName, %arg1, %arg2, %arg3, %arg4) { %zombie.mountImage(ZSniperImage1, 4); %zombie.mountImage(ZSniperImage2, 5); %zombie.mountImage(ZSniperImage3, 6); + %zombie.canFireWeapon = 1; + %zombie.canAltFire = 1; //Ultra-Demon Zombie case 12: @@ -616,7 +636,7 @@ function TWM2Lib_Zombie_Core(%functionName, %arg1, %arg2, %arg3, %arg4) { %zombie.mountImage(ZdummyslotImg, 4); //Flareguide Sniper Zombie (Mini-Boss) - case 11: + case 18: %zombie = new player(){ Datablock = "FlareguideSniperZombieArmor"; }; diff --git a/scripts/TWM2/Zombie/ZombieTypes/Sniper.cs b/scripts/TWM2/Zombie/ZombieTypes/Sniper.cs index ef58c83..fb79af4 100644 --- a/scripts/TWM2/Zombie/ZombieTypes/Sniper.cs +++ b/scripts/TWM2/Zombie/ZombieTypes/Sniper.cs @@ -63,16 +63,52 @@ function SniperZombieArmor::AI(%datablock, %zombie) { return; } %pos = %zombie.getWorldBoxCenter(); + //Target update. + if(%zombie.hadTarget) { + if(!isObject(%zombie.targetPlayer) || %zombie.targetPlayer.getState() $= "dead") { + //Target is down for the count, let's move on. + %zombie.hasTarget = 0; + %zombie.CQCTarget = 0; + %zombie.reallyFarTarget = 0; + %zombie.targetPlayer = 0; + } + } + //Do I have a target, but it's far out (IE: Running or Pilot?) + if(%zombie.hasTarget && %zombie.reallyFarTarget) { + //Throw a 1 in 25 chance to abort and re-search + if(getRandom(1, 25) == 19) { + %zombie.hasTarget = 0; + %zombie.targetPlayer = 0; + %zombie.CQCTarget = 0; + %zombie.reallyFarTarget = 0; + } + } + //Are there any targets? if(!%zombie.hasTarget) { %preferSniper = getRandom(1, 10); - if(%preferSniper > 3) { + if(%preferSniper > 4) { for(%i = 0; %i < ClientGroup.getCount(%i); %i++) { %check = ClientGroup.getObject(%i); if(isObject(%check.player) && %check.player.getState() !$= "dead") { //Check their weapon - + %w = %check.player.getMountedImage($WeaponSlot).getName(); + if(!strStr(strlwr(%w), "sniper")) { + //Bingo! + %zombie.hasTarget = 1; + %zombie.targetPlayer = %check.player; + break; + } } } + //No enemy snipers found... Look for a new target. + if(!%zombie.hasTarget) { + %closestClient = TWM2Lib_Zombie_Core("lookForTarget", %zombie); + %closestDistance = getWord(%closestClient, 1); + if(isObject(%closestClient.player) && %closestClient.player.getState() !$= "dead") { + %zombie.hasTarget = 1; + %zombie.targetPlayer = %closestClient.player; + } + } } else { %closestClient = TWM2Lib_Zombie_Core("lookForTarget", %zombie); @@ -83,8 +119,48 @@ function SniperZombieArmor::AI(%datablock, %zombie) { } } } - - %zombie.moveloop = %datablock.Move(%zombie); + //Do I have a target? + if(%zombie.hasTarget) { + //Check the distance... + %tPos = %zombie.targetPlayer.getPosition(); + %dist = vectorDist(%pos, %tPos); + if(%dist < $Zombie::Sniper_SidearmRange) { + //Target is getting into sidearm range... engage close range attacks + %zombie.reallyFarTarget = 0; + %zombie.CQCTarget = 1; + %datablock.Move(%zombie); + if(%zombie.canAltFire) { + TWM2Lib_Zombie_Core("setZFlag", %zombie, "canAltFire", 0); + schedule($Zombie::Sniper_RifleCooldown, 0, TWM2Lib_Zombie_Core, "setZFlag", %zombie, "canAltFire", 1); + for(%i = 0; %i < $Zombie::Sniper_SidearmBurstCount; %i++) { + %timeDelay = ($Zombie::Sniper_SidearmBurstTime / $Zombie::Sniper_SidearmBurstCount) * %i; + %datablock.schedule(%timeDelay, "zFire_Alternate", %zombie); + } + } + } + else if(%dist < $Zombie::Sniper_MaximumEngageRange && %dist > $Zombie::Sniper_SidearmRange) { + //Target is in Sniper Range... engage. + %zombie.reallyFarTarget = 0; + %zombie.CQCTarget = 0; + if(vectorDist(%pos, %tPos) > $Zombie::Sniper_PreferedDistanceFromTarget) { + //I'm a bit far out myself, let's move up while we engage. + %datablock.Move(%zombie); + } + if(%zombie.canFireWeapon) { + TWM2Lib_Zombie_Core("setZFlag", %zombie, "canFireWeapon", 0); + schedule($Zombie::Sniper_RifleCooldown, 0, TWM2Lib_Zombie_Core, "setZFlag", %zombie, "canFireWeapon", 1); + %datablock.zFire(%zombie); + } + } + else { + //Target's a bit too far out, let's move up, but flag our AI to begin thinking about a new target + %zombie.reallyFarTarget = 1; + %zombie.CQCTarget = 0; + %datablock.Move(%zombie); + } + } + //No targets... Let's just wait. + %zombie.aiLoop = %datablock.schedule(%zombie.updateTimeFrequency, "AI", %zombie); } function SniperZombieArmor::Move(%datablock, %zombie) { @@ -92,46 +168,63 @@ function SniperZombieArmor::Move(%datablock, %zombie) { 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(Game.CheckModifier("SuperLunge") == 1) { - %ld = $Zombie::LungeDistance * 5; - } - else { - %ld = $Zombie::LungeDistance; - } - if(%closestDistance <= %ld && %zombie.canjump == 1) { - %vector = vectorScale(%vector, 4); - } - %vector = vectorScale(%vector, %zombie.speed); - %upvec = "150"; - if(%closestDistance <= %ld && %zombie.canjump == 1) { - %upvec *= 2; - TWM2Lib_Zombie_Core("setZFlag", %zombie, "canJump", 0); - schedule($Zombie::BaseJumpCooldown, 0, TWM2Lib_Zombie_Core, "setZFlag", %zombie, "canJump", 1); - } + %tPos = %zombie.targetPlayer.getPosition(); + %upVec = "250"; + //Are we fighting in close quarters or not? + if(%zombie.CQCTarget) { + %zombie.setVelocity("0 0 0"); + //FaceTarget(%zombie, %target); + TWM2Lib_Zombie_Core("zombieGetFacingDirection", %zombie, %tPos); + %vector = vectorNormalize(vectorSub(%tPos, %zombie.getPosition())); + %vector = vectorscale(%vector, %zombie.speed); %x = Getword(%vector, 0); %y = Getword(%vector, 1); - %z = Getword(%vector, 2); - if(%z >= 600) { - %upvec = (%upvec * 5); - } + %nv1 = %y; + %nv2 = (%x * -1); + %vector = %nv1@" "@%nv2@" 0"; + %zombie.applyImpulse(%zombie.getPosition(), %vector); + } + else { + %vector = TWM2Lib_Zombie_Core("zombieGetFacingDirection", %zombie, %tPos); + //Scale to speed + %vector = vectorScale(%vector, %zombie.speed); + %x = getWord(%vector, 0); + %y = getWord(%vector, 1); %vector = %x@" "@%y@" "@%upvec; - %zombie.applyImpulse(%pos, %vector); + %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, "Move", %zombie); +} + +function SniperZombieArmor::zFire(%datablock, %zombie) { + %target = %zombie.targetPlayer; + %num = getRandom(250, 1000); + %vec = vectorsub(VectorAdd(%target.getPosition(), "0 0 2.2"), %zombie.getMuzzlePoint(4)); + %accuracy = (vectorlen(%vec) / %num); + %vec = vectoradd(%vec, vectorscale(%target.getvelocity(), %accuracy)); + %p = new TracerProjectile() { + dataBlock = SniperZombieAcidShot; + initialDirection = %vec; + initialPosition = %zombie.getMuzzlePoint(4); + sourceObject = %zombie; + sourceSlot = 4; + }; + ServerPlay3D(CentaurArtilleryFireSound, %zombie.getPosition()); +} + +function SniperZombieArmor::zFire_Alternate(%datablock, %zombie) { + %target = %zombie.targetPlayer; + %num = getRandom(250, 1000); + %vec = vectorsub(VectorAdd(%target.getPosition(), "0 0 1"), %zombie.getMuzzlePoint(4)); + %accuracy = (vectorlen(%vec) / %num); + %vec = vectoradd(%vec, vectorscale(%target.getvelocity(), %accuracy)); + %p = new TracerProjectile() { + dataBlock = SniperZombieAcidSidearmShot; + initialDirection = %vec; + initialPosition = %zombie.getMuzzlePoint(4); + sourceObject = %zombie; + sourceSlot = 4; + }; + ServerPlay3D(CentaurArtilleryFireSound, %zombie.getPosition()); } //***************************************************************** @@ -163,93 +256,6 @@ datablock PlayerData(FlareguideSniperZombieArmor) : SniperZombieArmor { max[Grenade] = 0; }; - - -function SniperZombiemovetotarget(%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(%closestDistance < 50) { //runz0rs - %TPos = %closestClient.getPosition(); - %tvel = %closestclient.getvelocity(); - %vec = vectorsub(%tpos,%pos); - %dist = vectorlen(%vec); - %velpredict = vectorscale(%tvel,(%dist / 50)); - %vector = vectoradd(%vec,%velpredict); - %vector = vectornormalize(%vector); - %x = getWord(%vector, 0); - %y = getWord(%vector, 1); - %finX = %x * -1; - %finY = %y * -1; - %finalVec = %finX SPC %finY SPC 0; - %finalVec = VectorScale(%finalVec, $Zombie::DForwardSpeed * 3); - //Z is unimportant - %zombie.applyImpulse(%pos, %finalVec); - } - else { - if(%zombie.hastarget != 1 && %closestdistance >= 50){ - SniperZombieFire(%zombie,%closestclient); - %zombie.canjump = 0; - schedule(4000, %zombie, "Zsetjump", %zombie); - } - if(%zombie.hastarget != 1){ - serverPlay3d("ZombieHOWL",%zombie.getWorldBoxCenter()); - %zombie.hastarget = 1; - } - %chance = (getrandom() * 20); - if(%chance >= 19) - serverPlay3d("ZombieMoan",%zombie.getWorldBoxCenter()); - - %vector = ZgetFacingDirection(%zombie,%closestClient,%pos); - - if (%closestdistance >= 50 && %zombie.canjump == 1){ - SniperZombieFire(%zombie,%closestclient); - %zombie.canjump = 0; - schedule(4000, %zombie, "Zsetjump", %zombie); - } - if(%closestdistance > 175) { //only move toward my foe, if he is too - %vector = vectorscale(%vector, $Zombie::DForwardSpeed); //far away - %upvec = "150"; - %x = Getword(%vector,0); - %y = Getword(%vector,1); - %z = Getword(%vector,2); - if(%z >= ($Zombie::DForwardSpeed / 3 * 2)) - %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, "SniperZombiemovetotarget", %zombie); -} - -function SniperZombieFire(%zombie,%closestclient){ - %num = getRandom(250, 1000); - %vec = vectorsub(VectorAdd(%closestclient.getPosition(), "0 0 2.2"),%zombie.getMuzzlePoint(4)); - %accuracy = (vectorlen(%vec) / %num); - %vec = vectoradd(%vec, vectorscale(%closestclient.getvelocity(), %accuracy)); - %p = new TracerProjectile() { //TWM2 Sniper zombies use ALSWP Snipers :P - dataBlock = SniperZombieAcidShot; - initialDirection = %vec; - initialPosition = %zombie.getMuzzlePoint(4); - sourceObject = %zombie; - sourceSlot = 4; - }; - ServerPlay3D(ALSWPFireSound, %zombie.getPosition()); -} - - function FlareguideSniperZombieAcidShot::onExplode(%data, %proj, %pos, %mod) { Parent::OnExplode(%data, %proj, %pos, %mod); //Create the mini-pulses