#using scripts\shared\ai_shared; #using scripts\shared\clientfield_shared; #using scripts\shared\flag_shared; #using scripts\shared\fx_shared; #using scripts\shared\math_shared; #using scripts\shared\scene_shared; #using scripts\shared\spawner_shared; #using scripts\shared\util_shared; #using scripts\shared\array_shared; #using scripts\shared\laststand_shared; #using scripts\shared\ai\zombie_shared; #using scripts\shared\ai\zombie_death; #using scripts\shared\ai\systems\gib; #using scripts\shared\ai\systems\behavior_tree_utility; #using scripts\codescripts\struct; #namespace zombie_utility; function zombieSpawnSetup() { self.zombie_move_speed = "walk"; if ( !isdefined( self.zombie_arms_position ) ) { if(randomint( 2 ) == 0) self.zombie_arms_position = "up"; else self.zombie_arms_position = "down"; } self.missingLegs = false; self setAvoidanceMask( "avoid none" ); self PushActors( true ); clientfield::set( "zombie", true ); self.ignorePathEnemyFightDist = true; // prevent sight tracing in Actor_ShouldFaceMotion } function get_closest_valid_player( origin, ignore_player, ignore_laststand_players = false ) { PixBeginEvent( "get_closest_valid_player" ); valid_player_found = false; targets = GetPlayers(); if( isdefined( level.closest_player_targets_override ) ) { targets = [[ level.closest_player_targets_override ]](); } if( IsDefined( ignore_player ) ) { for(i = 0; i < ignore_player.size; i++ ) { ArrayRemoveValue( targets, ignore_player[i] ); } } done = true; while ( targets.size && !done ) { done = true; for(i = 0; i < targets.size; i++ ) { target = targets[i]; if( !is_player_valid( target, true, ignore_laststand_players ) ) { ArrayRemoveValue( targets, target ); done = false; break; } } } if( targets.size == 0 ) { pixendevent(); return undefined; } if( IsDefined( self.closest_player_override ) ) { target = [[ self.closest_player_override ]]( origin, targets ); } else if( IsDefined( level.closest_player_override ) ) { target = [[ level.closest_player_override ]]( origin, targets ); } if( IsDefined( target ) ) { pixendevent(); return target; } sortedPotentialTargets = ArraySortClosest( targets, self.origin ); while(sortedPotentialTargets.size) { if( is_player_valid( sortedPotentialTargets[0], true , ignore_laststand_players) ) { pixendevent(); return sortedPotentialTargets[0]; } ArrayRemoveValue( sortedPotentialTargets, sortedPotentialTargets[0] ); } pixendevent(); return undefined; } function is_player_valid( player, checkIgnoreMeFlag, ignore_laststand_players ) { if( !IsDefined( player ) ) { return false; } if( !IsAlive( player ) ) { return false; } if( !IsPlayer( player ) ) { return false; } if( IsDefined(player.is_zombie) && player.is_zombie == true ) { return false; } if( player.sessionstate == "spectator" ) { return false; } if( player.sessionstate == "intermission" ) { return false; } if( ( isdefined( player.intermission ) && player.intermission ) ) { return false; } if(!( isdefined( ignore_laststand_players ) && ignore_laststand_players )) { if( player laststand::player_is_in_laststand() ) { return false; } } if ( player IsNoTarget() ) { return false; } //We only want to check this from the zombie attack script if( ( isdefined( checkIgnoreMeFlag ) && checkIgnoreMeFlag ) && player.ignoreme ) { return false; } //for additional level specific checks if( IsDefined( level.is_player_valid_override ) ) { return [[ level.is_player_valid_override ]]( player ); } return true; } function append_missing_legs_suffix( animstate ) { if ( self.missingLegs && self HasAnimStateFromASD( animstate + "_crawl" ) ) { return animstate + "_crawl"; } return animstate; } // Every script calls initAnimTree to ensure a clean, fresh, known animtree state. // ClearAnim should never be called directly, and this should never occur other than // at the start of an animscript // This function now also does any initialization for the scripts that needs to happen // at the beginning of every main script. function initAnimTree(animscript) { if ( animscript != "pain" && animscript != "death" ) { self.a.special = "none"; } assert( IsDefined( animscript ), "Animscript not specified in initAnimTree" ); self.a.script = animscript; } // UpdateAnimPose does housekeeping at the start of every script's main function. function UpdateAnimPose() { assert( self.a.movement=="stop" || self.a.movement=="walk" || self.a.movement=="run", "UpdateAnimPose "+self.a.pose+" "+self.a.movement ); self.desired_anim_pose = undefined; } function initialize( animscript ) { if ( IsDefined( self.longDeathStarting ) ) { if ( animscript != "pain" && animscript != "death" ) { // we probably just came out of an animcustom. // just die, it's not safe to do anything else self DoDamage( self.health + 100, self.origin ); } if ( animscript != "pain" ) { self.longDeathStarting = undefined; self notify( "kill_long_death" ); } } if ( IsDefined( self.a.mayOnlyDie ) && animscript != "death" ) { // we probably just came out of an animcustom. // just die, it's not safe to do anything else self DoDamage( self.health + 100, self.origin ); } // scripts can define this to allow cleanup before moving on if ( IsDefined( self.a.postScriptFunc ) ) { scriptFunc = self.a.postScriptFunc; self.a.postScriptFunc = undefined; [[scriptFunc]]( animscript ); } if ( animscript != "death" ) { self.a.nodeath = false; } self.isHoldingGrenade = undefined; self.coverNode = undefined; self.changingCoverPos = false; self.a.scriptStartTime = GetTime(); self.a.atConcealmentNode = false; if ( IsDefined( self.node ) && (self.node.type == "Conceal Crouch" || self.node.type == "Conceal Stand") ) { self.a.atConcealmentNode = true; } initAnimTree( animscript ); UpdateAnimPose(); } function GetNodeYawToOrigin(pos) { if (IsDefined (self.node)) { yaw = self.node.angles[1] - GetYaw(pos); } else { yaw = self.angles[1] - GetYaw(pos); } yaw = AngleClamp180( yaw ); return yaw; } function GetNodeYawToEnemy() { pos = undefined; if ( isValidEnemy( self.enemy ) ) { pos = self.enemy.origin; } else { if (IsDefined (self.node)) { forward = AnglesToForward(self.node.angles); } else { forward = AnglesToForward(self.angles); } forward = VectorScale (forward, 150); pos = self.origin + forward; } if (IsDefined (self.node)) { yaw = self.node.angles[1] - GetYaw(pos); } else { yaw = self.angles[1] - GetYaw(pos); } yaw = AngleClamp180( yaw ); return yaw; } function GetCoverNodeYawToEnemy() { pos = undefined; if ( isValidEnemy( self.enemy ) ) { pos = self.enemy.origin; } else { forward = AnglesToForward(self.coverNode.angles + self.animarray["angle_step_out"][self.a.cornerMode]); forward = VectorScale (forward, 150); pos = self.origin + forward; } yaw = self.CoverNode.angles[1] + self.animarray["angle_step_out"][self.a.cornerMode] - GetYaw(pos); yaw = AngleClamp180( yaw ); return yaw; } function GetYawToSpot(spot) { pos = spot; yaw = self.angles[1] - GetYaw(pos); yaw = AngleClamp180( yaw ); return yaw; } // warning! returns (my yaw - yaw to enemy) instead of (yaw to enemy - my yaw) function GetYawToEnemy() { pos = undefined; if ( isValidEnemy( self.enemy ) ) { pos = self.enemy.origin; } else { forward = AnglesToForward(self.angles); forward = VectorScale (forward, 150); pos = self.origin + forward; } yaw = self.angles[1] - GetYaw(pos); yaw = AngleClamp180( yaw ); return yaw; } function GetYaw(org) { angles = VectorToAngles(org-self.origin); return angles[1]; } function GetYaw2d(org) { angles = VectorToAngles((org[0], org[1], 0)-(self.origin[0], self.origin[1], 0)); return angles[1]; } // 0 if I'm facing my enemy, 90 if I'm side on, 180 if I'm facing away. function AbsYawToEnemy() { assert( isValidEnemy( self.enemy ) ); yaw = self.angles[1] - GetYaw(self.enemy.origin); yaw = AngleClamp180( yaw ); if (yaw < 0) { yaw = -1 * yaw; } return yaw; } // 0 if I'm facing my enemy, 90 if I'm side on, 180 if I'm facing away. function AbsYawToEnemy2d() { assert( isValidEnemy( self.enemy ) ); yaw = self.angles[1] - GetYaw2d(self.enemy.origin); yaw = AngleClamp180( yaw ); if (yaw < 0) { yaw = -1 * yaw; } return yaw; } // 0 if I'm facing my enemy, 90 if I'm side on, 180 if I'm facing away. function AbsYawToOrigin(org) { yaw = self.angles[1] - GetYaw(org); yaw = AngleClamp180( yaw ); if (yaw < 0) { yaw = -1 * yaw; } return yaw; } function AbsYawToAngles(angles) { yaw = self.angles[1] - angles; yaw = AngleClamp180( yaw ); if (yaw < 0) { yaw = -1 * yaw; } return yaw; } function GetYawFromOrigin(org, start) { angles = VectorToAngles(org-start); return angles[1]; } function GetYawToTag(tag, org) { yaw = self GetTagAngles( tag )[1] - GetYawFromOrigin(org, self GetTagOrigin(tag)); yaw = AngleClamp180( yaw ); return yaw; } function GetYawToOrigin(org) { yaw = self.angles[1] - GetYaw(org); yaw = AngleClamp180( yaw ); return yaw; } function GetEyeYawToOrigin(org) { yaw = self GetTagAngles("TAG_EYE")[1] - GetYaw(org); yaw = AngleClamp180( yaw ); return yaw; } function GetCoverNodeYawToOrigin(org) { yaw = self.coverNode.angles[1] + self.animarray["angle_step_out"][self.a.cornerMode] - GetYaw(org); yaw = AngleClamp180( yaw ); return yaw; } function isStanceAllowedWrapper( stance ) { if ( IsDefined( self.coverNode ) ) { return self.coverNode doesNodeAllowStance( stance ); } return self IsStanceAllowed( stance ); } function GetClaimedNode() { myNode = self.node; if ( IsDefined(myNode) && (self nearNode(myNode) || (IsDefined( self.coverNode ) && myNode == self.coverNode)) ) { return myNode; } return undefined; } function GetNodeType() { myNode = GetClaimedNode(); if (IsDefined(myNode)) { return myNode.type; } return "none"; } function GetNodeDirection() { myNode = GetClaimedNode(); if (IsDefined(myNode)) { //thread [[anim.println]]("GetNodeDirection found node, returned: "+myNode.angles[1]);#/ return myNode.angles[1]; } //thread [[anim.println]]("GetNodeDirection didn't find node, returned: "+self.desiredAngle);#/ return self.desiredAngle; } function GetNodeForward() { myNode = GetClaimedNode(); if (IsDefined(myNode)) { return AnglesToForward ( myNode.angles ); } return AnglesToForward( self.angles ); } function GetNodeOrigin() { myNode = GetClaimedNode(); if (IsDefined(myNode)) { return myNode.origin; } return self.origin; } function safemod(a,b) { /* Here are some modulus results from in-game: 10 % 3 = 1 10 % -3 = 1 -10 % 3 = -1 -10 % -3 = -1 however, we never want a negative result. */ result = int(a) % b; result += b; return result % b; } // Gives the result as an angle between 0 and 360 function AngleClamp( angle ) { angleFrac = angle / 360.0; angle = (angleFrac - floor( angleFrac )) * 360.0; return angle; } // Returns an array of 4 weights (2 of which are guaranteed to be 0), which should be applied to forward, // right, back and left animations to get the angle specified. // front // /----|----\ // / 180 \ // /\ | /\ // / -135 | 135 \ // | \ | / | // left|-90----+----90-|right // | / | \ | // \ -45 | 45 / // \/ | \/ // \ 0 / // \----|----/ // back function QuadrantAnimWeights( yaw ) { // ALEXP 6/26/09: I don't understand why they'd want trig interpolation between angles instead of linear //forwardWeight = cos( yaw ); //leftWeight = sin( yaw ); forwardWeight = (90 - abs(yaw)) / 90; leftWeight = (90 - AbsAngleClamp180(abs(yaw-90))) / 90; result["front"] = 0; result["right"] = 0; result["back"] = 0; result["left"] = 0; if ( IsDefined( self.alwaysRunForward ) ) { assert( self.alwaysRunForward ); // always set alwaysRunForward to either true or undefined. result["front"] = 1; return result; } useLeans = GetDvarInt( "ai_useLeanRunAnimations"); if (forwardWeight > 0) { result["front"] = forwardWeight; if (leftWeight > 0) { result["left"] = leftWeight; } else { result["right"] = -1 * leftWeight; } } else if( useLeans ) { result["back"] = -1 * forwardWeight; if (leftWeight > 0) { result["left"] = leftWeight; } else { result["right"] = -1 * leftWeight; } } else //cod4 back strafe { // if moving backwards, don't blend. // it looks horrible because the feet cycle in the opposite direction. // either way, feet slide, but this looks better. backWeight = -1 * forwardWeight; if ( leftWeight > backWeight ) { result["left"] = 1; } else if ( leftWeight < forwardWeight ) { result["right"] = 1; } else { result["back"] = 1; } } return result; } function getQuadrant(angle) { angle = AngleClamp(angle); if (angle<45 || angle>315) { quadrant = "front"; } else if (angle<135) { quadrant = "left"; } else if (angle<225) { quadrant = "back"; } else { quadrant = "right"; } return quadrant; } // Checks to see if the input is equal to any of up to ten other inputs. function IsInSet(input, set) { for (i = set.size - 1; i >= 0; i--) { if (input == set[i]) { return true; } } return false; } function NotifyAfterTime(notifyString, killmestring, time) { self endon("death"); self endon(killmestring); wait time; self notify (notifyString); } function drawStringTime(msg, org, color, timer) { /# maxtime = timer*20; for (i=0;i 45 ) { if ( IsDefined( atNode ) && atNode.type != "Cover Crouch" && atNode.type != "Conceal Crouch" ) { return false; } if ( pitch > 45 || pitch < anim.coverCrouchLeanPitch - 45 ) { return false; } } return true; } function showLines(start, end, end2) { /# for (;;) { line(start, end, (1,0,0), 1); {wait(.05);}; line(start, end2, (0,0,1), 1); {wait(.05);}; } #/ } // Returns an animation from an array of animations with a corresponding array of weights. function anim_array(animArray, animWeights) { total_anims = animArray.size; idleanim = RandomInt(total_anims); assert (total_anims); assert (animArray.size == animWeights.size); if (total_anims == 1) { return animArray[0]; } weights = 0; total_weight = 0; for (i = 0; i < total_anims; i++) { total_weight += animWeights[i]; } anim_play = RandomFloat(total_weight); current_weight = 0; for (i = 0; i < total_anims; i++) { current_weight += animWeights[i]; if (anim_play >= current_weight) { continue; } idleanim = i; break; } return animArray[idleanim]; } function notForcedCover() { return ((self.a.forced_cover == "none") || (self.a.forced_cover == "Show")); } function forcedCover(msg) { return IsDefined(self.a.forced_cover) && (self.a.forced_cover == msg); } function print3dtime(timer, org, msg, color, alpha, scale) { /# newtime = timer / 0.05; for (i=0;i 0); } function scriptChange() { self.a.current_script = "none"; self notify (anim.scriptChange); } function delayedScriptChange() { {wait(.05);}; scriptChange(); } function sawEnemyMove(timer) { if (!IsDefined(timer)) { timer = 500; } return (GetTime() - self.personalSightTime < timer); } function canThrowGrenade() { if (!self.grenadeAmmo) { return false; } if (self.script_forceGrenade) { return true; } return (IsPlayer(self.enemy)); } function random_weight (array) { idleanim = RandomInt (array.size); if (array.size > 1) { anim_weight = 0; for (i=0;i 0; } function animArrayPickRandom( animname ) { assert( IsDefined( self.a.array ) ); /# if ( !IsDefined(self.a.array[animname]) ) { dumpAnimArray(); assert( IsDefined(self.a.array[animname]), "self.a.array[ \"" + animname + "\" ] is undefined" ); } #/ assert( self.a.array[animname].size > 0 ); if ( self.a.array[animname].size > 1 ) { index = RandomInt( self.a.array[animname].size ); } else { index = 0; } return self.a.array[animname][index]; } /# function dumpAnimArray() { println("self.a.array:"); keys = getArrayKeys( self.a.array ); for ( i=0; i < keys.size; i++ ) { if ( isarray( self.a.array[ keys[i] ] ) ) { println( " array[ \"" + keys[i] + "\" ] = {array of size " + self.a.array[ keys[i] ].size + "}" ); } else { println( " array[ \"" + keys[i] + "\" ] = ", self.a.array[ keys[i] ] ); } } } #/ function getAnimEndPos( theanim ) { moveDelta = getMoveDelta( theanim, 0, 1, self ); return self localToWorldCoords( moveDelta ); } function isValidEnemy( enemy ) { if ( !IsDefined( enemy ) ) { return false; } return true; } function damageLocationIsAny( a, b, c, d, e, f, g, h, i, j, k, ovr ) { /* possibile self.damageLocation's: "torso_upper" "torso_lower" "helmet" "head" "neck" "left_arm_upper" "left_arm_lower" "left_hand" "right_arm_upper" "right_arm_lower" "right_hand" "gun" "none" "left_leg_upper" "left_leg_lower" "left_foot" "right_leg_upper" "right_leg_lower" "right_foot" */ if(!isDefined(self.damageLocation)) return false; if ( !IsDefined( a ) ) return false; if ( self.damageLocation == a ) return true; if ( !IsDefined( b ) ) return false; if ( self.damageLocation == b ) return true; if ( !IsDefined( c ) ) return false; if ( self.damageLocation == c ) return true; if ( !IsDefined( d ) ) return false; if ( self.damageLocation == d ) return true; if ( !IsDefined( e ) ) return false; if ( self.damageLocation == e ) return true; if ( !IsDefined( f ) ) return false; if ( self.damageLocation == f ) return true; if ( !IsDefined( g ) ) return false; if ( self.damageLocation == g ) return true; if( !IsDefined( h ) ) return false; if( self.damageLocation == h ) return true; if( !IsDefined( i ) ) return false; if( self.damageLocation == i ) return true; if( !IsDefined( j ) ) return false; if( self.damageLocation == j ) return true; if( !IsDefined( k ) ) return false; if( self.damageLocation == k ) return true; assert(!IsDefined(ovr)); return false; } function ragdollDeath( moveAnim ) { self endon ( "killanimscript" ); lastOrg = self.origin; moveVec = (0,0,0); for ( ;; ) { {wait(.05);}; force = distance( self.origin, lastOrg ); lastOrg = self.origin; if ( self.health == 1 ) { self.a.nodeath = true; self startRagdoll(); {wait(.05);}; physicsExplosionSphere( lastOrg, 600, 0, force * 0.1 ); self notify ( "killanimscript" ); return; } } } function isCQBWalking() { return IsDefined( self.cqbwalking ) && self.cqbwalking; } function squared( value ) { return value * value; } function randomizeIdleSet() { self.a.idleSet = RandomInt( 2 ); } // meant to be used with any integer seed, for a small integer maximum (ideally one that divides anim.randomIntTableSize) function getRandomIntFromSeed( intSeed, intMax ) { assert( intMax > 0 ); index = intSeed % anim.randomIntTableSize; return anim.randomIntTable[ index ] % intMax; } // MikeD (1/24/2008): Added Banzai Feature. function is_banzai() { return IsDefined( self.banzai ) && self.banzai; } // SCRIPTER_MOD: JesseS (4/16/2008): HMG guys have their own anims function is_heavy_machine_gun() { return IsDefined( self.heavy_machine_gunner ) && self.heavy_machine_gunner; } function is_zombie() { if (IsDefined(self.is_zombie) && self.is_zombie) { return true; } return false; } function is_civilian() { if (IsDefined(self.is_civilian) && self.is_civilian) { return true; } return false; } function is_skeleton(skeleton) { if ((skeleton == "base") && IsSubStr(get_skeleton(), "scaled")) { // Scaled skeletons should identify as "base" as well return true; } return (get_skeleton() == skeleton); } function get_skeleton() { if (IsDefined(self.skeleton)) { return self.skeleton; } else { return "base"; } } function set_orient_mode( mode, val1 ) { /# if ( level.dog_debug_orient == self getentnum() ) { if ( IsDefined( val1 ) ) println( "DOG: Setting orient mode: " + mode + " " + val1 + " " + getTime() ); else println( "DOG: Setting orient mode: " + mode + " " + getTime() ); } #/ if ( IsDefined( val1 ) ) self OrientMode( mode, val1 ); else self OrientMode( mode ); } function debug_anim_print( text ) { /# if ( IsDefined( level.dog_debug_anims ) && level.dog_debug_anims ) println( text+ " " + getTime() ); if ( IsDefined( level.dog_debug_anims_ent ) && level.dog_debug_anims_ent == self getentnum() ) println( text+ " " + getTime() ); #/ } function debug_turn_print( text, line ) { /# if ( IsDefined( level.dog_debug_turns ) && level.dog_debug_turns == self getentnum() ) { duration = 200; currentYawColor = (1,1,1); lookaheadYawColor = (1,0,0); desiredYawColor = (1,1,0); currentYaw = AngleClamp180(self.angles[1]); desiredYaw = AngleClamp180(self.desiredangle); lookaheadDir = self.lookaheaddir; lookaheadAngles = vectortoangles(lookaheadDir); lookaheadYaw = AngleClamp180(lookaheadAngles[1]); println( text+ " " + getTime() + " cur: " + currentYaw + " look: " + lookaheadYaw + " desired: " + desiredYaw ); } #/ } function debug_allow_combat() { /# return ( anim_get_dvar_int( "debug_dog_allow_combat", "1" ) ); #/ return true; } function debug_allow_movement() { /# return ( anim_get_dvar_int( "debug_dog_allow_movement", "1" ) ); #/ return true; } //-------------------------------------------------------------------------------------------------- // FUNCTIONS MOVED TO SHARED UTILITY FROM CP (_ZM_UTILITY) //-------------------------------------------------------------------------------------------------- function set_zombie_var( zvar, value, is_float = false, column = 1, is_team_based = false ) { if(!isdefined(level.zombie_vars))level.zombie_vars=[]; if ( is_team_based ) { foreach( team in level.teams ) { if(!isdefined(level.zombie_vars[team]))level.zombie_vars[team]=[]; level.zombie_vars[ team ][ zvar ] = value; } } else { level.zombie_vars[zvar] = value; } return value; } function spawn_zombie( spawner,target_name,spawn_point,round_number) { if( !IsDefined( spawner ) ) { /# println( "ZM >> spawn_zombie - NO SPAWNER DEFINED" ); #/ return undefined; } while ( GetFreeActorCount() < 1 ) { {wait(.05);}; } spawner.script_moveoverride = true; if( ( isdefined( spawner.script_forcespawn ) && spawner.script_forcespawn )) { if(( SessionModeIsCampaignZombiesGame() )) guy = spawner spawner::spawn( true ); else if( IsActorSpawner( spawner ) && IsDefined( level.overrideZombieSpawn ) ) { guy = [[level.overrideZombieSpawn]](); } else { guy = spawner SpawnFromSpawner( 0, true ); } if( !zombie_spawn_failed( guy ) ) { guy.spawn_time = GetTime(); // Time at spawning if ( isdefined( level.giveExtraZombies ) ) { guy [[level.giveExtraZombies]](); } guy EnableAimAssist(); if(IsDefined(round_number)) { guy._starting_round_number = round_number; } //guy.type = "zombie"; guy.team = level.zombie_team; if( IsActor( guy ) ) guy ClearEntityOwner(); level.zombieMeleePlayerCounter = 0; if( IsActor( guy ) ) guy forceteleport( spawner.origin ); guy show(); spawner.count = 666; if( IsDefined( target_name ) ) { guy.targetname = target_name; } if(IsDefined(spawn_point) && IsDefined(level.move_spawn_func)) { guy thread [[level.move_spawn_func]](spawn_point); } /# if( IsDefined( spawner.zm_variant_type ) ) { guy.variant_type = spawner.zm_variant_type; } #/ return guy; } else { /# println( "ZM >> spawn_zombie - FAILED TO SPAWN A ZOMBIE FROM SPAWNER AT ", spawner.origin ); #/ return undefined; } } else { /# println( "ZM >> spawn_zombie - ZOMBIE SPAWNER MUST BE SET FORCESPAWN", spawner.origin ); #/ return undefined; } return undefined; } /@ "Name: zombie_spawn_failed( )" "Summary: Checks to see if the spawned AI spawned correctly or had errors. Also waits until all spawn initialization is complete. Returns true or false." "MandatoryArg: : The actor that just spawned" @/ function zombie_spawn_failed( spawn ) { if ( IsDefined(spawn) && IsAlive(spawn) ) { if ( IsAlive(spawn) ) { return false; } } return true; } //-------------------------------------------------------------------------------------------------- // FUNCTIONS MOVED TO SHARED UTILITY FROM CP (_ZM_SPAWNER) //-------------------------------------------------------------------------------------------------- function get_desired_origin() { if( IsDefined( self.target ) ) { ent = GetEnt( self.target, "targetname" ); if( !IsDefined( ent ) ) { ent = struct::get( self.target, "targetname" ); } if( !IsDefined( ent ) ) { ent = GetNode( self.target, "targetname" ); } assert( IsDefined( ent ), "Cannot find the targeted ent/node/struct, \"" + self.target + "\" at " + self.origin ); return ent.origin; } return undefined; } function hide_pop() { self endon( "death" ); self Ghost(); wait( 0.5 ); if ( IsDefined( self ) ) { self Show(); util::wait_network_frame(); if(IsDefined(self)) { self.create_eyes = true; } } } function handle_rise_notetracks(note, spot) { self thread finish_rise_notetracks(note, spot); } function finish_rise_notetracks(note, spot) { // the anim notetracks control which death anim to play // default to "deathin" (still in the ground) if (note == "deathout" || note == "deathhigh") { self.zombie_rise_death_out = true; self notify("zombie_rise_death_out"); wait 2; spot notify("stop_zombie_rise_fx"); } } /* function zombie_rise_death: function Track when the zombie should die, set the death anim, and stop the animscripted so he can die */ function zombie_rise_death(zombie, spot) { //self.nodeathragdoll = true; zombie.zombie_rise_death_out = false; zombie endon("rise_anim_finished"); while ( IsDefined( zombie ) && IsDefined( zombie.health ) && zombie.health > 1) // health will only go down to 1 when playing animation with AnimScripted() { zombie waittill("damage", amount); } if( IsDefined(spot) ) { spot notify("stop_zombie_rise_fx"); } if ( IsDefined( zombie ) ) { zombie.deathanim = zombie get_rise_death_anim(); zombie StopAnimScripted(); // stop the anim so the zombie can die. death anim is handled by the anim scripts. } } function get_rise_death_anim() { if ( self.zombie_rise_death_out ) { return "zm_rise_death_out"; } self.noragdoll = true; self.nodeathragdoll = true; return "zm_rise_death_in"; } function reset_attack_spot() { if( IsDefined( self.attacking_node ) ) { node = self.attacking_node; index = self.attacking_spot_index; node.attack_spots_taken[index] = false; self.attacking_node = undefined; self.attacking_spot_index = undefined; } } function zombie_gut_explosion() { self.guts_explosion=1; GibServerUtils::Annihilate( self ); } //-------------------------------------------------------------------------------------------------- // ZOMBIE EYE GLOWS //-------------------------------------------------------------------------------------------------- /* function delayed_zombie_eye_glow: function Fixes problem where zombies that climb out of the ground are warped to their start positions function and their eyes glowed above the ground for a split second before their animation started even function though the zombie model is hidden. and applying this delay to all the zombies doesn't really matter. */ function delayed_zombie_eye_glow() { self endon("zombie_delete"); self endon("death"); if ( ( isdefined( self.in_the_ground ) && self.in_the_ground ) || ( isdefined( self.in_the_ceiling ) && self.in_the_ceiling ) ) { while(!IsDefined(self.create_eyes)) { wait(0.1); } } else { wait .5; } self zombie_eye_glow(); } // When a Zombie spawns, set his eyes to glowing. function zombie_eye_glow() { if(!IsDefined(self) || !IsActor( self )) { return; } if ( !IsDefined( self.no_eye_glow ) || !self.no_eye_glow ) { self clientfield::set("zombie_has_eyes", 1); } } // Called when either the Zombie dies or if his head gets blown off function zombie_eye_glow_stop() { if(!IsDefined(self) || !IsActor( self )) { return; } if ( !IsDefined( self.no_eye_glow ) || !self.no_eye_glow ) { self clientfield::set("zombie_has_eyes", 0); } } //-------------------------------------------------------------------------------------------------- // ZOMBIE ROUND FUNCTIONALITY //-------------------------------------------------------------------------------------------------- //put the conditions in here which should //cause the failsafe to reset function round_spawn_failsafe_debug_draw() { self endon("death");//guy just died ////////////////////////////////////////////////////////////// //FAILSAFE "hack shit" DT#33203 ////////////////////////////////////////////////////////////// prevorigin = self.origin; while(1) { if( ( isdefined( level.toggle_keyline_always ) && level.toggle_keyline_always ) ) { self clientfield::set("zombie_keyline_render", 1); wait( 1.0 ); continue; } wait( 4.0 ); //if i've torn a board down in the last 5 seconds, just //wait again. if ( IsDefined(self.lastchunk_destroy_time) ) { if ( (GetTime() - self.lastchunk_destroy_time) < 8000 ) continue; } //hasnt moved 24 inches in 10 seconds - draw outline if ( DistanceSquared( self.origin, prevorigin ) < 576 ) { self clientfield::set("zombie_keyline_render", 1); } else { self clientfield::set("zombie_keyline_render", 0); } prevorigin = self.origin; } ////////////////////////////////////////////////////////////// //END OF FAILSAFE "hack" ////////////////////////////////////////////////////////////// } //-------------------------------------------------------------------------------------------------- // ZOMBIE ROUND FUNCTIONALITY //-------------------------------------------------------------------------------------------------- //put the conditions in here which should //cause the failsafe to reset function round_spawn_failsafe() { self endon("death");//guy just died if( ( isdefined( level.debug_keyline_zombies ) && level.debug_keyline_zombies ) ) { self thread round_spawn_failsafe_debug_draw(); } ////////////////////////////////////////////////////////////// //FAILSAFE "hack shit" DT#33203 ////////////////////////////////////////////////////////////// prevorigin = self.origin; while(1) { if( !level.zombie_vars["zombie_use_failsafe"] ) { return; } if ( ( isdefined( self.ignore_round_spawn_failsafe ) && self.ignore_round_spawn_failsafe ) ) { return; } if(!IsDefined(level.failsafe_waittime)) { level.failsafe_waittime = 30; } wait( level.failsafe_waittime ); if( self.missingLegs ) { wait( 10.0 ); } //inert zombies can ignore this if ( ( isdefined( self.is_inert ) && self.is_inert ) ) { continue; } //if i've torn a board down in the last 5 seconds, just //wait 30 again. if ( IsDefined(self.lastchunk_destroy_time) ) { if ( (GetTime() - self.lastchunk_destroy_time) < 8000 ) continue; } //fell out of world if ( self.origin[2] < level.zombie_vars["below_world_check"] ) { if(( isdefined( level.put_timed_out_zombies_back_in_queue ) && level.put_timed_out_zombies_back_in_queue ) && !level flag::get("special_round") && !( isdefined( self.isscreecher ) && self.isscreecher ) ) { level.zombie_total++; level.zombie_total_subtract++; } self dodamage( self.health + 100, (0,0,0) ); break; } //hasnt moved 24 inches in 30 seconds? if ( DistanceSquared( self.origin, prevorigin ) < 576 ) { if( IsDefined( level.move_failsafe_override ) ) { self thread [[level.move_failsafe_override]]( prevorigin ); } else { //add this zombie back into the spawner queue to be re-spawned if(( isdefined( level.put_timed_out_zombies_back_in_queue ) && level.put_timed_out_zombies_back_in_queue ) && !level flag::get("special_round")) { //only if they have crawled thru a window and then timed out if ( !self.ignoreall && !( isdefined( self.nuked ) && self.nuked ) && !( isdefined( self.marked_for_death ) && self.marked_for_death ) && !( isdefined( self.isscreecher ) && self.isscreecher ) && !self.missingLegs ) { level.zombie_total++; level.zombie_total_subtract++; } } //add this to the stats even tho he really didn't 'die' level.zombies_timeout_playspace++; // DEBUG HACK self dodamage( self.health + 100, (0,0,0) ); } break; } prevorigin = self.origin; } ////////////////////////////////////////////////////////////// //END OF FAILSAFE "hack" ////////////////////////////////////////////////////////////// } function ai_calculate_health( round_number ) { level.zombie_health = level.zombie_vars["zombie_health_start"]; for ( i=2; i <= round_number; i++ ) { // After round 10, get exponentially harder if ( i >= 10 ) { old_health = level.zombie_health; level.zombie_health += Int( level.zombie_health * level.zombie_vars["zombie_health_increase_multiplier"] ); if ( level.zombie_health < old_health ) { // we must have overflowed the signed integer space, just use the last good health, it'll give some headroom to the capped value to account for extra damage applications level.zombie_health = old_health; return; } } else { level.zombie_health = Int( level.zombie_health + level.zombie_vars["zombie_health_increase"] ); } } } function default_max_zombie_func( max_num, n_round ) { /# count = GetDvarInt( "zombie_default_max", -1 ); if ( count > -1 ) { return count; } #/ max = max_num; if( n_round < 2 ) { max = int( max_num * 0.25 ); } else if( n_round < 3 ) { max = int( max_num * 0.3 ); } else if( n_round < 4 ) { max = int( max_num * 0.5 ); } else if( n_round < 5 ) { max = int( max_num * 0.7 ); } else if( n_round < 6 ) { max = int( max_num * 0.9 ); } return max; } function zombie_speed_up() { if( level.round_number <= 3 ) { return; } level endon( "intermission" ); level endon( "end_of_round" ); level endon( "restart_round" ); level endon( "kill_round" ); // Wait until we've finished spawning while ( level.zombie_total > 4 ) { wait( 3.0 ); } // Keep checking as long as there's a zombie left. a_ai_zombies = get_round_enemy_array(); while( a_ai_zombies.size > 0 || level.zombie_total > 0 ) { if( a_ai_zombies.size == 1 ) { ai_zombie = a_ai_zombies[0]; if( IsAlive( ai_zombie ) ) { if ( IsDefined( level.zombie_speed_up ) ) { ai_zombie thread [[ level.zombie_speed_up ]](); } else { //set_zombie_run_cycle to sprint if( !( ai_zombie.zombie_move_speed === "sprint" ) ) { ai_zombie zombie_utility::set_zombie_run_cycle( "sprint" ); ai_zombie.zombie_move_speed_original = ai_zombie.zombie_move_speed; } } } } wait(0.5); a_ai_zombies = get_round_enemy_array(); } } function get_current_zombie_count() { enemies = get_round_enemy_array(); return enemies.size; } function get_round_enemy_array() { a_ai_enemies = []; a_ai_valid_enemies = []; a_ai_enemies = GetAITeamArray( level.zombie_team ); for( i = 0; i < a_ai_enemies.size; i++ ) { if ( ( isdefined( a_ai_enemies[i].ignore_enemy_count ) && a_ai_enemies[i].ignore_enemy_count ) ) { continue; } if ( !isdefined( a_ai_valid_enemies ) ) a_ai_valid_enemies = []; else if ( !IsArray( a_ai_valid_enemies ) ) a_ai_valid_enemies = array( a_ai_valid_enemies ); a_ai_valid_enemies[a_ai_valid_enemies.size]=a_ai_enemies[i];; } return a_ai_valid_enemies; } // Returns an array of all zombies function get_zombie_array() { enemies = []; valid_enemies = []; enemies = GetAiSpeciesArray( level.zombie_team, "all" ); for( i = 0; i < enemies.size; i++ ) { if ( enemies[i].archetype == "zombie" ) { if ( !isdefined( valid_enemies ) ) valid_enemies = []; else if ( !IsArray( valid_enemies ) ) valid_enemies = array( valid_enemies ); valid_enemies[valid_enemies.size]=enemies[i];; } } return valid_enemies; } // self = zombie function set_zombie_run_cycle_override_value( new_move_speed ) { set_zombie_run_cycle( new_move_speed ); self.zombie_move_speed_override = new_move_speed; } // self = zombie function set_zombie_run_cycle_restore_from_override() { str_restore_move_speed = self.zombie_move_speed_restore; self.zombie_move_speed_override = undefined; // turn off the override, so set_zombie_run_cycle will accept our change set_zombie_run_cycle( str_restore_move_speed ); } function set_zombie_run_cycle( new_move_speed ) { // if we're currently overriding the zombie speed, store off any attempted changes and we can restore them later if( isdefined( self.zombie_move_speed_override ) ) { self.zombie_move_speed_restore = new_move_speed; return; } self.zombie_move_speed_original = self.zombie_move_speed; if ( IsDefined( new_move_speed ) ) { self.zombie_move_speed = new_move_speed; } else { if( level.gamedifficulty == 0 ) //no sprinters on easy self set_run_speed_easy(); else self set_run_speed(); } if( IsDefined( level.zm_variant_type_max ) ) { /# // sjakatdar TU3 (11/12/2015) // Removed the debugging here to make sure that we have same behavior as ship as this lead to some ship only problems // If you need to test variations, just change the script locally if(0) { debug_variant_type = GetDvarInt( "scr_zombie_variant_type", -1 ); if( debug_variant_type != -1 ) { if(debug_variant_type <= level.zm_variant_type_max[self.zombie_move_speed][self.zombie_arms_position] ) { self.variant_type = debug_variant_type; } else { self.variant_type = level.zm_variant_type_max[self.zombie_move_speed][self.zombie_arms_position] - 1; } } else { self.variant_type = RandomInt( level.zm_variant_type_max[self.zombie_move_speed][self.zombie_arms_position] ); } } #/ if( self.archetype === "zombie" ) { if ( isdefined( self.zm_variant_type_max ) ) { self.variant_type = RandomInt( self.zm_variant_type_max[self.zombie_move_speed][self.zombie_arms_position] ); } else { if ( isdefined( level.zm_variant_type_max[self.zombie_move_speed] ) ) { self.variant_type = RandomInt( level.zm_variant_type_max[self.zombie_move_speed][self.zombie_arms_position] ); } else { /# ErrorMsg( "No variants set up for move speed " + self.zombie_move_speed ); #/ self.variant_type = 0; } } } } self.needs_run_update = true; self notify( "needs_run_update" ); self.deathanim = self zombie_utility::append_missing_legs_suffix( "zm_death" ); } function set_run_speed() { // We may want to force running zombies at the start of a round, to get zombies in to the arena quickly if( isdefined(level.zombie_force_run) ) { self.zombie_move_speed = "run"; level.zombie_force_run--; if( level.zombie_force_run <= 0 ) { level.zombie_force_run = undefined; } return; } rand = randomintrange( level.zombie_move_speed, level.zombie_move_speed + 35 ); // self thread print_run_speed( rand ); if( rand <= 35 ) { self.zombie_move_speed = "walk"; } else if( rand <= 70 ) { self.zombie_move_speed = "run"; } else { self.zombie_move_speed = "sprint"; } } function set_run_speed_easy() { rand = randomintrange( level.zombie_move_speed, level.zombie_move_speed + 25 ); // self thread print_run_speed( rand ); if( rand <= 35 ) { self.zombie_move_speed = "walk"; } else { self.zombie_move_speed = "run"; } } //self = zombie being knocked down //entity = entity knocking zombie down function setup_zombie_knockdown( entity ) { self.knockdown = true; zombie_to_entity = entity.origin - self.origin; zombie_to_entity_2d = VectorNormalize( ( zombie_to_entity[0], zombie_to_entity[1], 0 ) ); zombie_forward = AnglesToForward( self.angles ); zombie_forward_2d = VectorNormalize( ( zombie_forward[0], zombie_forward[1], 0 ) ); zombie_right = AnglesToRight( self.angles ); zombie_right_2d = VectorNormalize( ( zombie_right[0], zombie_right[1], 0 ) ); dot = VectorDot( zombie_to_entity_2d, zombie_forward_2d ); if( dot >= 0.5 ) { self.knockdown_direction = "front"; self.getup_direction = "getup_back"; } else if ( dot < 0.5 && dot > -0.5 ) { dot = VectorDot( zombie_to_entity_2d, zombie_right_2d ); if( dot > 0 ) { self.knockdown_direction = "right"; if ( math::cointoss() ) { self.getup_direction = "getup_back"; } else { self.getup_direction = "getup_belly"; } } else { self.knockdown_direction = "left"; self.getup_direction = "getup_belly"; } } else { self.knockdown_direction = "back"; self.getup_direction = "getup_belly"; } } function clear_all_corpses() { corpse_array = GetCorpseArray(); for ( i = 0; i < corpse_array.size; i++ ) { if ( IsDefined( corpse_array[ i ] ) ) { corpse_array[ i ] Delete(); } } } function get_current_actor_count() { count = 0; actors = GetAiSpeciesArray( level.zombie_team, "all" ); if (IsDefined(actors)) count += actors.size; count += get_current_corpse_count(); return count; } function get_current_corpse_count() { corpse_array = GetCorpseArray(); if (IsDefined(corpse_array)) return corpse_array.size; return 0; } //-------------------------------------------------------------------------------------------------- // ZOMBIE GIBBING //-------------------------------------------------------------------------------------------------- // gib limbs if enough firepower occurs function zombie_gib_on_damage() { // self endon( "death" ); while( 1 ) { self waittill( "damage", amount, attacker, direction_vec, point, type, tagName, ModelName, Partname, weapon ); if( !IsDefined( self ) ) { return; } if( !self zombie_should_gib( amount, attacker, type ) ) { continue; } if( self head_should_gib( attacker, type, point ) && type != "MOD_BURNED" ) { self zombie_head_gib( attacker, type ); continue; } if( !( isdefined( self.gibbed ) && self.gibbed ) && isDefined(self.damageLocation) ) { // The head_should_gib() above checks for this, so we should not randomly gib if shot in the head if ( self damagelocationisany( "head", "helmet", "neck" ) ) { continue; } //Zombie is about to stumble, so don't gib. self.stumble = undefined; switch( self.damageLocation ) { case "torso_upper": case "torso_lower": // HACK the torso that gets swapped for guts also removes the left arm // so we need to sometimes do another ref if ( !GibServerUtils::IsGibbed( self, 32 ) ) GibServerUtils::GibRightArm( self ); break; case "right_arm_upper": case "right_arm_lower": case "right_hand": if ( !GibServerUtils::IsGibbed( self, 32 ) ) GibServerUtils::GibRightArm( self ); break; case "left_arm_upper": case "left_arm_lower": case "left_hand": if ( !GibServerUtils::IsGibbed( self, 16 ) ) GibServerUtils::GibLeftArm( self ); break; case "right_leg_upper": case "right_leg_lower": case "right_foot": if( self.health <= 0 ) { GibServerUtils::GibRightLeg( self ); if( randomint( 100 ) > 75 ) GibServerUtils::GibLeftLeg( self ); self.missingLegs = true; } break; case "left_leg_upper": case "left_leg_lower": case "left_foot": if( self.health <= 0 ) { GibServerUtils::GibLeftLeg( self ); if( randomint( 100 ) > 75 ) GibServerUtils::GibRightLeg( self ); self.missingLegs = true; } break; default: if( self.damageLocation == "none" ) { // SRS 9/7/2008: might be a nade or a projectile if( type == "MOD_GRENADE" || type == "MOD_GRENADE_SPLASH" || type == "MOD_PROJECTILE" || type == "MOD_PROJECTILE_SPLASH" ) { // ... in which case we have to derive the ref ourselves self derive_damage_refs( point ); break; } } } if( IsDefined( level.custom_derive_damage_refs ) ) { //refs = self [[ level.custom_derive_damage_refs ]](refs, point, weapon); } // Don't stand if a leg is gone if( ( isdefined( self.missingLegs ) && self.missingLegs ) && self.health > 0 )//( self.a.gib_ref == "no_legs" || self.a.gib_ref == "right_leg" || self.a.gib_ref == "left_leg" ) && self.health > 0 ) //( GibServerUtils::IsGibbed(self, GIB_LEGS_LEFT_LEG_FLAG) || GibServerUtils::IsGibbed(self, GIB_LEGS_RIGHT_LEG_FLAG) ) && self.health > 0) { self AllowedStances( "crouch" ); // reduce collbox so player can jump over self setPhysParams( 15, 0, 24 ); self AllowPitchAngle( 1 ); self setPitchOrient(); health = self.health; health = health * 0.1; if ( IsDefined( self.crawl_anim_override ) ) { self [[ self.crawl_anim_override ]](); } } if( self.health > 0 ) { if ( IsDefined( level.gib_on_damage ) ) { self thread [[ level.gib_on_damage ]](); } } } } } function add_zombie_gib_weapon_callback( weapon_name, gib_callback, gib_head_callback ) { if(!isdefined(level.zombie_gib_weapons))level.zombie_gib_weapons=[]; if(!isdefined(level.zombie_gib_head_weapons))level.zombie_gib_head_weapons=[]; level.zombie_gib_weapons[weapon_name] = gib_callback; level.zombie_gib_head_weapons[weapon_name] = gib_head_callback; } function have_zombie_weapon_gib_callback( weapon ) { if(!isdefined(level.zombie_gib_weapons))level.zombie_gib_weapons=[]; if(!isdefined(level.zombie_gib_head_weapons))level.zombie_gib_head_weapons=[]; if ( IsWeapon( weapon ) ) weapon = weapon.name; if ( IsDefined(level.zombie_gib_weapons[weapon] ) ) return true; return false; } function get_zombie_weapon_gib_callback( weapon, damage_percent ) { if(!isdefined(level.zombie_gib_weapons))level.zombie_gib_weapons=[]; if(!isdefined(level.zombie_gib_head_weapons))level.zombie_gib_head_weapons=[]; if ( IsWeapon( weapon ) ) weapon = weapon.name; if ( IsDefined(level.zombie_gib_weapons[weapon] ) ) { return self [[level.zombie_gib_weapons[weapon]]]( damage_percent ); } return false; } function have_zombie_weapon_gib_head_callback( weapon ) { if(!isdefined(level.zombie_gib_weapons))level.zombie_gib_weapons=[]; if(!isdefined(level.zombie_gib_head_weapons))level.zombie_gib_head_weapons=[]; if ( IsWeapon( weapon ) ) weapon = weapon.name; if ( IsDefined(level.zombie_gib_head_weapons[weapon] ) ) return true; return false; } function get_zombie_weapon_gib_head_callback( weapon, damage_location ) { if(!isdefined(level.zombie_gib_weapons))level.zombie_gib_weapons=[]; if(!isdefined(level.zombie_gib_head_weapons))level.zombie_gib_head_weapons=[]; if ( IsWeapon( weapon ) ) weapon = weapon.name; if ( IsDefined(level.zombie_gib_head_weapons[weapon] ) ) { return self [[level.zombie_gib_head_weapons[weapon]]]( damage_location ); } return false; } function zombie_should_gib( amount, attacker, type ) { if( !IsDefined( type ) ) { return false; } if ( ( isdefined( self.is_on_fire ) && self.is_on_fire ) ) { return false; } if ( IsDefined( self.no_gib ) && ( self.no_gib == 1 ) ) { return false; } prev_health = amount + self.health; if( prev_health <= 0 ) { prev_health = 1; } damage_percent = ( amount / prev_health ) * 100; weapon = undefined; if( IsDefined( attacker ) ) { if( IsPlayer( attacker ) || ( isdefined( attacker.can_gib_zombies ) && attacker.can_gib_zombies ) ) { if( IsPlayer( attacker ) ) { weapon = attacker GetCurrentWeapon(); } else { weapon = attacker.weapon; } if ( have_zombie_weapon_gib_callback( weapon ) ) { if ( self get_zombie_weapon_gib_callback( weapon, damage_percent ) ) { return true; } return false; } } } switch( type ) { case "MOD_UNKNOWN": case "MOD_TELEFRAG": case "MOD_FALLING": case "MOD_SUICIDE": case "MOD_TRIGGER_HURT": case "MOD_BURNED": return false; case "MOD_MELEE": //Z2 HasPerk( "specialty_altmelee" ) is returning undefined // if( isPlayer( attacker ) && randomFloat( 1 ) > 0.25 && attacker HasPerk( "specialty_altmelee" ) ) // { // return true; // } // else { return false; } } if( type == "MOD_PISTOL_BULLET" || type == "MOD_RIFLE_BULLET" ) { if( !IsDefined( attacker ) || !IsPlayer( attacker ) ) { return false; } if( weapon == level.weaponNone || (IsDefined(level.start_weapon) && weapon == level.start_weapon) || weapon.isGasWeapon ) { return false; } } // println( "**DEBUG amount = ", amount ); // println( "**DEBUG self.head_gibbed = ", self.head_gibbed ); // println( "**DEBUG self.health = ", self.health ); if( damage_percent < 10 /*|| damage_percent >= 100*/ ) { return false; } return true; } function head_should_gib( attacker, type, point ) { if( ( isdefined( self.head_gibbed ) && self.head_gibbed ) ) { return false; } if( !isdefined( attacker ) ) { return false; } if( !IsPlayer( attacker ) ) { if( !( isdefined( attacker.can_gib_zombies ) && attacker.can_gib_zombies ) )//allow non player types to gib (robot) { return false; } } // check if the attacker was a player if( IsPlayer(attacker )) { weapon = attacker GetCurrentWeapon(); } else { weapon = attacker.weapon; } if ( have_zombie_weapon_gib_head_callback( weapon ) ) { if ( self get_zombie_weapon_gib_head_callback( weapon, self.damagelocation ) ) { return true; } return false; } // SRS 9/2/2008: check for damage type // - most SMGs use pistol bullets // - projectiles = rockets, raygun if( type != "MOD_RIFLE_BULLET" && type != "MOD_PISTOL_BULLET" ) { // maybe it's ok, let's see if it's a grenade if( type == "MOD_GRENADE" || type == "MOD_GRENADE_SPLASH" ) { if( Distance( point, self GetTagOrigin( "j_head" ) ) > 55 ) { return false; } else { // the grenade airburst close to the head so return true return true; } } else if( type == "MOD_PROJECTILE" ) { if( Distance( point, self GetTagOrigin( "j_head" ) ) > 10 ) { return false; } else { return true; } } // shottys don't give a testable damage type but should still gib heads else if( weapon.weapClass != "spread" ) { return false; } } // check location now that we've checked for grenade damage (which reports "none" as a location) if ( !self damagelocationisany( "head", "helmet", "neck" ) ) { return false; } // check weapon - don't want "none", base pistol, or flamethrower if( ( type == "MOD_PISTOL_BULLET" && weapon.weapClass != "smg" && weapon.weapClass != "spread" ) || weapon == level.weaponNone || (IsDefined(level.start_weapon) && weapon == level.start_weapon) || weapon.isGasWeapon ) { return false; } //DCS: temporarily added for cp zombie gibbing until can check a weapon gdt "do gibbing" availability. if( SessionModeIsCampaignGame() && (type == "MOD_PISTOL_BULLET" && weapon.weapClass != "smg")) { return false; } // check the enemy's health low_health_percent = ( self.health / self.maxhealth ) * 100; if( low_health_percent > 10 ) { // TOOD(David Young 9-15-14): Commenting out because gibbing anything // should not happen here. Will remove completely once hat gibbing is supported. // self zombie_hat_gib( attacker, type ); return false; } return true; } function zombie_hat_gib( attacker, means_of_death ) { self endon( "death" ); if ( ( isdefined( self.hat_gibbed ) && self.hat_gibbed ) ) { return; } if ( !IsDefined( self.gibSpawn5 ) || !IsDefined( self.gibSpawnTag5 ) ) { return; } self.hat_gibbed = true; if ( IsDefined( self.hatmodel ) ) { self detach( self.hatModel, "" ); } temp_array = []; temp_array[0] = level._ZOMBIE_GIB_PIECE_INDEX_HAT; self gib( "normal", temp_array ); //stat tracking if ( IsDefined( level.track_gibs ) ) { level [[ level.track_gibs ]]( self, temp_array ); } } function head_gib_damage_over_time( dmg, delay, attacker, means_of_death ) { self endon( "death" ); self endon( "exploding" ); if( !IsAlive( self ) ) { return; } if( !IsPlayer( attacker ) ) { attacker = self; } if ( !IsDefined( means_of_death ) ) { means_of_death = "MOD_UNKNOWN"; } dot_location = self.damageLocation; dot_weapon = self.damageweapon; while( 1 ) { if( IsDefined( delay ) ) { wait( delay ); } if (IsDefined(self)) { if( ( isdefined( self.no_gib ) && self.no_gib ) ) { return; } if(IsDefined(attacker)) { self DoDamage( dmg, self GetTagOrigin( "j_neck" ), attacker, self, dot_location, means_of_death, 0, dot_weapon );//player can drop out } else { self DoDamage( dmg, self GetTagOrigin( "j_neck" )); } } } } // SRS 9/7/2008: need to derive damage location for types that return location of "none" function derive_damage_refs( point ) { if( !IsDefined( level.gib_tags ) ) { init_gib_tags(); } closestTag = undefined; for( i = 0; i < level.gib_tags.size; i++ ) { if( !IsDefined( closestTag ) ) { closestTag = level.gib_tags[i]; } else { if( DistanceSquared( point, self GetTagOrigin( level.gib_tags[i] ) ) < DistanceSquared( point, self GetTagOrigin( closestTag ) ) ) { closestTag = level.gib_tags[i]; } } } // figure out the refs based on the tag returned if( closestTag == "J_SpineLower" || closestTag == "J_SpineUpper" || closestTag == "J_Spine4" ) { // HACK the torso that gets swapped for guts also removes the left arm // so we need to sometimes do another ref //GibServerUtils::GibEntity( self, GIB_TORSO_GUTS_FLAG ); GibServerUtils::GibRightArm( self ); } else if( closestTag == "J_Shoulder_LE" || closestTag == "J_Elbow_LE" || closestTag == "J_Wrist_LE" ) { if ( !GibServerUtils::IsGibbed( self, 16 ) ) GibServerUtils::GibLeftArm( self ); } else if( closestTag == "J_Shoulder_RI" || closestTag == "J_Elbow_RI" || closestTag == "J_Wrist_RI" ) { if ( !GibServerUtils::IsGibbed( self, 32 ) ) GibServerUtils::GibRightArm( self ); } else if( closestTag == "J_Hip_LE" || closestTag == "J_Knee_LE" || closestTag == "J_Ankle_LE" ) { if ( ( isdefined( self.noCrawler ) && self.noCrawler ) ) { return; } GibServerUtils::GibLeftLeg( self ); if( randomint(100) > 75) GibServerUtils::GibRightLeg( self ); self.missingLegs = true; } else if( closestTag == "J_Hip_RI" || closestTag == "J_Knee_RI" || closestTag == "J_Ankle_RI" ) { if ( ( isdefined( self.noCrawler ) && self.noCrawler ) ) { return; } GibServerUtils::GibRightLeg( self ); if( randomint(100) > 75) GibServerUtils::GibLeftLeg( self ); self.missingLegs = true; } //return refs; } function init_gib_tags() { tags = []; // "guts", "right_arm", "left_arm", "right_leg", "left_leg", "no_legs" // "guts" tags[tags.size] = "J_SpineLower"; tags[tags.size] = "J_SpineUpper"; tags[tags.size] = "J_Spine4"; // "left_arm" tags[tags.size] = "J_Shoulder_LE"; tags[tags.size] = "J_Elbow_LE"; tags[tags.size] = "J_Wrist_LE"; // "right_arm" tags[tags.size] = "J_Shoulder_RI"; tags[tags.size] = "J_Elbow_RI"; tags[tags.size] = "J_Wrist_RI"; // "left_leg"/"no_legs" tags[tags.size] = "J_Hip_LE"; tags[tags.size] = "J_Knee_LE"; tags[tags.size] = "J_Ankle_LE"; // "right_leg"/"no_legs" tags[tags.size] = "J_Hip_RI"; tags[tags.size] = "J_Knee_RI"; tags[tags.size] = "J_Ankle_RI"; level.gib_tags = tags; } //-------------------------------------------------------------------------------------------------- function getAnimDirection( damageyaw ) { if( ( damageyaw > 135 ) ||( damageyaw <= -135 ) ) // Front quadrant { return "front"; } else if( ( damageyaw > 45 ) &&( damageyaw <= 135 ) ) // Right quadrant { return "right"; } else if( ( damageyaw > -45 ) &&( damageyaw <= 45 ) ) // Back quadrant { return "back"; } else { // Left quadrant return "left"; } return "front"; } function anim_get_dvar_int( dvar, def ) { return int( anim_get_dvar( dvar, def ) ); } // dvar set/fetch/check function anim_get_dvar( dvar, def ) { if ( GetDvarString( dvar ) != "" ) return getdvarfloat( dvar ); else { SetDvar( dvar, def ); return def; } } function makeZombieCrawler( b_both_legs ) { if( ( isdefined( b_both_legs ) && b_both_legs ) ) { val = 100; } else { val = randomint( 100 ); } if( val > 75 ) { GibServerUtils::GibRightLeg( self ); GibServerUtils::GibLeftLeg( self ); } else if ( val > 37 ) GibServerUtils::GibRightLeg( self ); else GibServerUtils::GibLeftLeg( self ); self.missingLegs = true; self AllowedStances( "crouch" ); // reduce collbox so player can jump over self setPhysParams( 15, 0, 24 ); self AllowPitchAngle( 1 ); self setPitchOrient(); health = self.health; health = health * 0.1; } function zombie_head_gib( attacker, means_of_death ) { self endon( "death" ); if ( ( isdefined( self.head_gibbed ) && self.head_gibbed ) ) { return; } if( ( isdefined( self.no_gib ) && self.no_gib ) ) { return; } self.head_gibbed = true; self zombie_eye_glow_stop(); if( !( isdefined( self.disable_head_gib ) && self.disable_head_gib ) ) { GibServerUtils::GibHead( self ); } self thread head_gib_damage_over_time( ceil( self.health * 0.2 ), 1, attacker, means_of_death ); } function gib_random_parts() { if( ( isdefined( self.no_gib ) && self.no_gib ) ) { return; } val = randomint( 100 ); if( val > 50 ) { self zombie_utility::zombie_head_gib(); } val = randomint( 100 ); if( val > 50 ) { GibServerUtils::GibRightLeg( self ); } val = randomint( 100 ); if( val > 50 ) { GibServerUtils::GibLeftLeg( self ); } val = randomint( 100 ); if( val > 50 ) { if ( !GibServerUtils::IsGibbed( self, 32 ) ) GibServerUtils::GibRightArm( self ); } val = randomint( 100 ); if( val > 50 ) { if ( !GibServerUtils::IsGibbed( self, 16 ) ) GibServerUtils::GibLeftArm( self ); } } // ------------- ignore player utility ----------- // function autoexec init_ignore_player_handler() { level._IGNORE_PLAYER_HANDLER = []; } function register_ignore_player_handler( archetype, ignore_player_func ) { Assert( IsDefined( archetype ), "IgnorePlayerHandler undefined archetype." ); Assert( !IsDefined( level._IGNORE_PLAYER_HANDLER[ archetype ] ), "IgnorePlayerHandler for " + archetype + " is already registered." ); level._IGNORE_PLAYER_HANDLER[ archetype ] = ignore_player_func; } function run_ignore_player_handler() { if ( IsDefined( level._IGNORE_PLAYER_HANDLER[ self.archetype ] ) ) { self [[ level._IGNORE_PLAYER_HANDLER[ self.archetype ] ]](); } } // hit marker on player, call this in the damage callback for AI on the player attacker to show crosshair function show_hit_marker() // self = player { if ( IsDefined( self ) && IsDefined( self.hud_damagefeedback ) ) { self.hud_damagefeedback SetShader( "damage_feedback", 24, 48 ); self.hud_damagefeedback.alpha = 1; self.hud_damagefeedback FadeOverTime(1); self.hud_damagefeedback.alpha = 0; } }