Code: Select all
{
GROUP [bunny] // name of monster group
BOXWIDTH [60] // width and depth of bounding box
HOSTILEPLAYER [true] // hostile to player
HOSTILEDIFFERENT [false] // hostile to different Pawn group
HOSTILESAME [false] // hostile to same Pawn group
HEALTHATTRIBUTE [health] // name of health attribute
HEALTH [100] // initial amount of health
SIGHTDIST [450] // max distance monster can see at idle
ALERTSIGHTDIST [600] // max distance monster can see when alert
FOV [180] // field of view in degrees
YAWSPEED [120] // speed of rotation in deg/sec
DAMAGEATTRIBUTE [health] // attribute damaged by attack
ALERTTRIGGER [Alert] // triggername used to go to alert mode
STAND [idle]
TURNL [walk]
TURNR [walk]
PAIN [pain_head]
PAIN [pain_stomach]
PAINPERCENT [25]
DIE [die_spin]
DIE1 [die_backwards]
DIEHOLD [10]
DIEFADE [5]
RUN [run]
RUNSPEED [120]
MELEEATTACK [punches]
MELEERANGE [90]
MINMELEEDAMAGE [25]
MAXMELEEDAMAGE [30]
MELEEDELAY [2]
RUNFUNC [monster_run_start]
MELEEFUNC [monster_melee_start]
LOSTFUNC [monster_lost_target_start]
AS_NONE [0]
AS_MELEE [2]
AS_STRAIGHT [3]
melee_time [0]
lost_time [0]
back_up [false]
back_time [0]
left_time [0]
back_flag [false]
attack_state [0]
Spawn[ ()
{
Console(true);
BoxWidth(BOXWIDTH); // set bounding box width/depth
AttributeOrder(HEALTHATTRIBUTE, HEALTH, "Death"); // give monster health
HostilePlayer(HOSTILEPLAYER); // set who monster is hostile to
HostileSame(HOSTILESAME);
HostileDifferent(HOSTILEDIFFERENT);
SetFOV(FOV); // set field of view
SetGroup(GROUP); // assign a group to belong to
FindTargetOrder(SIGHTDIST, "FoundTarget", DAMAGEATTRIBUTE); // seen a target to chase
AddPainOrder("IdlePain", 100); // show pain and trigger alert
AddTriggerOrder("IdleToAlert", ALERTTRIGGER, 0); // go to alert when triggered
NewOrder("Idle");
} ]
Idle[()
{
PlayAnimation(STAND, true, "");
if(random(1,10)>6)
{
if(random(1,10)>5)
{
Rotate(TURNL, 102, 0, 90, 0, "");
PlayAnimation(STAND, true, "");
Rotate(TURNR, 108, 0, -90, 0, "");
}
else
{
Rotate(TURNR, 108, 0, -90, 0,"");
PlayAnimation(STAND, true, "");
Rotate(TURNL, 102, 0 90, 0, "");
}
}
RestartOrder();
}]
IdlePain[ ()
{
switch(random(1,2))
{
case1
{
PlayAnimation(PAIN, true, "");
}
case2
{
PlayAnimation(PAIN1, true, "");
}
}
SetEventState(ALERTTRIGGER, true);
Return();
}]
IdleToAlert[ ()
{
FindTargetOrder(ALERTSIGHTDIST, "FoundTarget", DAMAGEATTRIBUTE);
AddPainOrder("AlertPain", PAINPERCENT);
AddTimerOrder(1, 10, "AlerToIdle");
SetEventStateventState(ALERTTRIGGER, false);
NewOrder("Alert");
}]
Alert[ ()
{
PlayAnimation(STAND, true, "");
if(random(1,10)>3) // turn around once in awhile
{
if(random(1,10)>5) // turn around to left
{
Rotate(TURNL, 102, 0, 90, 0, "");
Rotate(TURNL, 102, 0, 90, 0, "");
Rotate(TURNL, 102, 0, 90, 0, "");
Rotate(TURNL, 102, 0, 90, 0, "");
}
else // turn around to right
{
Rotate(TURNR, 108, 0, -90, 0, "");
Rotate(TURNR, 108, 0, -90, 0, "");
Rotate(TURNR, 108, 0, -90, 0, "");
Rotate(TURNR, 108, 0, -90, 0, "");
}
}
RestartOrder();
}]
AlertPain[ ()
{
switch(random(1,2))
{
case1
{
PlayAnimation(PAIN, true, "");
}
case2
{
PlayAnimation(PAIN1, true, "");
}
}
Return();
}]
AlertToIdle[ ()
{
DelTimerOrder(1);
LowLevel1(RUNFUNC);
}]
LostTarget{ ()
{
FindTargetOrder(SIGHTDIST, "FoundTarget", DAMAGEATTRIBUTE);
AddPainOrder("IdlePain", 100);
AddTriggerOrder("IdleToAlert", ALERTTRIGGER, 0);
NewOrder("Idle");
}
}]
DeadTarget[ ()
{
FindTargetOrder(SIGHTDIST, "FoundTarget", DAMAGEATTRIBUTE);
AddPainOrder("IdlePain", 100);
AddTriggerOrder("IdleToAlert", ALERTTRIGGER, 0);
NewOrder("Idle");
}
}]
Death[ ()
{
DelTimerOrder(1);
AddPainOrder("IdlePain", 0);
FindTargetOrder(0, "FoundTarget", DAMAGEATTRIBUTE);
DelTriggerOrder("IdleToAlert");
SetNoCollision();
switch(random(1,2))
{
case1
{
AnimateStop(DIE, DIEHOLD, "")
}
case2
{
AnimateStop(DIE1, DIEHOLD, "")
}
}
FadeOut(DIEFADE, 0);
Remove(true);
}]
monster_run_start[()
{
Animate(RUN);
self.ThinkTime = 0;
self.think = "monster_run";
self.ideal_yaw = elsenemy_yaw;
attack_state state = AS_NONE
self.yaw_speed = YAWSPEED;
melee_time = time;
back_up = false;
}]
monster_run[ ()
{
self.ThinkTime = 0.1;
if(self.health<=0)
{
HighLevel("Death");
return 0 ;
}
if((self.in_pain = true) and (random(1,100)<PAINPERCENT))
{
self.think = "monster_run_pain_start";
return 0 ;
}
if(EnemyExist(DAMAGEATTRIBUTE) < 3)
{
HighLevel("LostTarget");
return 0;
}
if(enemy_vis = false)
{
self.think = LOSTFUNC;
lost_time = time + LOSTTIME;
return 0;
}
ai_run(random(RUNSPEED-2,RUNSPEED+2));
}]
monster_run_pain_start[()
{
Animate(PAIN);
SetHoldAtEnd(true);
self.ThinkTime = 0.1;
self.think = "monster_run_pain";
} ]
// wait for animation to stop
monster_run_pain[ ()
{
self.ThinkTime = 0.1;
if(self.animate_at_end = true) // wait for end of animation
{
self.think = "monster_run_start"; // start running again
SetHoldAtEnd(false); // remove animation stop
self.ThinkTime = 0;
}
} ]
// start of lost sight of enemy routine
monster_lost_target_start[ ()
{
Animate(RUN); // play run animation
self.ThinkTime = 0; // start thinking on next frame
self.think = "monster_lost_target";
} ]
// go to last known location of enemy
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(enemy_range>POINTRADIUS) // get close to last known location
{
walk_movetogoal(random(RUNSPEED-2,RUNSPEED+2)); // move to last knowm location
}
else
{
HighLevel("LostTarget"); // can't find at last known location
return 0;
}
} ]
// start of showing pain
monster_lost_pain_start[ ()
{
Animate(PAIN); // play pain animation
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;
}
} ]
// start of melee attack
monster_melee_start[ ()
{
Animate(MELEEATTACK); // play melee attack animation
self.ThinkTime = 0;
self.think = "monster_melee";
} ]
// melee attack
monster_melee[ ()
{
self.ThinkTime = 0.1;
if(self.health<=0)
{
HighLevel("Death"); // you died
return 0;
}
if((self.in_pain = true) and (random(1,100)<PAINPERCENT))
{
self.think = "monster_melee_pain_start"; // in pain
return 0;
}
exist = EnemyExist(DAMAGEATTRIBUTE); // see if target is around
if(exist < 2)
{
HighLevel("LostTarget"); // enemy is dead and gone
return 0;
}
if(exist = 2)
{
HighLevel("DeadTarget"); // enemy is dead but body remains
return 0;
}
if(enemy_vis = false)
{
self.think = LOSTFUNC; // lost sight of enemy
lost_time = time + LOSTTIME;
return 0;
}
if(enemy_range>MELEERANGE)
{
self.think = RUNFUNC; // too far away so run toward
return 0;
}
ai_face(); // face enemy while attacking
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
}
} ]
// start of showing pain
monster_melee_pain_start[ ()
{
Animate(PAIN); // play pain animation
SetHoldAtEnd(true); // set to stop at end
self.ThinkTime = 0.1;
self.think = "monster_melee_pain";
} ]
// wait until animation is done
monster_melee_pain[ ()
{
self.ThinkTime = 0.1;
if(self.animate_at_end = true) // animation is done
{
self.think = "monster_melee_start"; // go back to melee attack
SetHoldAtEnd(false);
self.ThinkTime = 0;
melee_time = time + MELEEDELAY; // reset attack deley
}
} ]
// basic AI routines
// run toward enemy and see if you are ready to attack
ai_run[ (dist)
{
if (attack_state = AS_MELEE) // do melee attack
{
ai_run_melee();
return 0;
}
if (CheckAnyAttack()) // check if you can start the actual attack
{
return 0;
}
walk_movetogoal(dist); // move toward the enemy
} ]
// melee attack setup
ai_run_melee[ ()
{
ai_face(); // turn to face target
if(FacingIdeal()) // got close enough
{
self.think = MELEEFUNC; // start melee attack
self.attack_state = AS_STRAIGHT;
}
} ]
// face enemy
ai_face[ ()
{
self.ideal_yaw = enemy_yaw;
ChangeYaw(); // rotate to face enemy
} ]
// use walkmove to navigate to enemy
walk_movetogoal[ (dist)
{
if(IsFalling = true)
{
return 0; // don't move while falling
}
if(back_up = false)
{
ai_face(); // turn to face enemy
if(FacingIdeal())
{
if(walkmove(self.current_yaw, dist) = true)
{
return 0; // can move in current direction
}
else // blocked
{
if(random(1,10)<3) // backup and move sideways sometimes
{
back_up = true;
back_time = time + 0.5;
back_flag = false;
return 0;
}
else
{
ForceUp(30); // jump up, forward and to side
ForceForward(30);
if(random(1,10)<6)
{
ForceRight(30);
}
else
{
ForceLeft(30);
}
}
}
}
}
else
{
if(back_flag = false) // go backward 1/2 sec
{
if(back_time > time)
{
walkmove((self.current_yaw-(180*0.0174532925199433)), dist);
return 0;
}
else
{
back_time = time + 0.5;
back_flag = true;
}
}
if(back_time > time) // go sideways 1/2 sec
{
walkmove((self.current_yaw-(90*0.0174532925199433)), dist);
return 0;
}
back_up = false;
}
} ]
// 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 > -20 and delta < 20) // within 20 degrees is close enough
{
return true;
}
return false;
} ]
// check if ready to do actual attacking
CheckAnyAttack[ ()
{
if(enemy_range<MELEERANGE) // inside melee range
{
attack_state = AS_MELEE; // do a melee attack
return true;
}
return false;
} ]
}