///
// Template file v5.12 (07/27/01)
////////////////////////////////////////////////////////////////////////
// File: war.wdl "Modified by the Mad Dog on
10/1/01"
// WDL prefabs for hostile entities
////////////////////////////////////////////////////////////////////////
// Mod Date: 05/18/01 DCP
// Fixed
"double shots" (the cause of robot1's self hits)
//
// Mod Date: 06/05/01 DCP
// attack_approach(), avoid_walls():
Replaced TO_ANGLE with vec_to_angle()
//
// Mod Date: 06/21/01 DCP
// Replaced
SYNONYMs with pointers
//
// Mod: 07/19/01 MWS
// state_wait(): Use specified
cowardice value instead of universal 30.
// Use
specified alertness instead of universal 1000.
// Also
now skips the player scan entirely if alertness is zero.
//
// attack_fire(): MY_ANGLE used in
shooting at player now varies by a random
// amount
between -MY._I_ACCURACY to +MY._I_ACCURACY
//
// Mod Date: 7/26/01 MWS
// attack_transitions(): Made
"escape" branch test for MY._I_COWARDICE
// instead
of default 30.
////////////////////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////////////////////
//DEFINE WAR_CHAT_ON; //
display 'thought' actions for the AIs
IFDEF WAR_CHAT_ON;
STRING
war_wait_str,"Waiting...";
STRING
war_attack_str,"Attacking!!!";
STRING
war_hunt_str,"Searching...";
STRING
war_escape_str,"Retreat!!!";
STRING
war_dead_str,"I have been undone!";
ENDIF;
//////////////////////////////////////////////////////////////////////
// WAR.WDL - fighting behaviour
///////////////////////////////////////////////////////////////////////
IFNDEF WAR_DEFS;
DEFINE
ACTOR_EXPLO,<explo+7.pcx>;
DEFINE
ACTOR_EXPLO_FRAMES,7;
ENDIF;
///////////////////////////////////////////////////////////////////////
var freeze_actors = 0; //
1,2 for testing purposes
///////////////////////////////////////////////////////////////
///Add different attack gun sounds for a killer bot (enemy)
////////////
SOUND bigax,<pickax.wav>; // replace with your sounds
sound gunmy,<snd1.wav>; //ex. "sound
NAME,<name.wav>;"
///////////////////////////////////////////////////////////////////////
DEFINE _STATE_WAIT,1;
DEFINE _STATE_ATTACK,2;
DEFINE _STATE_ESCAPE,3;
DEFINE _STATE_DIE,4;
DEFINE _STATE_FREEZE,5; // 05/15/01 DCP: Changed from 4->5
DEFINE _STATE_HUNT,6;
////////////////////////////////////////////////
// For different enemy gun sounds and or sword
////////
DEFINE MELEE,1; //
assign this in enemy action script example
DEFINE RANGED,2; // "my._attackmode = Melee;
DEFINE MYGUN,3; //
the gun sounds for these are controlled
//
in "function attack_fire"
DEFINE _ATTACKMODE,SKILL48;//attackmode definition for enemy
gun sound
// End of my different gun sounds
//////////////////////////////////////////////
DEFINE _MUZZLE_VERT,SKILL32;
DEFINE _I_ACCURACY,SKILL33; // 07/19/01 MWS: Added
DEFINE _I_COWARDICE,SKILL34; // 07/19/01 MWS: Added
DEFINE _I_ALERTNESS,SKILL35; // 07/19/01 MWS: Added
// DEFINE USER INPUT SKILLs
// These skill will be inputted from WED and work with
entities that use the actions:
//
"player_walk_fight" and "actor_walk_fight"
DEFINE _WALKSWIM_DIST,
SKILL1; // Walk Distance . Swim
Distance
DEFINE _RUNCRAWL_DIST,
SKILL2; // Run Distance . Crawl
Distance
DEFINE _STANDJUMP_TIME,
SKILL3; // Stand Time . Jump Time
DEFINE _ATTACKDUCK_TIME,
SKILL4; // Attack Time . Duck Time
DEFINE _RUNTHRESHOLD,
SKILL14; // Run Threshold
//DEFINE _FORCE,
SKILL5; // Force
//DEFINE _BANKING,
SKILL6; // Banking
amount (player only)
//DEFINE _HITMODE,
SKILL6; // Hitmode (actor only)
//DEFINE _MOVEMODE,
SKILL7; // Starting movemode
//DEFINE _FIREMODE,
SKILL8; // Firemode
//DEFINE _HEALTH,
SKILL9; // Heath
//DEFINE _ARMOR,
SKILL10; // Armor
DEFINE _ALERTNESS,
SKILL11; // Alertness
DEFINE _ACCURACY,
SKILL12; // Accuracy
DEFINE _COWARDICE,
SKILL13; // Cowardice
DEFINE _MUZZLEATTACH,
SKILL15; // Muzzle vertex .
Attach vertex
///////////////////////////////////////////////////////////////////////
entity* test_actor; //
test actor entity pointer. Used in robot_test
entity* ent_marker; //
marker entity, used in hunting
STRING robot_attack_str,
"attack"; // attack
frame names used by robot
ACTION robot_test
{
IFNDEF test;
remove(ME);
return;
ENDIF;
test_actor
= ME;
MY._WALKFRAMES
= 8.030;
MY._RUNFRAMES
= 4.050;
MY._ATTACKFRAMES
= 6;
MY._DIEFRAMES
= 1;
MY._FORCE =
2;
MY._FIREMODE
= DAMAGE_SHOOT+FIRE_PARTICLE+HIT_FLASH+0.05;
MY._HITMODE
= HIT_GIB;//HIT_EXPLO;
MY._WALKSOUND
= _SOUND_ROBOT;
drop_shadow();
anim_init();
actor_fight();
}
// Desc: Action for robot #1
// Init values and call actor_fight
ACTION robot1
{
MY._FORCE =
0.7;
MY._FIREMODE
= DAMAGE_EXPLODE+FIRE_BALL+HIT_EXPLO+BULLET_SMOKETRAIL+0.20;
MY._HITMODE
= 0;
MY._WALKSOUND
= _SOUND_ROBOT;
anim_init();
drop_shadow(); // attach shadow to robot
actor_fight();
// if(MY.FLAG4
== ON) { CALL patrol; }
}
// Desc: Action for robot #2
// Init values and call actor_fight
ACTION robot2
{
MY._FORCE =
2;
MY._FIREMODE
= DAMAGE_SHOOT+FIRE_PARTICLE+HIT_FLASH+0.05;
MY._HITMODE
= HIT_GIB;//HIT_EXPLO;
MY._WALKSOUND
= _SOUND_ROBOT;
anim_init();
drop_shadow();
// attach shadow to robot
actor_fight();
// if(MY.FLAG4
== ON) { patrol(); }
create(<arrow.pcx>,MY.POS,_ROBOT_TEST_WATCHER); // used to activate watcher drone
}
// Desc: Action for robot #3
// Init values and call actor_fight
//
// NOTE: robot #3 removes itself from the game!
ACTION robot3
{
remove(ME);
return;
MY._FORCE =
0.7;
MY._FIREMODE
= DAMAGE_EXPLODE+FIRE_BALL+HIT_EXPLO+BULLET_SMOKETRAIL+0.20;
anim_init();
drop_shadow();
actor_fight();
// if(MY.FLAG4
== ON) { patrol(); }
}
// Desc: Debugging utility, watches a ROBOT and sets flags
using its values
//
entity* ent_test_watcher; // marker entity, used in hunting
function _ROBOT_TEST_WATCHER()
{
MY.PASSABLE
= ON;
// MY.INVISIBLE
= ON;
MY.ENTITY1
= YOU;
WHILE(MY.ENTITY1
!= 0)
{
ent_test_watcher = MY.ENTITY1;
// tst_value_1
= ent_test_watcher._STATE;
wait(1);
}
// tst_value_1 = -1;
remove(ME);
}
///////////////////////////////////////////////////////////////////////
// Desc: the wait state
//
// Mod: 07/19/01 MWS
// Use specified cowardice value
instead of universal 30.
// Use specified alertness instead of
universal 1000.
// Also now skips the player scan
entirely if alertness is zero.
function state_wait()
{
IFDEF WAR_CHAT_ON;
msg.STRING = war_wait_str;
show_message();
ENDIF;
// init
values here for backwards compatibility
if(MY._I_COWARDICE
== 0)
{
MY._I_COWARDICE = 30; } // use default cowardice
if(MY._I_ALERTNESS
== 0)
{
MY._I_ALERTNESS = 1000; } //
use default alertness
MY._STATE =
_STATE_WAIT;
while(MY._STATE
== _STATE_WAIT)
{
if(MY._HEALTH <= 0) // no health? Die
{
EXCLUSIVE_ENTITY;
wait(1);
state_die();
return;
}
// Note: changed, negative cowardice
means never retreat
if(MY._HEALTH <=
MY._I_COWARDICE && MY._I_COWARDICE >= 0) // low health? Escape
{
EXCLUSIVE_ENTITY;
wait(1);
state_escape();
return;
}
if (MY._I_ALERTNESS > 0) // dont be alert if negative
alertness
{
// scan for the player coming near
temp.PAN = 180;
temp.TILT = 180;
temp.Z = MY._I_ALERTNESS;
indicator = _WATCH;
scan(MY.POS,MY.ANGLE,temp);
// if the player has been
detected...
if(MY._SIGNAL == _DETECTED)
{
MY._SIGNAL
= 0;
state_attack(); // ATTACK!
return;
}
}
force = 0; // no force from this actor
actor_move(); // react to outside forces (gravity, etc) even while
waiting
waitt(1);
}
}
// Desc: The attack state
//
function state_attack()
{
MY._STATE =
_STATE_ATTACK;
IFDEF WAR_CHAT_ON;
msg.STRING = war_attack_str;
show_message();
ENDIF;
MY._MOVEMODE
= _MODE_ATTACK; // stop patrolling etc.
attack_transitions(); // branch to other states depending on
values
// fire and
close distance
while(MY._STATE
== _STATE_ATTACK)
{
// fire two or three times
MY._COUNTER = 1; // MY._COUNTER = 1.5 + RANDOM(1);
//
check to see if player is shootable,
if((attack_fire())
== -1)
{
// cannot see player
EXCLUSIVE_ENTITY;
wait(1);
state_hunt();
return;
}
while(MY._COUNTER > 0) {
waitt(1); } // don't continue
until attack_fire is finished
//
walk towards player for one to three seconds
MY._COUNTER = 16 + RANDOM(32);
attack_approach();
while(MY._COUNTER > 0) {
waitt(1); } // don't continue until attack_approach is finished
}
}
// Desc: Use internal and external values to branch to other
states
//
// Mod Date: 7/26/01 MWS
// Made
"escape" branch test for MY._I_COWARDICE instead of default 30
function attack_transitions()
{
while(MY._STATE
== _STATE_ATTACK)
{
if(MY._HEALTH <= 0) // goto die state
{
EXCLUSIVE_ENTITY;
wait(1);
state_die();
return;
}
if(MY._HEALTH <=
MY._I_COWARDICE && MY._I_COWARDICE >= 0) // low health? Escape
{
EXCLUSIVE_ENTITY;
wait(1);
state_escape();
return;
}
if(freeze_actors > 0) // goto freeze state
{
EXCLUSIVE_ENTITY;
wait(1);
state_freeze();
return;
}
wait(1);
}
}
// Desc:
attack the player:
// -turn
towards player
// -check
if entity can see the player
// -play
fire animation
// -fire
shot
//
// Mod Date: 05/18/01 DCP
// Fixed
"double shots" (the cause of robot1's self hits)
//
// Mod Date: 07/19/01 MWS
// MY_ANGLE
used in shooting at player now varies by a random
// amount
between -MY._I_ACCURACY to +MY._I_ACCURACY
//
// Mod Date: 08/22/01 DCP
// Added "Advanced" Animation
for firing
///////////////////////////////////////////////////
/////////////////////////////////////////
//
function attack_fire()
{
while((MY._STATE
== _STATE_ATTACK) && (MY._COUNTER > 0))
{
// turn towards player
temp.X = player.X - MY.X;
temp.Y = player.Y - MY.Y;
temp.Z = player.Z - MY.Z;
vec_to_angle(MY_ANGLE,temp);
force = MY._FORCE * 2;
actor_turn();
// watch out if player visible. If
yes, then shoot at him
indicator = _WATCH;
//SHOOT MY.POS,player.POS;
trace_mode = IGNORE_ME +
IGNORE_PASSABLE + IGNORE_PASSENTS;
trace(MY.POS,player.POS);
if((RESULT > 0) && (YOU
== player)) // spotted him!
{
// fire at player
// 1) PLAY ATTACK ANIMATION
// check for *ADVANCED ANIMATION*
(frame names and entity tick/dist values)
if(int(MY._WALKFRAMES) < 0)
{
//
reset entity's animation time to zero
MY._ANIMDIST
= 0;
while(MY._ANIMDIST
< 50)
{
// if you have more than one attacking animation, here's
where you would test for it...
// calculate a percentage out of the animation time
MY._ANIMDIST +=
100*(TIME /
(INT(((-MY._ADVANIM_TICK)&MASK_ANIM_ATTACK_TICKS)>>10)));
// set the frame from the percentage
ent_frame(robot_attack_str,MY._ANIMDIST);
wait(1);
}
}
else
{
//
decide whether it's a frame number (old) or frame name (new) animation
if(frc(MY._WALKFRAMES)
> 0)// OLD STYLE ATTACK ANIMATION
{
// play attack animation
MY.FRAME = 1 + MY._WALKFRAMES + MY._RUNFRAMES + 1; // set frame to start
MY.NEXT_FRAME = 0; //
inbetween to the real next frame
while(MY.FRAME > 1 + MY._WALKFRAMES + MY._RUNFRAMES +
MY._ATTACKFRAMES)
{
wait(1);
MY.FRAME += 0.4 * TIME;
}
MY.FRAME
= 1 + MY._WALKFRAMES + MY._RUNFRAMES + 1;
// end at start frame
}//
END OLD STYLE ATTACK ANIMATION
else
// new animation format
(frc(MY._WALKFRAMES) == 0)
{
// reset entity's animation time to zero
MY._ANIMDIST = 0;
while(MY._ANIMDIST < 50)
{
wait(1);
// calculate a percentage out of the
animation time
MY._ANIMDIST
+= 8.0 * TIME; // attack in ~1 second
// set
the frame from the percentage
// -old-
SET_FRAME ME,robot_attack_str,MY._ANIMDIST;
ent_frame(robot_attack_str,MY._ANIMDIST);
}
}//
END NEW STYLE ATTACK ANIMATION
} // END NOT ADVANCED
// 2) fire shot
damage = frc(MY._FIREMODE) * 100;
fire_mode = MY._FIREMODE;
if((fire_mode & MODE_DAMAGE) ==
DAMAGE_SHOOT)
{
shot_speed.X
= 500;
}
else
{
shot_speed.X
= 100;
}
shot_speed.Y = 0;
shot_speed.Z = 0;
MY_ANGLE.PAN = MY.PAN; // TILT is already set from TO_ANGLE
// Add in a random amount according
to MY._I_ACCURACY
if (MY._I_ACCURACY != 0)
{
MY_ANGLE.PAN += random(MY._I_ACCURACY*2) -
MY._I_ACCURACY;
MY_ANGLE.TILT += random(MY._I_ACCURACY*2)
- MY._I_ACCURACY;
}
vec_rotate(shot_speed,MY_ANGLE);
// now shot_speed points ahead
// check to see if the model is
using a muzzle vertice
/////////////////////////////////////////////////////////////////
if(MY._MUZZLE_VERT == 0)
{
//
default gun muzzle
vec_set(gun_muzzle.X,MY.X);
}
else
{
ent_vertex(gun_muzzle.X,MY._MUZZLE_VERT);
}
///////////////////////////////////////////////////////////////
///////////////////// Add the new play entsound here /////////////////
///////////////////////////////////////////////////////////////
IF(MY._ATTACKMODE ==
MELEE)
{
play_entsound(ME,bigax,150); // change these sounds to your
}
IF(MY._ATTACKMODE
== RANGED) // own gun sound..
(Me,YOUR SOUND,150);
{
play_entsound(ME,gun_wham,150);
}
IF(MY._ATTACKMODE
== MYGUN)
{
play_entsound(ME,gunmy,150);
}
gun_shot();
//MY._COUNTER -= 1;// don't use this
//if(frc(MY._WALKFRAMES) == 0)//
don't use this
{
/////////////////////////////////////////////////////////////
// check for *ADVANCED ANIMATION* (frame names and entity
tick/dist values)
if(int(MY._WALKFRAMES) < 0)
{
//
set entity's animation to halfway
MY._ANIMDIST
= 50;
while(MY._ANIMDIST
< 100)
{
// if you have more than one attacking animation, here's
where you would test for it...
// calculate a percentage out of the animation time
MY._ANIMDIST +=
100 * (TIME /
(INT(((-MY._ADVANIM_TICK)&MASK_ANIM_ATTACK_TICKS)>>10)));
// set the frame from the percentage
ent_frame(robot_attack_str,MY._ANIMDIST);
wait(1);
}
}
else
{
if(frc(MY._WALKFRAMES)
== 0)
{
// play the second half of the animation
while(MY._ANIMDIST
< 100)
{
wait(1);
// calculate a percentage out of the
animation time
MY._ANIMDIST
+= 8.0 * TIME; // attack in ~1 second
// set
the frame from the percentage
// -old-
SET_FRAME ME,robot_attack_str,MY._ANIMDIST;
ent_frame(robot_attack_str,MY._ANIMDIST);
}
}//
END NEW STYLE ATTACK ANIMATION
}
}// END if((RESULT > 0)
&& (YOU == player))
else
{
MY._COUNTER = 0;
return(-1); // can not see player
}
waitt(2); // space shots appart (so they don't hit each other!
MY._COUNTER -= 1;
}//
END while((MY._STATE == _STATE_ATTACK)
&& (MY._COUNTER > 0))
}
// Desc: approach the player with random deviation
// do not approach any closer then 50
units
//
// Note: only
rotates by PANning
//
// Calls:
actor_turn()
// actor_move()
//
// Mod Date: 06/05/01 DCP
// Replaced TO_ANGLE with
vec_to_angle()
function attack_approach()
{
//
calculate a direction to walk into
temp.X =
player.X - MY.X;
temp.Y =
player.Y - MY.Y;
temp.Z = 0;
//--- TO_ANGLE
MY_ANGLE,temp;
vec_to_angle(MY_ANGLE,temp);
// ADD
random deviation angle
MY._TARGET_PAN
= MY_ANGLE.PAN - 15 + RANDOM(30);
while((MY._STATE
== _STATE_ATTACK) && (MY._COUNTER > 0))
{
// turn
towards player
MY_ANGLE.PAN = MY._TARGET_PAN;
MY_ANGLE.TILT = 0;
MY_ANGLE.ROLL = 0;
force = MY._FORCE * 2;
actor_turn();
// walk towards him if not too close
temp = (player.X - MY.X)*(player.X -
MY.X)+(player.Y - MY.Y)*(player.Y - MY.Y);
if(temp > 10000) // 100^2
{
force = MY._FORCE;
MY._MOVEMODE = _MODE_WALKING;
actor_move();
}
wait(1);
MY._COUNTER -= TIME;
}
}
// low on health => try to run away
function state_escape()
{
IFDEF WAR_CHAT_ON;
msg.STRING = war_escape_str;
show_message();
ENDIF;
MY._STATE =
_STATE_ESCAPE;
while(MY._STATE
== _STATE_ESCAPE)
{
if(MY._HEALTH <= 0)
{
EXCLUSIVE_ENTITY;
wait(1);
state_die();
return;
}
// turn away from player
temp.X = MY.X - player.X;
temp.Y = MY.Y - player.Y;
temp.Z = MY.Z - player.Z;
vec_to_angle(MY_ANGLE,temp);
force = MY._FORCE * 6;
actor_turn();
force = MY._FORCE * 4;
MY._MOVEMODE = _MODE_WALKING;
actor_move();
wait(1);
}
}
// Desc: Die:
// -stop
moving
// -stop
scanning and shooting
// -null
out my.event
// -play
death animation
// -become
passable
//
//
// Mod Date:
08/22/01 DCP
// Use advanced animation for death
(twice attack ticks).
// Notes: Uses
entity's _AMIMDIST value as a 'percent death' value (0 start, 100 end)
function state_die()
{
IFDEF WAR_CHAT_ON;
msg.STRING = war_dead_str;
show_message();
ENDIF;
MY._MOVEMODE
= 0; //
don't move anymore
MY._STATE =
_STATE_DIE;
MY.ENABLE_SCAN
= OFF; // get deaf and blind
MY.ENABLE_SHOOT
= OFF;
MY.EVENT =
NULL; // and don't react anymore
// check if
we have a marker...
if(MY.ENTITY1
!= 0)
{
// if so, remove it.
remove(MY.ENTITY1);
}
// check
for *ADVANCED ANIMATION* (frame names and entity tick/dist values)
if(int(MY._WALKFRAMES)
< 0)
{
// reset entity's animation time to
zero
MY._ANIMDIST = 0;
while(MY._ANIMDIST < 100)
{
// if you have more than one
attacking animation, here's where you would test for it...
// calculate a percentage out of the
animation time
MY._ANIMDIST += 100*(TIME /
(INT(((-MY._ADVANIM_TICK)&MASK_ANIM_ATTACK_TICKS)>>9))); //
(x>>10) * 2
// set the frame from the percentage
ent_frame(anim_death_str,MY._ANIMDIST);
wait(1);
}
}
else
{
// decide whether it's a frame
number (old) or frame name (new) animation
if(frc(MY._WALKFRAMES) > 0)// OLD
STYLE ATTACK ANIMATION
{
MY.FRAME = 1 + MY._WALKFRAMES + MY._RUNFRAMES +
MY._ATTACKFRAMES + 1;
MY.NEXT_FRAME = 0; // inbetween to the real next frame
while(MY.FRAME <= (1 +
MY._WALKFRAMES + MY._RUNFRAMES + MY._ATTACKFRAMES + MY._DIEFRAMES))
{
wait(1);
MY.FRAME
+= 0.7 * TIME;
}
}// END OLD STYLE Death ANIMATION
else // new animation format (frc(MY._WALKFRAMES) == 0)
{
// reset entity's animation time to
zero
MY._ANIMDIST = 0;
while(MY._ANIMDIST < 100)
{
wait(1);
//
calculate a percentage out of the animation time
MY._ANIMDIST
+= 5.0 * TIME; // death in ~1.25
seconds
//
set the frame from the percentage
//
-old- SET_FRAME ME,anim_death_str,MY._ANIMDIST;
ent_frame(anim_death_str,MY._ANIMDIST);
}
}// END NEW STYLE ATTACK ANIMATION
} // END
NOT ADVANCED
// If
entity explodes after death
if((ME !=
player) && ((MY._HITMODE & MODE_HIT) == HIT_EXPLO))//(MY._HITMODE
& HIT_EXPLO))
{
morph(ACTOR_EXPLO,ME);
MY._DIEFRAMES
= ACTOR_EXPLO_FRAMES;
actor_explode(); // ends with the removal of this entity
return;
}
// If entity 'gibs' after death
if((ME !=
player) && ((MY._HITMODE & MODE_HIT) == HIT_GIB))//(MY._HITMODE
& HIT_GIB))
{
_gib(25); // use new "gib" function
actor_explode(); // ends with the removal of this entity
return;
}
MY.PASSABLE
= ON; // body remains
}
// Desc: used to "tag" the last know target
location
// "CREATE
<arrow.pcx>, MY.POS, _last_know_target_loc;"
function _last_know_target_loc()
{
YOUR.ENTITY1
= ME;
MY.INVISIBLE
= ON;
MY.PASSABLE
= ON;
}
// Desc: move to the entity1 location
function hunt_approach()
{
ent_marker
= MY.ENTITY1;
//
calculate a direction to walk into
temp.X =
ent_marker.X - MY.X;
temp.Y =
ent_marker.Y - MY.Y;
temp.Z =
ent_marker.Z - MY.Z;
vec_to_angle(MY_ANGLE,temp);
MY._TARGET_PAN
= MY_ANGLE.PAN;
// turn towards
target
MY_ANGLE.PAN
= MY._TARGET_PAN;
MY_ANGLE.TILT
= 0;
MY_ANGLE.ROLL
= 0;
force =
MY._FORCE * 2;
actor_turn();
if(avoid_walls()
== 1) // avoid hitting walls
{
force = MY._FORCE * 4;
actor_turn();
}
temp =
(ent_marker.X - MY.X)*(ent_marker.X - MY.X)+(ent_marker.Y - MY.Y)*(ent_marker.Y
- MY.Y);
// walk
towards target only if not too close
if(temp
< 100) // 10^2
{
remove(MY.ENTITY1); // remove marker
EXCLUSIVE_ENTITY;
wait(1);
state_wait(); // branch to wait mode
return;
}
// walk
towards ent_marker
force =
MY._FORCE;
MY._MOVEMODE
= _MODE_WALKING;
actor_move();
}
// Desc: avoid hitting walls
//
// Return: 1 if to close to a wall, o otherwise
function avoid_walls()
{
// shoot a
line between the entity and the target point
SHOOT
MY.POS, ent_marker.POS;
IF((YOU !=
ent_marker) && (RESULT < 50)) //
too close to a wall...
{
// angle away from the wall.
temp.X = (TARGET.X + (100 *
NORMAL.X)) - MY.X;
temp.Y = (TARGET.Y + (100 *
NORMAL.Y)) - MY.Y;
temp.Z = MY.Z;
vec_to_angle(MY_ANGLE,temp);
RETURN(1);
}
RETURN(0);
}
// Desc: follow a wall until you reach a corner
//
// NOTE: to-do (pathfind.wdl?)
function follow_wall()
{
RETURN(0);
}
// Desc: Use internal and external values to branch to other
states
//
// Mod Date: 7/26/01 MWS
// Escape
branch tests for MY._I_COWARDICE instead of constant 30
function hunt_transitions()
{
while(MY._STATE
== _STATE_HUNT)
{
// Check heath
IF(MY._HEALTH <= 0) // goto die state
{
EXCLUSIVE_ENTITY;
wait(1);
state_die();
return;
}
if(MY._HEALTH <=
MY._I_COWARDICE && MY._I_COWARDICE >= 0) // low health? Escape
{
EXCLUSIVE_ENTITY;
wait(1);
state_escape();
return;
}
//
scan for the player coming near
temp.PAN = 180;
temp.TILT = 180;
temp.Z = MY._I_ALERTNESS;
indicator = _WATCH;
SCAN MY.POS,MY.ANGLE,temp;
// if the player has been
detected...
if(MY._SIGNAL == _DETECTED)
{
MY._SIGNAL = 0;
remove(MY.ENTITY1);
EXCLUSIVE_ENTITY;
wait(1);
state_attack(); // ATTACK!
return;
}
// Check debug
IF(freeze_actors > 0) // goto freeze state
{
EXCLUSIVE_ENTITY;
wait(1);
state_freeze();
return;
}
waitt(1);
}
}
// Desc: if you lose your target, move towards its last know
location while
// scanning.
function state_hunt()
{
MY._STATE =
_STATE_HUNT;
IFDEF WAR_CHAT_ON;
msg.STRING = war_hunt_str;
show_message();
ENDIF;
// create a
"marker" at the last known player location
create(<arrow.pcx>,
PLAYER.POS, _last_know_target_loc);
MY._MOVEMODE
= _MODE_WALKING; // stop patrolling etc.
hunt_transitions(); // branch to other states depending on
values
while(MY._STATE
== _STATE_HUNT)
{
// approach the marker
hunt_approach();
wait(1);
}
}
// Desc: freeze in place
function state_freeze()
{
MY._STATE =
_STATE_FREEZE;
MY._MOVEMODE
= _MODE_STILL; // stop patrolling
etc.
while(MY._STATE
== _STATE_FREEZE)
{
if(freeze_actors > 1)
{
MY.PASSABLE = ON;
}
if(freeze_actors == 0)
{
state_wait();
return;
}
wait(1);
}
}
////////////////////////////////////////////////////////////////////////
// Desc: take damage and detect player
//
// Mod: 02/06/01 DCP
// Added wait before shoot command to
remove 'Dangerous instruction error'
// Replaced SHOOT with trace()
//
// Mod: 05/05/01 DCP
// Modified damage from ranged
explosion (replaced ABS with MAX)
function fight_event()
{
if((EVENT_TYPE
== EVENT_SCAN && indicator == _EXPLODE)
|| (EVENT_TYPE == EVENT_SHOOT
&& indicator == _GUNFIRE))
{
MY._SIGNAL = _DETECTED; // by shooting, player gives away
his position
if (indicator == _EXPLODE)
{ //
reduce damage according to distance
damage *= max(0,(range -
RESULT))/range;
}
if(MY._ARMOR <= 0)
{
MY._HEALTH -= damage;
}
else
{
MY._ARMOR -= damage;
}
return;
}
if(EVENT_TYPE
== EVENT_DETECT && YOU == player)
{
wait(1); //fix for dangerous instruction
indicator = _WATCH;
//--- SHOOT MY.POS,YOUR.POS; // can player be seen from here?
trace_mode = IGNORE_ME + IGNORE_PASSABLE
+ ACTIVATE_SHOOT;
RESULT = trace(MY.POS,YOUR.POS); //
if entity YOU was visible from MY position, its SHOOT event was triggered
if((RESULT > 0) && (YOU
== player)) // yes
{
MY._SIGNAL = _DETECTED;
}
return;
}
}
// Desc: init actor for fighting and branch to the wait
state
function actor_fight()
{
// if(MY._ARMOR
== 0) { MY._ARMOR = 100; }
if(MY._HEALTH
== 0) { MY._HEALTH = 100; } //
default health
if(MY._FORCE
== 0) { MY._FORCE = 1; } //
default force
// Allow
player to pass thru actor if frozen
if(freeze_actors
> 1) { MY.PASSABLE = ON; }
MY._SIGNAL
= 0;
MY.ENABLE_SCAN
= ON;
MY.ENABLE_SHOOT
= ON;
MY.ENABLE_DETECT
= ON;
MY.EVENT =
fight_event;
state_wait();
}
// PLAYER ACTIONS
///////////////////////////////////////////////////////////////////////
// player_walk_fight - player walk and
fight action (basic FPS)
/////////////////////////////////////////////////////////////////////////
// Desc: player action. Walk and fight (take damage, use
gun)
//
// Used for
basic FPS type games. Set the following
skills and flags in
//WED to define the player (note: all skills default to 0,
all flags
//default to OFF)
//
// Flags
// Flag1 - ON = *NO damage from falls*
// OFF = Take damage from
falls
// Flag2 - ON = move as a car (i.e. no
turning unless moving),
// OFF = move normally
// Flag3 - ON = adapt tilt and roll
angle to slops
// OFF = stay upright
while walking up and down slops (normal)
// Flag4 - ON = *NO JUMPING*
// OFF = Allow jumping
// Flag5 - ON = *NO first person view
"head-bob" while moving*
// OFF = Enable first
person view "head-bob"
// Flag6 - ON = *Disable strafe*
// OFF = Allow strafing
// Flag7 - ON = *Disable trigger* (open
doors automatically)
// OFF = Trigger on
// Flag8 - ON = *NO DUCKING*
// OFF = Enable ducking
// Skills
// _WALKSWIM_DIST,
SKILL1; // Walk Distance . Swim Distance
// _RUNCRAWL_DIST,
SKILL2; // Run Distance . Crawl Distance
// _STANDJUMP_TIME,
SKILL3; // Stand Time . Jump Time
// _ATTACKDUCK_TIME,
SKILL4; // Attack Time . Duck
Time
// _RUNTHRESHOLD,
SKILL14; // Run Threshold
// _FORCE,
SKILL5; // Force
// _BANKING,
SKILL6; //
Hitmode (actor only)
// _MOVEMODE,
SKILL7; //
Starting movemode
// _HEALTH,
SKILL9; // Heath
// _ARMOR,
SKILL10; // Armor
//
// Mod: 6/18/01 DCP
// Created
//
// Mod: 8/21/01 DCP
// Updated to use new WED skills
//
ACTION player_walk_fight
{
// player
// uses: _WALKSWIM_DIST,_RUNCRAWL_DIST,_STANDJUMP_TIME,_ATTACKDUCK_TIME,
// _FORCE,_BANKING,_MOVEMODE,_FIREMODE,_HEALTH,_ARMOR,_RUNTHRESHOLD
//
Flags-------------------------------
IF(MY.FLAG1
== ON) { MY.__FALL = OFF; }
ELSE {
MY.__FALL = ON; }
//MY.__WHEELS
= MY.FLAG2; // note: redundant
//MY.__SLOPES
= MY.FLAG3; // note: redundant
IF(MY.FLAG4
== ON) { MY.__JUMP = OFF; }
ELSE {
MY.__JUMP = ON; }
IF(MY.FLAG5
== ON) { MY.__BOB = OFF; }
ELSE {
MY.__BOB = ON; }
IF(MY.FLAG6
== ON) { MY.__STRAFE = OFF; }
ELSE {
MY.__STRAFE = ON; }
IF(MY.FLAG7
== ON) { MY.__TRIGGER = OFF; }
ELSE {
MY.__TRIGGER = ON; }
IF(MY.FLAG8
== ON) { MY.__DUCK = OFF; }
ELSE {
MY.__DUCK = ON; }
//
Skill1------------------------------
if
(MY._HEALTH == 0) { //
if user didn't enter a value
MY._HEALTH = 100; // use
default
}
if
(MY._ARMOR < 0) { // if bad value specified somehow
MY._ARMOR = 0; //
default to zero
}
if(
MY._FORCE <= 0)
{
MY._FORCE = 0.75; // use default
}
player_anim_pack(); // use SKILLs 1-4 to set up animation
//
Other-------------------------------
MY.NARROW =
ON; // use narrow hull!
MY.FAT =
OFF;
MY.TRIGGER_RANGE = 5;
if(
MY._MOVEMODE <= 0)
{
MY._MOVEMODE = _MODE_WALKING;
}
if(
MY._BANKING == 0)
{
MY._BANKING = -0.1;
}
player_move(); // basic player move loop
player_fight(); // basic player fight loop
drop_shadow(); //
add a drop shadow (if the player has no 'real' shadow)
show_panels(); // show heath/armor/ammo panel
}
// Desc - An entity action that will make a generic AI
player with skill
// settings that can be set as above
in WED
////////////////////////////////////////////////////////////
// These skills can be set in WED for the intended effect //
////////////////////////////////////////////////////////////
// _WALKSWIM_DIST,
SKILL1; // Walk Distance . Swim Distance
// _RUNCRAWL_DIST,
SKILL2; // Run Distance . Crawl Distance
// _STANDJUMP_TIME,
SKILL3; // Stand Time . Jump Time
// _ATTACKDUCK_TIME,
SKILL4; // Attack Time . Duck
Time
// _RUNTHRESHOLD,
SKILL14; // Run Threshold
// _FORCE,
SKILL5; // Force
// _HITMODE,
SKILL6; // Hitmode (actor only)
// _MOVEMODE,
SKILL7; //
Starting movemode
// _FIREMODE,
SKILL8; //
Firemode
// _HEALTH,
SKILL9; // Heath
// _ARMOR,
SKILL10; // Armor
// _ALERTNESS,
SKILL11; // Alertness
// _ACCURACY,
SKILL12; //
Accuracy
// _COWARDICE,
SKILL13; //
Cowardice
// _MUZZLEATTACH,
SKILL15; // Muzzle vertex . Attach vertex
////////////////////////////////////////////////////////////
// 07/16/01 - MWS - Created
// 07/24/01 - MWS - Overhauled (16 WED skills now!)
//
// Mod: 8/21/01 DCP
// Updated to use new WED skills
//
// uses:
_WALKSWIM_DIST,_RUNCRAWL_DIST,_STANDJUMP_TIME,_ATTACKDUCK_TIME,
// _FORCE,_HITMODE,_MOVEMODE,_FIREMODE,_HEALTH,_ARMOR,_ALERTNESS,
// _ACCURACY,_COWARDICE,_RUNTHRESHOLD,_MUZZLEATTACH
//
action actor_walk_fight
{
// Health-
if
(MY._HEALTH <= 0) { MY._HEALTH = 50; } //
default 50 health
// Armor-
if
(MY._ARMOR <= 0) { MY._ARMOR = 0; } //
default 0 armor
// Force-
if
(MY._FORCE == 0) { MY._FORCE = 1.5; } //
default force
// Alertness-
MY._I_ALERTNESS
= MY._ALERTNESS;
//
Cowardice-
MY._I_COWARDICE
= MY._COWARDICE;
//
Firemode-
if
(MY._FIREMODE == 0)
{
MY._FIREMODE = DAMAGE_SHOOT+FIRE_PARTICLE+HIT_FLASH+0.10; } // use default
//
Accuracy-
if
(MY._ACCURACY > 0)
{
MY._I_ACCURACY = MY._ACCURACY; //
positive value given, use specified accuracy
}
else
{
if (MY._ACCURACY < 0)
{ MY._I_ACCURACY = 0; } // negative value given, use perfect
accuracy
else // MY._ACCURACY == 0
{ MY._I_ACCURACY = 5; } // no value given, use default
accuracy
}
// Hitmode-
//--MY._HITMODE
= MY._HITMODE; // zero is the
default, no extra checking needed
// Muzzle
Vertex-
MY._MUZZLE_VERT
= int(MY._MUZZLEATTACH);
// uses
same advanced animation packing as player (dont let the function name fool you)
player_anim_pack();
// Other
setup
MY._WALKSOUND
= _SOUND_ROBOT;
anim_init(); // setup animation a bit
drop_shadow();
// attach shadow to robot
actor_fight(); // start simple AI state machine
}
/*
These skills are set in WED as below - 7/12/01 - MWS
_WALKSWIM_DIST Walk
Distance . Swim Distance
_RUNCRAWL_DIST Run
Distance . Crawl Distance
_STANDJUMP_TIME
Stand Time . Jump Time
_ATTACKDUCK_TIME
Attack Time . Duck Time
_RUNTHRESHOLD
Run Threshold
Note: Times and distances after decimal use only TWO decimal
places
-----------
Default animation values, taken from animate.wdl
-----------
(divide these by 4 for quarter-seconds, as they're 16ths
now)
var anim_stand_ticks = 16; //
time for one standing anim cycle
var anim_jump_ticks = 6; //
time for one jump animation cycle
var anim_attack_ticks = 16;// time for one attack animation
cycle
var anim_duck_ticks = 8; //
time for one duck animation cycle
(multiply these by 4 for quarter-widths)
var anim_walk_dist = 1; //
dist per model width per walk cycle
var anim_swim_dist = 1; //
dist per model width per swim cycle
var anim_run_dist = 1.5; //
dist per model width per run cycle
var anim_crawl_dist = 0.8; // dist per model width per crawl
cycle
//var anim_wade_dist = 0.8;// dist per model width per crawl
cycle (unused)
var walk_or_run = 12; //
max quants per tick to switch from walk to run animation
*/
var pap_pack1;
// used to pack SKILL1
var pap_pack2;
// used to pack SKILL2
function player_anim_pack()
{
pap_pack1 =
0; //reset
pap_pack2 =
0; //reset
// Stand
Time
temp =
INT(MY._STANDJUMP_TIME); // quarter seconds
if (temp ==
0) { //
no value entered in WED
temp = 4; //
default anim_stand_ticks = 16
}
pap_pack1 += temp >> 10;
// fractional 10 bits (2^10 = 1024)
//pap_pack1
+= temp / 1024; //
fractional 10 bits (2^10 = 1024)
// Jump
Time
temp =
INT(FRC(MY._STANDJUMP_TIME) * 100); // quarter seconds
if (temp ==
0) { //
no value entered in WED
temp = 2; //
default anim_jump_ticks = 6
}
pap_pack1
+= temp << 6; //
middle 6 bits (2^6 = 64)
//pap_pack1
+= temp * 64; // middle 6 bits (2^6 = 64)
// Attack
Time
temp =
INT(MY._ATTACKDUCK_TIME); //
quarter seconds
if (temp ==
0) { //
no value entered in WED
temp = 4; //
default anim_attack_ticks = 16
}
pap_pack1
+= temp << 12; // upper 9 bits, shift 12 (2^12 =
4096)
//pap_pack1
+= temp * 4096; // upper 9 bits, shift 12 (2^12 = 4096)
// Duck
Time
temp =
INT(FRC(MY._ATTACKDUCK_TIME) * 100); // quarter seconds * 4 = game ticks
if (temp ==
0) { //
no value entered in WED
temp = 2; //
default anim_duck_ticks = 8
}
pap_pack1
+= temp; //
lowest 6 bits
// Walk
Distance
temp =
INT(MY._WALKSWIM_DIST); // quarter widths
if (temp ==
0) { //
no value entered in WED
temp = 4; //
default anim_walk_dist = 1
}
pap_pack2
+= temp << 10; // next upper 5 bits (2^10 = 1024)
//pap_pack2
+= temp * 1024; //
next upper 5 bits (2^10 = 1024)
// Swim
Distance
temp =
INT(FRC(MY._WALKSWIM_DIST) * 100); // quarter widths
if (temp ==
0) { //
no value entered in WED
temp = 4; //
default anim_swim_dist = 1
}
pap_pack2
+= temp; //
bottom 5 bits
//
Skill6------------------------------
// Run
Distance
temp =
INT(MY._RUNCRAWL_DIST); // quarter widths
if (temp ==
0) { //
no value entered in WED
temp = 6; //
default anim_run_dist = 1.5
}
pap_pack2
+= temp << 15; // upper 6 bits (2^15 = 2^10 * 2^5 =
1024 * 32)
//pap_pack2
+= temp * 1024 * 32; // upper 6 bits (2^15 = 2^10 * 2^5 =
1024 * 32)
// Crawl
Distance
temp =
INT(FRC(MY._RUNCRAWL_DIST) * 100); //
quarter widths
if (temp ==
0) { //
no value entered in WED
temp = 3; //
default anim_crawl_dist = 0.8
}
pap_pack2
+= temp << 5;
// upper
5 bits (2^5 = 32)
//pap_pack2
+= temp * 32; // upper 5 bits (2^5 = 32)
//
Skill7------------------------------
// Walk Or
Run
temp =
INT(MY._RUNTHRESHOLD); // whole quants
if (temp ==
0) { //
no value entered in WED
temp = 12; //
default walk_or_run = 12
}
pap_pack2
+= temp >> 10;
// fractional 10 bits (2^10 = 1024)
//pap_pack2
+= temp / 1024; // fractional 10 bits (2^10 = 1024)
MY._ADVANIM_TICK
= -1 * pap_pack1; // activate advanced animation
MY._ADVANIM_DIST = -1 * pap_pack2; // activate
advanced animation
}
function player_fight()
{
if(MY._HEALTH
== 0) { MY._HEALTH = 100; }
MY.ENABLE_SCAN
= ON;
MY.ENABLE_SHOOT
= ON;
MY.EVENT =
fight_event;
while(MY._HEALTH
> 0)
{
if(MY._SIGNAL == _DETECTED) { // Hit received?
MY._SIGNAL = 0; // reset the _signal skill
if(person_3rd == 0) {
player_shake();
}
}
wait(1);
}
// Dead
MY._HEALTH
= 0;
if(person_3rd
== 0)
{ // 1st person die action
MY._MOVEMODE = 0; //
don't move anymore
MY.EVENT = NULL; //
prevent health counting down
player_tip();
waitt(8);
weapon_remove(); // let weapon disappear
}
else
{
weapon_remove(); // prevent dead player firing
state_die();
return;
}
}
// Desc: explode into x number of random models
//
// Mod Date: 8/22/00 DCP
// Uses
function parameter (numberOfParts) to determine
// how many 'gib' parts to create
function _gib(numberOfParts)
{
temp = 0;
while(temp
< numberOfParts)
{
create(<gibbit.mdl>, MY.POS,
_gib_action);
temp += 1;
}
}
// Desc: Init and animate a gib-bit
//
// Mod Date: 6/29/00 Doug Poston
// Added
rotation and alpha-fading
// Mod Date: 8/31/00 DCP
// Scale
absdist vector by movement_scale
// Scale
the gibbits down by the actor_scale
function _gib_action()
{
// scall
the bits down by the actor_scale amount
vec_scale(MY.SCALE_X,actor_scale);
// Init gib
bit
MY._SPEED_X
= 25 * (RANDOM(10) - 5); // -125
-> +125
MY._SPEED_Y
= 25 * (RANDOM(10) - 5); // -125
-> +125
MY._SPEED_Z
= RANDOM(35) + 15; // 15 ->
50
MY._ASPEED_PAN
= RANDOM(35) + 35; // 35 -> 70
MY._ASPEED_TILT
= RANDOM(35) + 35; // 35 -> 70
MY._ASPEED_ROLL
= RANDOM(35) + 35; // 35 -> 70
MY.ROLL =
RANDOM(180); // start with a random
orientation
MY.PAN =
RANDOM(180);
MY.PUSH =
-1; // allow user/enemys to
push thru
// Animate
gib-bit
MY.SKILL9 =
RANDOM(50);
while(MY.SKILL9
> -75)
{
abspeed[0] = MY._SPEED_X * TIME;
abspeed[1] = MY._SPEED_Y * TIME;
abspeed[2] = MY._SPEED_Z * TIME;
MY.PAN += MY._ASPEED_PAN * TIME;
MY.TILT += MY._ASPEED_TILT * TIME;
MY.ROLL += MY._ASPEED_ROLL * TIME;
vec_scale(absdist,movement_scale); // scale absolute distance by
movement_scale
MOVE ME,NULLSKILL,abspeed;
IF(BOUNCE.Z)
{
MY._SPEED_Z = -(MY._SPEED_Z/2);
if(MY._SPEED_Z < 0.25)
{
MY._SPEED_X
= 0;
MY._SPEED_Y
= 0;
MY._SPEED_Z
= 0;
MY._ASPEED_PAN
= 0;
MY._ASPEED_TILT
= 0;
MY._ASPEED_ROLL
= 0;
}
}
MY._SPEED_Z -= 2;
MY.SKILL9 -= 1;
wait(1);
}
// Fade out
MY.transparent
= ON;
MY.alpha =
100;
while(1)
{
MY.alpha -= 5*time;
if(MY.alpha <=0)
{
// remove
remove(ME);
return;
}
wait(1);