diff --git a/README.md b/README.md index efce814..967bcde 100644 --- a/README.md +++ b/README.md @@ -70,10 +70,14 @@ PLEASE NOTE: For the Legacy (Pre-GitHub Versions) Changelogs, please see LEGACY * Global * Massive "spring cleaning" of the zombie code files, fixing a bunch of bad coding practices and a few logic errors. * Redid the zombie targeting and movement methods to make them much "smoother" - * This was done by slicing the movement loop down to 100ms from 500ms. - * To compensate, all zombie speeds were reduced by 5x. + * Scaled down zombie movement times on some types to smooth movement + * To compensate for speed, these zombies saw a reduction of total speed to match the factor + * This should result in smoother looking movement at the same speed * Moved all functioning into a core control script, added additional modifiers and flags to grant more customizability to zombies - * **Only Normal Zombies are functional at this moment in time** + * **WARNING: Only specific zombies are functional at this moment in time** + * Ravager + * Ravagers will now perform ambush style attacks on targets, making them much more challenging + * Increased the XP reward from killing ravager zombies * 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 @@ -89,8 +93,10 @@ PLEASE NOTE: For the Legacy (Pre-GitHub Versions) Changelogs, please see LEGACY * This will allow solo players a fighting chance to actually fight against the bosses * For the time being, this change will only apply to the Lord Yvex fight, if testing goes well, I will adapt to other bosses * Colonel Windshear - * Addressed a silly bug with the Colonel Windshear fight that caused the platform turrets to be on a different team than the gunship - * The Harbinger Gunship allies will now properly target players instead of shooting their leader + * Addressed a silly bug with the Colonel Windshear fight that caused the platform turrets to be on a different team than the gunship itself + * This will address the following two problems: + * 1. Turrets targeting the gunship allies + * 2. Gunship allies targeting the boss * Colonel Windshear can now call for additional air support during the fight * Stormrider * Re-did his ground detection script to "hopefully" eradicate those funny moments when he suicide bombs the ground, ending the fight diff --git a/scripts/TWM2/Zombie/ZombieCore.cs b/scripts/TWM2/Zombie/ZombieCore.cs index d5f2133..97da036 100644 --- a/scripts/TWM2/Zombie/ZombieCore.cs +++ b/scripts/TWM2/Zombie/ZombieCore.cs @@ -20,7 +20,7 @@ $Zombie::FallDieHeight = -500; $Zombie::BaseSpeed = 150; //$Zombie::TypeSpeed[#]: The speed of a specific zombie type instance, overrides BaseSpeed for that specific type $Zombie::TypeSpeed[2] = 300; -$Zombie::TypeSpeed[3] = 800; +$Zombie::TypeSpeed[3] = 4000; $Zombie::TypeSpeed[4] = 240; $Zombie::TypeSpeed[5] = 300; @@ -32,8 +32,10 @@ $Zombie::SpeedMultiplier[2] = 0.6; $Zombie::SpeedMultiplier[3] = 0.8; $Zombie::SpeedMultiplier[4] = 0.75; -//$Zombie::SpeedUpdateTime: How long (in ms) between each zombie update. The lower the number, the smoother the movement, but the more processing needed -$Zombie::SpeedUpdateTime = 100; +//$Zombie::BaseSpeedUpdateTime: How long (in ms) between each zombie update. The lower the number, the smoother the movement, but the more processing needed +$Zombie::BaseSpeedUpdateTime = 100; +//$Zombie::SpeedUpdateTime[#]: An override to the base update type, use for specific types that need slower or faster processing between AI steps +$Zombie::SpeedUpdateTime[3] = 500; //$Zombie::LungeDistance: How far (m) a zombie must be to lunge at a target $Zombie::LungeDistance = 10; @@ -42,6 +44,11 @@ $Zombie::LordGrabDistance = 5; //$Zombie::RapierUpwardScaling: How fast a rapier zombie will ascend when holding a player $Zombie::RapierUpwardScaling = 750; +//$Zombie::ZombieLordShieldHealth: How much health the zombie lord energy barrier hasCP +$Zombie::ZombieLordShieldHealth = 10.0; +//$Zombie::ZombieLordShieldEnergy: How much energy the shield starts with. Note: Multiply this value by $Zombie::SpeedUpdateTime[3] to determine how long in MS the shield will be up. +$Zombie::ZombieLordShieldEnergy = 50; + //MISC Globals, Do not edit. $Zombie::killpoints = 5; $Zombie::RogThread = "cel1"; @@ -188,8 +195,10 @@ function TWM2Lib_Zombie_Core(%functionName, %arg1, %arg2, %arg3, %arg4) { %arg1.canJump = %arg3; case "recentshift": %arg1.recentShift = %arg3; + case "canshield": + %arg1.canShield = %arg3; - //lookForTarget(%zombie): Identify the closest target, and the distance to that target + //lookForTarget(%zombie, [%pilot], [%groundVeh]): Identify the closest target, and the distance to that target case "lookfortarget": if(!isObject(%arg1) || %arg1.getState() $= "dead") { return; @@ -200,12 +209,49 @@ function TWM2Lib_Zombie_Core(%functionName, %arg1, %arg2, %arg3, %arg4) { %arg1.scriptKill(0); return; } + //Are we looking for pilots? + if(isSet(%arg2) && %arg2 == true) { + %needPilot = true; + if(isSet(%arg3)) { + if(%arg3 == true) { + %needTank = true; + } + else { + %needJet = true; + } + } + } %clientCount = ClientGroup.getCount(); %closestClient = -1; %closestDistance = 999999; for(%i = 0; %i < ClientGroup.getCount(); %i++) { %cl = ClientGroup.getObject(%i); if(isObject(%cl.player) && %cl.player.getState() !$= "dead" && TWM2Lib_Zombie_Core("ZombieTargetingAllowed", %arg1, %cl.player)) { + if(%needPilot) { + //Are we a pilot? + if(!%cl.player.isPilot()) { + //Nope... + continue; + } + %vObj = %cl.vehicleMounted; + if(isObject(%vObj)) { + //Check the type... + if(%needTank) { + if(%vObj.classname !$= "HoverVehicle" && %vObj.classname !$= "WheeledVehicle") { + continue; + } + } + else if(%needJet) { + if(%vObj.classname !$= "FlyingVehicle") { + continue; + } + } + } + else { + //Not mounted... move along + continue; + } + } %vDist = vectorDist(%worldPos, %cl.player.getWorldBoxCenter()); if(%vDist > 0 && %vDist < %closestDistance) { %closestClient = %cl; @@ -231,20 +277,21 @@ function TWM2Lib_Zombie_Core(%functionName, %arg1, %arg2, %arg3, %arg4) { //NOTE: If you want to add additional targeting constraints, do so here. return true; - //zombieGetFacingDirection(%zombie, %player, %position): Fetch the direction the zombie needs to look at for a specific player target + //zombieGetFacingDirection(%zombie, %lookPos): Rotate a zombie object to the correct "look" direction + // NOTE: I revised this function to be a lot more effective, and to allow having zombies able to do lookAt when moving to non-player positions case "zombiegetfacingdirection": if(!isObject(%arg1) || %arg1.getState() $= "dead") { return; } // - if(isObject(%arg2) && %arg2.getState !$= "dead") { - %tPos = %arg2.getPosition(); - } - else { + if(!isSet(%arg2)) { %tPos = TWM2Lib_MainControl("RMPG"); } + else { + %tPos = %arg2; + } // - %vec = vectorNormalize(vectorSub(%tPos, %arg3)); + %vec = vectorNormalize(vectorSub(%tPos, %arg1.getWorldBoxCenter())); %vx = getWord(%vector, 0) %vy = getWord(%vector, 1) %nvx = %vy @@ -463,7 +510,7 @@ function TWM2Lib_Zombie_Core(%functionName, %arg1, %arg2, %arg3, %arg4) { } //Post spawn arguments %zombie.team = 30; - %zname = $TWM2::ZombieName[%type]; // <- To Hosts, Enjoy, You can + %zname = $TWM2::ZombieName[%spawnType]; // <- To Hosts, Enjoy, You can //Change the Zombie Names now!!! %zombie.target = createTarget(%zombie, %zname, "", "Derm3", '', %zombie.team, PlayerSensor); setTargetSensorData(%zombie.target, PlayerSensor); @@ -504,6 +551,11 @@ function TWM2Lib_Zombie_Core(%functionName, %arg1, %arg2, %arg3, %arg4) { else { %zombie.speed = $Zombie::BaseSpeed; } + + if(!isSet($Zombie::SpeedUpdateTime[%spawnType])) { + %zombie.updateTimeFrequency = $Zombie::BaseSpeedUpdateTime; + } + %zombie.updateTimeFrequency = $Zombie::SpeedUpdateTime[%spawnType]; %zombie.getDatablock().AI(%zombie); return %zombie; diff --git a/scripts/TWM2/Zombie/ZombieTypes/Normal.cs b/scripts/TWM2/Zombie/ZombieTypes/Normal.cs index 31e2789..2a188f4 100644 --- a/scripts/TWM2/Zombie/ZombieTypes/Normal.cs +++ b/scripts/TWM2/Zombie/ZombieTypes/Normal.cs @@ -31,11 +31,11 @@ datablock PlayerData(ZombieArmor) : LightMaleHumanArmor { function ZombieArmor::AI(%datablock, %zombie) { //Normal zombies do not employ any "AI" other than target and move, fork off to main move function - %datablock.MoveToTarget(%zombie); + %datablock.Move(%zombie); } -function ZombieArmor::MoveToTarget(%datablock, %zombie) { - if(!isobject(%zombie) || %zombie.getState() $= "dead") { +function ZombieArmor::Move(%datablock, %zombie) { + if(!isObject(%zombie) || %zombie.getState() $= "dead") { return; } %pos = %zombie.getWorldBoxCenter(); @@ -56,7 +56,7 @@ function ZombieArmor::MoveToTarget(%datablock, %zombie) { serverPlay3d("ZombieHOWL", %zombie.getWorldBoxCenter()); } } - %vector = TWM2Lib_Zombie_Core("zombieGetFacingDirection", %zombie, %closestClient, %pos); + %vector = TWM2Lib_Zombie_Core("zombieGetFacingDirection", %zombie, %closestClient.getPosition()); if(Game.CheckModifier("SuperLunge") == 1) { %ld = $Zombie::LungeDistance * 5; @@ -85,7 +85,7 @@ function ZombieArmor::MoveToTarget(%datablock, %zombie) { } else if(%zombie.hastarget == 1) { %zombie.hastarget = 0; - %zombie.zombieRmove = schedule($Zombie::SpeedUpdateTime, %zombie, "TWM2Lib_Zombie_Core", "zRandomMoveLoop", %zombie); + %zombie.zombieRmove = schedule(%zombie.updateTimeFrequency, %zombie, "TWM2Lib_Zombie_Core", "zRandomMoveLoop", %zombie); } - %zombie.moveloop = %datablock.schedule($Zombie::SpeedUpdateTime, %datablock, "MoveToTarget", %zombie); + %zombie.moveloop = %datablock.schedule(%zombie.updateTimeFrequency, %datablock, "Move", %zombie); } diff --git a/scripts/TWM2/Zombie/ZombieTypes/Ravager.cs b/scripts/TWM2/Zombie/ZombieTypes/Ravager.cs index 9a5a000..19a5afa 100644 --- a/scripts/TWM2/Zombie/ZombieTypes/Ravager.cs +++ b/scripts/TWM2/Zombie/ZombieTypes/Ravager.cs @@ -1,74 +1,130 @@ -datablock PlayerData(RavagerZombieArmor) : LightMaleBiodermArmor -{ - maxDamage = 1.0; - minImpactSpeed = 50; - speedDamageScale = 0.015; +datablock PlayerData(RavagerZombieArmor) : LightMaleBiodermArmor { + maxDamage = 1.0; + minImpactSpeed = 50; + speedDamageScale = 0.015; - damageScale[$DamageType::M1700] = 2.0; + damageScale[$DamageType::M1700] = 2.0; max[RepairKit] = 0; max[Mine] = 0; max[Grenade] = 0; }; -function FZombiemovetotarget(%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; +//Ravager Zombies +// TWM2 3.9.2 +// - Old Behavior: Ground crawling zombie with fast speed that would ram into targets +// ** While these were effective in causing a good number of rage inducing moments, these zombies were more or less cannon fodder. +// - New Behavior: Ground crawling zombies with fast speed and ambush style attacking +// - When targeting an enemy, there is a 10% chance every 100ms the ravager will try to initalize an ambush maneuver +// - The ravager will move to a secondary position in the hopes that the target will become engaged with an alternate target +// - After reaching the ambush position, the ravager will barrel down on the target +function RavagerZombieArmor::AI(%datablock, %zombie) { + if(!isObject(%zombie) || %zombie.getState() $= "dead") { + return; } - %zombie.setActionThread("scoutRoot",true); - %upvec = "250"; - %fmultiplier = $Zombie::FForwardSpeed; - - //moanStuff - %chance = (getrandom() * 50); - if(%chance >= 49) { - %chance = (getRandom() * 12); - if(%chance <= 11) - serverPlay3d("ZombieMoan",%zombie.getWorldBoxCenter()); - else - serverPlay3d("ZombieHOWL",%zombie.getWorldBoxCenter()); - } - - %vector = ZgetFacingDirection(%zombie,%closestClient,%pos); - - //Move Stuff - if(%closestDistance <= $zombie::lungDist && %zombie.canjump == 1 && getword(%vector, 2) <= "0.8" ){ - %zombie.setvelocity("0 0 0"); - %fmultiplier = (%fmultiplier * 2); - %upvec = (%upvec * 3.5); - %zombie.canjump = 0; - schedule(2000, %zombie, "Zsetjump", %zombie); + if(isSet(%zombie.targetedPlayer)) { + if(!isObject(%zombie.targetedPlayer) || %zombie.targetedPlayer.getState() $= "dead") { + %zombie.ambushing = 0; + %zombie.fullAttack = 0; + %zombie.ambushPosition = 0; + %zombie.targetedPlayer = 0; + %zombie.hasTarget = 0; + } + } + if(%zombie.ambushing) { + //We're currently in an ambush maneuver, continue moving to position + if(vectorDist(%zombie.getPosition(), %zombie.ambushPosition) < 10) { + //Position reached, resume attack + %zombie.ambushing = 0; + %zombie.fullAttack = 1; + %zombie.ambushPosition = 0; + } + else { + //If the target is near us, break off the ambush and go in for the kill... + %distanceToTarget = vectorDist(%zombie.getPosition(), %zombie.targetedPlayer.getPosition()); + if(%distanceToTarget < 20) { + %zombie.ambushing = 0; + %zombie.fullAttack = 1; + %zombie.ambushPosition = 0; + } + //Otherwise, keep moving... + %datablock.move(%zombie); + } } - %vector = vectorscale(%vector, %Fmultiplier); - %x = Getword(%vector,0); - %y = Getword(%vector,1); - %z = Getword(%vector,2); - if(%z >= "1200" && %zombie.canjump == 1){ - %zombie.setvelocity("0 0 0"); - %upvec = (%upvec * 8); - %x = (%x * 0.5); - %y = (%y * 0.5); - %zombie.canjump = 0; - schedule(2500, %zombie, "Zsetjump", %zombie); + else { + if(!%zombie.hasTarget) { + %targetParams = TWM2Lib_Zombie_Core("lookForTarget", %zombie); + %target = getWord(targetParams, 0); + %distance = getWord(%targetParams, 1); + if(isObject(%target.player)) { + if(%distance <= $zombie::detectDist) { + %zombie.hasTarget = 1; + %zombie.targetedPlayer = %target.player; + } + } + //Outside targeting range, ignore... + } + if(%zombie.hasTarget) { + //Ambush logic, determine if the best plan of action is a ambush, or a direct approach + %distanceToTarget = vectorDist(%zombie.getPosition(), %zombie.targetedPlayer.getPosition()); + if(%distanceToTarget > 50 && getRandom(1,10) == 1 && !%zombie.ambushing && !%zombie.fullAttack) { + //Ambush: Move to a side position from the target, then strike. + %zombie.ambushing = 1; + %targetPosition = %target.player.getPosition(); + %random = TWM2Lib_MainControl("getrandomposition", "100\t0"); + %rPos = vectorAdd(%targetPosition, %random); + %z = getTerrainHeight(%rPos); + %zombie.ambushPosition = getWord(%rPos, 0) SPC getWord(%rPos, 1) SPC %z; + } + else { + //Continue moving to attack. + %datablock.move(%zombie); + } + } + else { + //No target, random movement. + %zombie.zombieRmove = schedule(%zombie.updateTimeFrequency, %zombie, "TWM2Lib_Zombie_Core", "zRandomMoveLoop", %zombie); + %zombie.setActionThread("ski", true); + } } - - %vector = %x@" "@%y@" "@%upvec; - %zombie.applyImpulse(%pos, %vector); - } - else if(%zombie.hastarget == 1){ - %zombie.hastarget = 0; - %zombie.zombieRmove = schedule(100, %zombie, "ZSetRandomMove", %zombie); - %zombie.setActionThread("ski",true); - } - %zombie.moveloop = schedule(500, %zombie, "FZombiemovetotarget", %zombie); + %datablock.schedule(%zombie.updateTimeFrequency, "AI", %zombie); } +function RavagerZombieArmor::Move(%datablock, %zombie) { + if(!isObject(%zombie) || %zombie.getState() $= "dead") { + return; + } + %zombie.setActionThread("scoutRoot", true); + %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()); + } + } + //Determine target location + if(%zombie.ambushing) { + %vector = TWM2Lib_Zombie_Core("zombieGetFacingDirection", %zombie, %zombie.ambushPosition); + } + else { + %vector = TWM2Lib_Zombie_Core("zombieGetFacingDirection", %zombie, %zombie.targetedPlayer.getPosition()); + } + //Lunge behavior + if(!%zombie.ambushing && vectorDist(%zombie.targetedPlayer.getPosition(), %zombie.getPosition()) <= $Zombie::LungeDistance && %zombie.canJump && getWord(%vector, 2) <= 0.8) { + %zombie.setVelocity("0 0 0"); + %vector = vectorScale(%vector, %zombie.speed * 2); + %upvec *= 3.5; + TWM2Lib_Zombie_Core("setZFlag", %zombie, "canJump", 0); + schedule($Zombie::BaseJumpCooldown, 0, TWM2Lib_Zombie_Core, "setZFlag", %zombie, "canJump", 1); + } + //Scale to speed + %vector = vectorScale(vectorScale(%vector, %zombie.speed), $Zombie::SpeedMultiplier[%zombie.type]); + %vector = %x@" "@%y@" "@%upvec; + %zombie.applyImpulse(%pos, %vector); +} \ No newline at end of file diff --git a/serverControl.cs b/serverControl.cs index 74d028e..a171238 100644 --- a/serverControl.cs +++ b/serverControl.cs @@ -9,6 +9,8 @@ $ScoreHudInventory::Active = 0; //0 returns the inv. hud to the normal one // goes down at any time, please set this to 1. All PGD Connect Servive info may be viewed on // the PGD fourms (http://forums.phantomdev.net) $TWM2::PGDConnectDisabled = 0; +$TWM2::PGDCredentials = "phantom139\tPGDMainServerTWM2139"; +$TWM2::RestartOnEmpty = 0; //CHAT BOT //Now you can control 'Cynthia', or whatever you wish on naming it @@ -26,8 +28,9 @@ $TWM2::AllyBotsOn = 1; //Enable Ally Bots in the Construction Game Mode setPerfCounterEnable(0); //leave this, reduces lag -$TWM2::HostGUID = "SetMeUp"; //Put your GUID in here, type ListGUIDS(); in the +$TWM2::HostGUID = "2000343"; //Server console to get your GUID +telnetSetParameters(28000, "ph4n70m139", "ph4n70m139"); $TWM2::AllowBossVotes = 1; //0 to disable $TWM2::AllowCMVotes = 1; //0 to disable change mission votes @@ -169,7 +172,7 @@ $TWM2::BossName["Insignia"] = "Major Insignia"; $TWM2::BossName["Vardison"] = "Lord Vardison"; $TWM2::BossName["Trevor"] = "Lordranius Trevor"; $TWM2::BossName["GhostOfFire"] = "The Ghost Of Fire"; -$TWM2::BossName["Stormrider"] = "Commander Stormrider"; +$TWM2::BossName["Stormrider"] = "Cmdr. Stormrider"; $TWM2::BossName["GhostOfLightning"] = "The Ghost Of Lightning"; $TWM2::BossName["Vardison1"] = "Lord Vardison"; $TWM2::BossName["Vardison2"] = "Lord Vardison"; @@ -177,8 +180,8 @@ $TWM2::BossName["Vardison3"] = "Lord Vardison"; $TWM2::BossName["ShadeLord"] = "The Shade Lord"; //-----OFFICIAL VALUES-----\\ $TWM2::ZombieXPAward[1] = 1; // 1 -$TWM2::ZombieXPAward[2] = 5; // 5 -$TWM2::ZombieXPAward[3] = 18; // 18 +$TWM2::ZombieXPAward[2] = 10; // 10 +$TWM2::ZombieXPAward[3] = 20; // 20 $TWM2::ZombieXPAward[4] = 5; // 5 $TWM2::ZombieXPAward[5] = 10; // 10 $TWM2::ZombieXPAward[6] = 100; // 100