mirror of
https://github.com/PhantomGamesDevelopment/TWM2.git
synced 2026-03-02 20:10:31 +00:00
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.
This commit is contained in:
parent
f7095e8823
commit
f4c2eeabc6
8 changed files with 153 additions and 120 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
//**********************************************
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue