Code: Select all
IDLE [idle] // idle animation
EMERGE [emerge] // emerge animation
DORMENT [dorment] // dorment animation
DIE [die] // die animation
HIT [hit] // hit animation
GROUP [hand]
HOSTILEPLAYER [true] // hostile to player
HOSTILESAME [false] // hostile to same Pawn group
HEALTHATTRIBUTE [enemy_health] // name of pawns health attribute
HEALTH [300] // initial pawn health
DAMAGEATTRIBUTE [health] // attribute damaged by attack
ALERTTRIGGER [AlertG] // name of alert trigger
DIEHOLD [15] // time corpse still appears
DIEFADE [10] // fadeout time of corpse
FOV [300] // field of view
SIGHTDIST [300] // max distance pawn can see at idle
ALERTSIGHTDIST [300] // max distance pawn can see when alert
ATTACKTYPE [melee] // type of attack - melee or missile
MELEEFUNC [monster_melee_start] // monster melee function
// the melee attack mode
MELEEATTACK [HIT] // melee attacking animations
MELEEATTACK1 [HIT]
MELEEATTACK2 [HIT]
MELEESOUND [brnatck3.wav] // sounds played when melee attacking
MELEESOUND1 [brnatck3.wav]
MELEESOUND2 [brnatck3.wav]
MELEERANGE [180] // max distance to start melee attack
MINMELEEDAMAGE [150] // minimum amount of damage per melee attack
MAXMELEEDAMAGE [300] // maximum amount of damage per melee attack
MELEEDELAY [1] // number of seconds between melee damages
MELEEDAMAGESOUND [melee2.wav] // sound played when damage is done
RUNFUNC [monster_run_start] // monster run to attack function
SHOOTFUNC [monster_melee] // monster missile function
LOSTFUNC [monster_lost_target_start] // monster lost target function
AS_NONE [0]
AS_SHOOT [2]
AS_STRAIGHT [3]
attack_delay [0]
lost_time [0]
back_up [false]
back_time [0]
left_time [0]
back_flag [false]
fire_delay [0]
skill_time [0]
attack_state [0]
spawn[ ()
{
//Console(true);
SetFOV(FOV); // set field of view
BoxWidth(40);
SetGroup(GROUP);
HostilePlayer(HOSTILEPLAYER);
HostileSame(HOSTILESAME);
// setup orders
AttributeOrder(HEALTHATTRIBUTE, HEALTH, "Death"); // give monster health
FindTargetOrder(SIGHTDIST, "FoundTarget", DAMAGEATTRIBUTE); // seen a target to chase
AddPainOrder("PainToAlert", 100); // show pain and trigger alert
AddTriggerOrder("IdleToAlert", ALERTTRIGGER, 0); // go to alert when triggered
NewOrder("idle");
} ]
idle[ ()
{
// just look around a bit
{ PlayAnimation("DORMENT", true, "");
RestartOrder();
} ]
PainToAlert[()
{
SetEventState(ALERTTRIGGER, true); // set trigger to go to alert
Return();
}]
IdleToAlert[()
{
FindTargetOrder(ALERTSIGHTDIST, "FoundTarget", DAMAGEATTRIBUTE); // increase viewing distance
AddTimerOrder(1, LOSTTIME, "AlertToIdle"); // go to idle after 10 secs
SetEventState(ALERTTRIGGER, false); // turn off alert trigger
NewOrder("Alert");
}]
Alert[()
{
// just look around a bit
{ PlayAnimation("Idle", true, "");
RestartOrder();
}]
FoundTarget[()
{
DelTimerOrder(1); // get rid of alert timer
LowLevel(RUNFUNC); // attack functions are low level
}]
LostTarget[ ()
{
{ PlayAnimation("DIE", true, "");
FindTargetOrder(SIGHTDIST, "FoundTarget", DAMAGEATTRIBUTE); // seen a target to chase
AddPainOrder("PainToAlert", 100); // trigger alert when pain
AddTriggerOrder("IdleToAlert", ALERTTRIGGER, 0); // go to alert when triggered
NewOrder("Idle");
} ]
Death[()
{
DelTimerOrder(1); // remove alert timer
AddPainOrder("PainToAlert", 0); // remove pain order
FindTargetOrder(0, "FoundTarget", DAMAGEATTRIBUTE); // remove target finding
DelTriggerOrder("IdleToAlert"); // remove alert trigger
SetNoCollision(); // remove bounding box so there are no collisions with corpse
AnimateStop(DIE, DIEHOLD, "");
FadeOut(DIEFADE, 0); // fade out corpse
Remove(true); // remove actor
}]
monster_lost_target_start[ ()
{
Animate(RUN); // play run animation
self.ThinkTime = 0; // start thinking on next frame
self.think = "monster_lost_target";
run_sound_time = time + RUNSOUNDDELAY;
} ]
monster_lost_target[ ()
{
self.ThinkTime = 0.1;
if(lost_time<time)
{
HighLevel("LostTarget"); // timed out while looking
return 0;
}
if(self.health<=0)
{
HighLevel("Death"); // died
return 0;
}
if((self.in_pain = true) and (random(1,100)<PAINPERCENT))
{
self.think = "monster_lost_pain_start"; // in pain
return 0;
}
if(EnemyExist(DAMAGEATTRIBUTE) < 3)
{
HighLevel("LostTarget"); // enemy died or was removed
return 0;
}
if(enemy_vis = true)
{
self.think = "monster_run_start"; // seen again so run to attack
self.ThinkTime = 0;
return 0;
}
if(run_sound_time < time)
{
if(random(1,10)>7)
{
run_sound_time = time + RUNSOUNDDELAY;
PlaySound(RUNSOUND);
}
}
if((enemy_range>POINTRADIUS) and (RUNSPEED > 0)) // get close to last known location
{
walk_movetogoal(random((RUNSPEED-2)*SCALE,(RUNSPEED+2)*SCALE));
}
else
{
HighLevel("LostTarget"); // can't find at last known location
return 0;
}
} ]
// start of showing pain while searching
monster_lost_pain_start[ ()
{
switch(random(1,4)) // play one of 4 pain animations
{
case 1
{
Animate(PAIN);
PlaySound(PAINSOUND);
}
case 2
{
Animate(PAIN1);
PlaySound(PAINSOUND1);
}
case 3
{
Animate(PAIN2);
PlaySound(PAINSOUND2);
}
case 4
{
Animate(PAIN3);
PlaySound(PAINSOUND3);
}
}
SetHoldAtEnd(true); // set to stop at end
self.ThinkTime = 0.1;
self.think = "monster_lost_pain";
} ]
// wait till animation is done
monster_lost_pain[ ()
{
self.ThinkTime = 0.1;
if(self.animate_at_end = true) // animation done
{
self.think = "monster_lost_target_start"; // go back to finding target
SetHoldAtEnd(false); // remove stop at end
self.ThinkTime = 0;
}
} ]
monster_run_start[ ()
{
self.ThinkTime = 0; // start thinking on next frame
self.think = "monster_run"; // go to run attack routine
attack_state = AS_NONE; // not attacking yet
melee_time = time;
run_sound_time = time;
} ]
// melee attack
monster_melee[ ()
{
self.ThinkTime = 0.1;
if(self.health<=0)
{
SetHoldAtEnd(false);
HighLevel("Death"); // you died
return 0;
}
if((self.in_pain = true) and (random(1,100)<PAINPERCENT))
{
SetHoldAtEnd(false);
self.think = "monster_melee_pain_start"; // in pain
return 0;
}
exist = EnemyExist(DAMAGEATTRIBUTE); // see if target is around
if(exist < 2)
{
SetHoldAtEnd(false);
HighLevel("LostTarget"); // enemy is dead and gone
return 0;
}
if(exist = 2)
{
SetHoldAtEnd(false);
HighLevel("DeadTarget"); // enemy is dead but body remains
return 0;
}
if(enemy_vis = false)
{
SetHoldAtEnd(false);
self.think = LOSTFUNC; // lost sight of enemy
lost_time = time + LOSTTIME;
return 0;
}
if(enemy_range>(MELEERANGE*SCALE))
{
SetHoldAtEnd(false);
self.think = RUNFUNC; // too far away so run toward
return 0;
}
ai_face(); // face enemy while attacking
if(self.animate_at_end = true) // animation is done
{
SetHoldAtEnd(false);
switch(random(1,3)) // play one of 3 melee animations
{
case 1
{
Animate(MELEEATTACK);
PlaySound(MELEESOUND);
}
case 2
{
Animate(MELEEATTACK1);
PlaySound(MELEESOUND1);
}
case 3
{
Animate(MELEEATTACK2);
PlaySound(MELEESOUND2);
}
}
SetHoldAtEnd(true); // set to stop at end
}
if(time>melee_time) // if time then damage
{
damage = random(MINMELEEDAMAGE, MAXMELEEDAMAGE); // get damage amount
Damage(damage, DAMAGEATTRIBUTE); // damage target
melee_time = time + MELEEDELAY; // reset time until next damage
PlaySound(MELEEDAMAGESOUND);
}
} ]
// melee attack setup
ai_run_melee[ ()
{
if(FacingIdeal()) // got close enough
{
self.think = MELEEFUNC; // start melee attack
self.attack_state = AS_STRAIGHT;
}
} ]
// check if nearly facing enemy
FacingIdeal[ ()
{
selfangle = self.current_yaw/0.0174532925199433; // your direction in degrees
idealangle = self.ideal_yaw/0.0174532925199433; // his direction in degrees
delta = selfangle - idealangle; // difference in directions
if (delta > -180 and delta < 180) // within 180 degrees is close enough
{
return true;
}
return false;
} ]
// check if ready to do actual attacking
CheckAnyAttack[ ()
{
if(ATTACKTYPE = "melee")
{
if(enemy_range<(MELEERANGE*SCALE)) // inside melee range
{
attack_state = AS_MELEE; // do a melee attack
return true;
}
return false;
}
if(ATTACKTYPE = "missile")
{
if(attack_delay>time)
{
return false;
}
if(enemy_range<MISSILERANGE) // inside missile range
{
attack_state = AS_MISSILE; // do a missile attack
return true;
}
}
return false;
} ]
}