Has anybody figured out that pawn multiAtttributeCheck thing
- fps
- Posts: 504
- Joined: Mon Sep 26, 2005 9:54 pm
- Location: in a magical land devoid of hope, happiness, and sanity.
so you are saying that I could do this most easily if instead of damaging the pawn so it recives -1 health I should damage it so it recives +1 health.
How would I set that up?
What do I need to do to get this system working?
I am still having a bit of trouble adding the head shot stuff but I am trying to work through the bugs myself, if I get stuck I will post the script here again.
Thanks,
Fps
How would I set that up?
What do I need to do to get this system working?
I am still having a bit of trouble adding the head shot stuff but I am trying to work through the bugs myself, if I get stuck I will post the script here again.
Thanks,
Fps
Previously I was saying that your flashbangs should do 1 damage and when the pawn detects that its health attribute has decreased by 1 it would receive +1 to its health so it can't be killed with a flashbang.
Now that I think of it, if a flashbang had -1 damage, it would actually heal the pawn and the pawn could check if it has been healed. It might work... haven't tested though.
Now that I think of it, if a flashbang had -1 damage, it would actually heal the pawn and the pawn could check if it has been healed. It might work... haven't tested though.
Pain is only psychological.
- fps
- Posts: 504
- Joined: Mon Sep 26, 2005 9:54 pm
- Location: in a magical land devoid of hope, happiness, and sanity.
so, what I know so far is that the pawn can check if it has been damaged by checking if the health value has been decreased. but what I want to know is how to use a simmilar command to make the pawns script exectute a different order then "pain" when it recives a negative damage value, thus I want the script to also check of if the health value has been increased, (And better yet for later use, how do i get the pawn to check for other specific health values and act on them accordingly eg. -1=flashbanged -10=stunned).
That very last part is not as important for now but remember that i want to try to add that later on.
My question is, how?
PS. how will pawns respond to picking up medical kits now? I also wanted to add a weapon that heals later on, will i have to go by increments of 25 or somthing?
PPS.
The first scripting feature we were working on dosen't work yet and i want to fix it first.
Can you find what I am doing wrong because I can't.
Thanks,
Fps
{
// Generic Missile Ambush Monster
GROUP [Generic] // name of monster group
BOXWIDTH [40] // 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 [50] // initial amount of health
SIGHTDIST [500] // 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 [AlertG] // name of alert trigger
STAND [idle] // idle animation
TURNL [walk] // turn left animation
TURNR [walk] // turn right animation
PAIN [pain_stomach] // pain animations
PAIN1 [pain_stomach]
PAIN2 [pain_stomach]
PAIN3 [pain_head]
PAINPERCENT [50] // percentage of time pain is shown
DIE [die_spin] // dying animations
DIE1 [die_backwards]
DIE2 [die_backwards]
DIE3 [die_spin]
DIEHOLD [15] // time corpse still appears
DIEFADE [10] // fadeout time of corpse
RUN [run] // running animation
RUNSPEED [100] // average run speed
MISSILEATTACK [jab] // missile attacking animation
MISSILERANGE [300] // max distance to start missile attack
PROJECTILE [pistol_shell] // projectile name
FIREBONE [Dummy07] // projectile launch bone
OFFSETX [0] // launch offsets
OFFSETY [0]
OFFSETZ [25]
ATTACKDELAY [2] // time between shots
FIREDELAY [0.8] // delay after animation starts before projectile launch
SKILL [5] // skill level 1 to 10
LOSTTIME [15] // time to search for enemy before giving up
POINTRADIUS [20] // radius from point when considered there
// local variables - do not change
RUNFUNC [monster_run_start] // monster run to attack function
MISSILEFUNC [monster_missile_start] // monster missile function
LOSTFUNC [monster_lost_target_start] // monster lost target function
AS_NONE [0]
AS_MISSILE [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 pawn and do setup work
Spawn[ ()
{
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("IdleCheckHeadshot1", 100); // show pain and check for a headshot
AddTriggerOrder("IdleToAlert", ALERTTRIGGER, 0); // go to alert when triggered
NewOrder("Idle");
} ]
// idle in place waiting for something to happen
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();
} ]
IdleCheckHeadshot1[ ()
{
LowLevel("IdleCheckHeadshot2");
} ]
IdleCheckHeadshot2[ ()
{
check_headshot(); //again that order from my previous post
if(random(1,100)<PAINPERCENT)
{
HighLevel("IdlePain"); // in pain
}
HighLevel("Idle"); //back to idle if no pain animation is to be played
} ]
// show pain at idle then trigger to alert
IdlePain[ ()
{
switch(random(1,4)) // chose between 4 animations
{
case 1
{
PlayAnimation(PAIN, true, "");
}
case 2
{
PlayAnimation(PAIN1, true, "");
}
case 3
{
PlayAnimation(PAIN2, true, "");
}
case 4
{
PlayAnimation(PAIN3, true, "");
}
}
SetEventState(ALERTTRIGGER, true); // set trigger to go to alert
Return();
} ]
// start shifting from idle to alert
IdleToAlert[ ()
{
FindTargetOrder(ALERTSIGHTDIST, "FoundTarget", DAMAGEATTRIBUTE); // increase viewing distance
AddPainOrder("AlertCheckHeadshot1", 100); // show pain check for head shot
AddTimerOrder(1, 10, "AlertToIdle"); // go to idle after 10 secs
SetEventState(ALERTTRIGGER, false); // turn off alert trigger
NewOrder("Alert");
} ]
// look around at alert looking for enemy
Alert[ ()
{
PlayAnimation(STAND, true, "");
if(random(1,10)>3) // turn around once in awhile
{
if(random(1,10)>5)
{
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
{
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();
} ]
// show pain at alert
AlertPain[ ()
{
switch(random(1,4)) // play on of 4 animations
{
case 1
{
PlayAnimation(PAIN, true, "");
}
case 2
{
PlayAnimation(PAIN1, true, "");
}
case 3
{
PlayAnimation(PAIN2, true, "");
}
case 4
{
PlayAnimation(PAIN3, true, "");
}
}
Return();
} ]
// timed out at alert so go to idle
AlertToIdle[ ()
{
FindTargetOrder(SIGHTDIST, "FoundTarget", DAMAGEATTRIBUTE); // decrease viewing distance
AddPainOrder("IdlePain", 100); // show pain
AddTriggerOrder("IdleToAlert", ALERTTRIGGER, 0); // go to alert when triggered
NewOrder("Idle");
} ]
// found a target to attack
FoundTarget[ ()
{
DelTimerOrder(1); // get rid of alert timer
LowLevel(RUNFUNC); // attack functions are low level
} ]
// lost target while attacking so go back to idle again
LostTarget[ ()
{
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");
} ]
check_headshot[ ()
{
self.ThinkTime = 0.1;
if(GetLastBoneHit()="bone_head") //change "bone_head" to whatever your actor's "head" bone is called
{
HighLevel("Death"); //kill pawn when headshot occurs
}
} ]
// you died
Death[ ()
{
DelTimerOrder(1); // remove alert timer
AddPainOrder("IdlePain", 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
switch(random(1,4)) // chose between 4 death animations
{
case 1
{
AnimateStop(DIE, DIEHOLD, "");
}
case 2
{
AnimateStop(DIE1, DIEHOLD, "");
}
case 3
{
AnimateStop(DIE2, DIEHOLD, "");
}
case 4
{
AnimateStop(DIE, DIEHOLD, "");
}
}
FadeOut(DIEFADE, 0); // fade out corpse
Remove(true); // remove actor
} ]
// Low level attack routines
// Start of run to attack
monster_run_start[ ()
{
Animate(RUN); // play run animation
self.ThinkTime = 0; // start thinking on next frame
self.think = "monster_run"; // go to run attack routine
self.ideal_yaw = enemy_yaw; // set direction to run
self.yaw_speed = YAWSPEED; // set rotation speed
back_up = false; // initialize obsticle avoidance
attack_state = AS_NONE; // not attacking yet
} ]
// run to enemy to attack
monster_run[ ()
{
self.ThinkTime = 0.1;
if(self.health<=0)
{
HighLevel("Death"); // dead
return 0;
}
if(self.in_pain = true)
{
check_headshot();
if(random(1,100)<PAINPERCENT))
{
self.think = "monster_run_pain_start"; // in pain
return 0;
}
if(EnemyExist(DAMAGEATTRIBUTE) < 3)
{
HighLevel("LostTarget"); // enemy is gone or dead
return 0;
}
if(enemy_vis = false)
{
self.think = LOSTFUNC; // lost sight of enemy
lost_time = time + LOSTTIME;
return 0;
}
ai_run(random(RUNSPEED-2,RUNSPEED+2)); // run toward enemy
} ]
// start of pain while running
monster_run_pain_start[ ()
{
Animate(PAIN); // play pain animation
SetHoldAtEnd(true); // set to stop at animation end
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)
{
check_headshot();
if(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));
}
else
{
HighLevel("LostTarget"); // can't find at last known location
return 0;
}
} ]
// start of showing pain while searching
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 missile attack
monster_missile_start[ ()
{
Animate(MISSILEATTACK); // play missile attack animation
SetHoldAtEnd(true);
self.ThinkTime = 0;
self.think = "monster_missile";
fire_delay = time + FIREDELAY; // set firing delay
UpdateTarget(); // update target location
skill_time = time + (SKILL*0.1); // calculate next update time
attack_delay = time + ATTACKDELAY; // delay until next shot
} ]
// attack target with projectile
monster_missile[ ()
{
self.ThinkTime = 0.1;
if(self.health<=0)
{
SetHoldAtEnd(false);
HighLevel("Death"); // dead
return 0;
}
if(self.in_pain = true)
{
check_headshot();
if(random(1,100)<PAINPERCENT))
{
self.think = "monster_missile_pain_start"; // in pain
return 0;
}
exist = EnemyExist(DAMAGEATTRIBUTE);
if(exist < 2)
{
SetHoldAtEnd(false);
HighLevel("LostTarget"); // enemy is dead and gone
return 0;
}
if(exist = 2)
{
SetHoldAtEnd(false);
HighLevel("LostTarget"); // enemy is dead but body remains
return 0;
}
if(enemy_vis = false)
{
self.think = LOSTFUNC; // lost sight of enemy
lost_time = time + LOSTTIME;
SetHoldAtEnd(false);
return 0;
}
if(enemy_range>MISSILERANGE)
{
self.think = RUNFUNC; // too far away so run toward
SetHoldAtEnd(false);
return 0;
}
if(skill_time<time) // update according to skill level
{
UpdateTarget(); // update target location
skill_time = time + (SKILL*0.1);
ai_face(); // face enemy while attacking
}
if(fire_delay<time) // delay after animation starts before firing
{
FireProjectile(PROJECTILE, FIREBONE, OFFSETX, OFFSETY, OFFSETZ, DAMAGEATTRIBUTE);
fire_delay = time + 1000; // set delay well ahead so it is ignored
}
// wait until animation is done before attacking again
// hold animation at end until attack delay is over
if(self.animate_at_end = true)
{
if(attack_delay < time)
{
self.think = "monster_missile_start"; // go back to missile attack
SetHoldAtEnd(false);
self.ThinkTime = 0.1;
}
}
} ]
// start of showing pain
monster_missile_pain_start[ ()
{
Animate(PAIN); // play pain animation
SetHoldAtEnd(true); // set to stop at end
self.ThinkTime = 0.1;
self.think = "monster_missile_pain";
} ]
// wait until animation is done
monster_missile_pain[ ()
{
self.ThinkTime = 0.1;
if(self.animate_at_end = true) // animation is done
{
self.think = "monster_missile_start"; // go back to missile attack
SetHoldAtEnd(false);
self.ThinkTime = 0;
}
} ]
// basic AI routines
// run toward enemy and see if you are ready to attack
ai_run[ (dist)
{
if (attack_state = AS_MISSILE) // do missile attack
{
ai_run_missile();
return 0;
}
if (CheckAnyAttack()) // check if you can start the actual attack
{
return 0;
}
walk_movetogoal(dist); // else move toward the enemy
} ]
// missile attack setup
ai_run_missile[ ()
{
ai_face();
if(FacingIdeal()) // got close enough
{
self.think = MISSILEFUNC; // start missile attack
attack_state = AS_STRAIGHT;
UpdateTarget();
skill_time = time + (SKILL*0.1);
}
} ]
// face enemy
ai_face[ ()
{
self.ideal_yaw = enemy_yaw;
ChangeYaw(); // rotate to face enemy
} ]
// use walkmove to naviagte 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
{
if(random(1,10)<3) // backup and move sideways
{
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(attack_delay>time)
{
return false;
}
if(enemy_range<MISSILERANGE) // inside missile range
{
attack_state = AS_MISSILE; // do a missile attack
return true;
}
return false;
} ]
}
That very last part is not as important for now but remember that i want to try to add that later on.
My question is, how?
PS. how will pawns respond to picking up medical kits now? I also wanted to add a weapon that heals later on, will i have to go by increments of 25 or somthing?
PPS.
The first scripting feature we were working on dosen't work yet and i want to fix it first.
Can you find what I am doing wrong because I can't.
Thanks,
Fps
{
// Generic Missile Ambush Monster
GROUP [Generic] // name of monster group
BOXWIDTH [40] // 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 [50] // initial amount of health
SIGHTDIST [500] // 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 [AlertG] // name of alert trigger
STAND [idle] // idle animation
TURNL [walk] // turn left animation
TURNR [walk] // turn right animation
PAIN [pain_stomach] // pain animations
PAIN1 [pain_stomach]
PAIN2 [pain_stomach]
PAIN3 [pain_head]
PAINPERCENT [50] // percentage of time pain is shown
DIE [die_spin] // dying animations
DIE1 [die_backwards]
DIE2 [die_backwards]
DIE3 [die_spin]
DIEHOLD [15] // time corpse still appears
DIEFADE [10] // fadeout time of corpse
RUN [run] // running animation
RUNSPEED [100] // average run speed
MISSILEATTACK [jab] // missile attacking animation
MISSILERANGE [300] // max distance to start missile attack
PROJECTILE [pistol_shell] // projectile name
FIREBONE [Dummy07] // projectile launch bone
OFFSETX [0] // launch offsets
OFFSETY [0]
OFFSETZ [25]
ATTACKDELAY [2] // time between shots
FIREDELAY [0.8] // delay after animation starts before projectile launch
SKILL [5] // skill level 1 to 10
LOSTTIME [15] // time to search for enemy before giving up
POINTRADIUS [20] // radius from point when considered there
// local variables - do not change
RUNFUNC [monster_run_start] // monster run to attack function
MISSILEFUNC [monster_missile_start] // monster missile function
LOSTFUNC [monster_lost_target_start] // monster lost target function
AS_NONE [0]
AS_MISSILE [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 pawn and do setup work
Spawn[ ()
{
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("IdleCheckHeadshot1", 100); // show pain and check for a headshot
AddTriggerOrder("IdleToAlert", ALERTTRIGGER, 0); // go to alert when triggered
NewOrder("Idle");
} ]
// idle in place waiting for something to happen
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();
} ]
IdleCheckHeadshot1[ ()
{
LowLevel("IdleCheckHeadshot2");
} ]
IdleCheckHeadshot2[ ()
{
check_headshot(); //again that order from my previous post
if(random(1,100)<PAINPERCENT)
{
HighLevel("IdlePain"); // in pain
}
HighLevel("Idle"); //back to idle if no pain animation is to be played
} ]
// show pain at idle then trigger to alert
IdlePain[ ()
{
switch(random(1,4)) // chose between 4 animations
{
case 1
{
PlayAnimation(PAIN, true, "");
}
case 2
{
PlayAnimation(PAIN1, true, "");
}
case 3
{
PlayAnimation(PAIN2, true, "");
}
case 4
{
PlayAnimation(PAIN3, true, "");
}
}
SetEventState(ALERTTRIGGER, true); // set trigger to go to alert
Return();
} ]
// start shifting from idle to alert
IdleToAlert[ ()
{
FindTargetOrder(ALERTSIGHTDIST, "FoundTarget", DAMAGEATTRIBUTE); // increase viewing distance
AddPainOrder("AlertCheckHeadshot1", 100); // show pain check for head shot
AddTimerOrder(1, 10, "AlertToIdle"); // go to idle after 10 secs
SetEventState(ALERTTRIGGER, false); // turn off alert trigger
NewOrder("Alert");
} ]
// look around at alert looking for enemy
Alert[ ()
{
PlayAnimation(STAND, true, "");
if(random(1,10)>3) // turn around once in awhile
{
if(random(1,10)>5)
{
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
{
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();
} ]
// show pain at alert
AlertPain[ ()
{
switch(random(1,4)) // play on of 4 animations
{
case 1
{
PlayAnimation(PAIN, true, "");
}
case 2
{
PlayAnimation(PAIN1, true, "");
}
case 3
{
PlayAnimation(PAIN2, true, "");
}
case 4
{
PlayAnimation(PAIN3, true, "");
}
}
Return();
} ]
// timed out at alert so go to idle
AlertToIdle[ ()
{
FindTargetOrder(SIGHTDIST, "FoundTarget", DAMAGEATTRIBUTE); // decrease viewing distance
AddPainOrder("IdlePain", 100); // show pain
AddTriggerOrder("IdleToAlert", ALERTTRIGGER, 0); // go to alert when triggered
NewOrder("Idle");
} ]
// found a target to attack
FoundTarget[ ()
{
DelTimerOrder(1); // get rid of alert timer
LowLevel(RUNFUNC); // attack functions are low level
} ]
// lost target while attacking so go back to idle again
LostTarget[ ()
{
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");
} ]
check_headshot[ ()
{
self.ThinkTime = 0.1;
if(GetLastBoneHit()="bone_head") //change "bone_head" to whatever your actor's "head" bone is called
{
HighLevel("Death"); //kill pawn when headshot occurs
}
} ]
// you died
Death[ ()
{
DelTimerOrder(1); // remove alert timer
AddPainOrder("IdlePain", 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
switch(random(1,4)) // chose between 4 death animations
{
case 1
{
AnimateStop(DIE, DIEHOLD, "");
}
case 2
{
AnimateStop(DIE1, DIEHOLD, "");
}
case 3
{
AnimateStop(DIE2, DIEHOLD, "");
}
case 4
{
AnimateStop(DIE, DIEHOLD, "");
}
}
FadeOut(DIEFADE, 0); // fade out corpse
Remove(true); // remove actor
} ]
// Low level attack routines
// Start of run to attack
monster_run_start[ ()
{
Animate(RUN); // play run animation
self.ThinkTime = 0; // start thinking on next frame
self.think = "monster_run"; // go to run attack routine
self.ideal_yaw = enemy_yaw; // set direction to run
self.yaw_speed = YAWSPEED; // set rotation speed
back_up = false; // initialize obsticle avoidance
attack_state = AS_NONE; // not attacking yet
} ]
// run to enemy to attack
monster_run[ ()
{
self.ThinkTime = 0.1;
if(self.health<=0)
{
HighLevel("Death"); // dead
return 0;
}
if(self.in_pain = true)
{
check_headshot();
if(random(1,100)<PAINPERCENT))
{
self.think = "monster_run_pain_start"; // in pain
return 0;
}
if(EnemyExist(DAMAGEATTRIBUTE) < 3)
{
HighLevel("LostTarget"); // enemy is gone or dead
return 0;
}
if(enemy_vis = false)
{
self.think = LOSTFUNC; // lost sight of enemy
lost_time = time + LOSTTIME;
return 0;
}
ai_run(random(RUNSPEED-2,RUNSPEED+2)); // run toward enemy
} ]
// start of pain while running
monster_run_pain_start[ ()
{
Animate(PAIN); // play pain animation
SetHoldAtEnd(true); // set to stop at animation end
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)
{
check_headshot();
if(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));
}
else
{
HighLevel("LostTarget"); // can't find at last known location
return 0;
}
} ]
// start of showing pain while searching
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 missile attack
monster_missile_start[ ()
{
Animate(MISSILEATTACK); // play missile attack animation
SetHoldAtEnd(true);
self.ThinkTime = 0;
self.think = "monster_missile";
fire_delay = time + FIREDELAY; // set firing delay
UpdateTarget(); // update target location
skill_time = time + (SKILL*0.1); // calculate next update time
attack_delay = time + ATTACKDELAY; // delay until next shot
} ]
// attack target with projectile
monster_missile[ ()
{
self.ThinkTime = 0.1;
if(self.health<=0)
{
SetHoldAtEnd(false);
HighLevel("Death"); // dead
return 0;
}
if(self.in_pain = true)
{
check_headshot();
if(random(1,100)<PAINPERCENT))
{
self.think = "monster_missile_pain_start"; // in pain
return 0;
}
exist = EnemyExist(DAMAGEATTRIBUTE);
if(exist < 2)
{
SetHoldAtEnd(false);
HighLevel("LostTarget"); // enemy is dead and gone
return 0;
}
if(exist = 2)
{
SetHoldAtEnd(false);
HighLevel("LostTarget"); // enemy is dead but body remains
return 0;
}
if(enemy_vis = false)
{
self.think = LOSTFUNC; // lost sight of enemy
lost_time = time + LOSTTIME;
SetHoldAtEnd(false);
return 0;
}
if(enemy_range>MISSILERANGE)
{
self.think = RUNFUNC; // too far away so run toward
SetHoldAtEnd(false);
return 0;
}
if(skill_time<time) // update according to skill level
{
UpdateTarget(); // update target location
skill_time = time + (SKILL*0.1);
ai_face(); // face enemy while attacking
}
if(fire_delay<time) // delay after animation starts before firing
{
FireProjectile(PROJECTILE, FIREBONE, OFFSETX, OFFSETY, OFFSETZ, DAMAGEATTRIBUTE);
fire_delay = time + 1000; // set delay well ahead so it is ignored
}
// wait until animation is done before attacking again
// hold animation at end until attack delay is over
if(self.animate_at_end = true)
{
if(attack_delay < time)
{
self.think = "monster_missile_start"; // go back to missile attack
SetHoldAtEnd(false);
self.ThinkTime = 0.1;
}
}
} ]
// start of showing pain
monster_missile_pain_start[ ()
{
Animate(PAIN); // play pain animation
SetHoldAtEnd(true); // set to stop at end
self.ThinkTime = 0.1;
self.think = "monster_missile_pain";
} ]
// wait until animation is done
monster_missile_pain[ ()
{
self.ThinkTime = 0.1;
if(self.animate_at_end = true) // animation is done
{
self.think = "monster_missile_start"; // go back to missile attack
SetHoldAtEnd(false);
self.ThinkTime = 0;
}
} ]
// basic AI routines
// run toward enemy and see if you are ready to attack
ai_run[ (dist)
{
if (attack_state = AS_MISSILE) // do missile attack
{
ai_run_missile();
return 0;
}
if (CheckAnyAttack()) // check if you can start the actual attack
{
return 0;
}
walk_movetogoal(dist); // else move toward the enemy
} ]
// missile attack setup
ai_run_missile[ ()
{
ai_face();
if(FacingIdeal()) // got close enough
{
self.think = MISSILEFUNC; // start missile attack
attack_state = AS_STRAIGHT;
UpdateTarget();
skill_time = time + (SKILL*0.1);
}
} ]
// face enemy
ai_face[ ()
{
self.ideal_yaw = enemy_yaw;
ChangeYaw(); // rotate to face enemy
} ]
// use walkmove to naviagte 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
{
if(random(1,10)<3) // backup and move sideways
{
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(attack_delay>time)
{
return false;
}
if(enemy_range<MISSILERANGE) // inside missile range
{
attack_state = AS_MISSILE; // do a missile attack
return true;
}
return false;
} ]
}
Negative damages can be checked in the pain orders just like that headshot checking. I made a couple of tests and found out that pawns in fact execute their pain orders even if the damage that they receive is negative or even 0. So basically you need to add a variable that contains the last known hitpoints of the pawn and update it every time the health changes. Just before updating the variable you could check if current health = last known health + 1... this way it is possible to say exactly how much damage the pawn has taken (or how much it has healed).
By adding line "Console(true);" to the Spawn order you can get rid of all the little scripting errors.
Here you need to change the "bone_head" to whatever your actor's "head" bone is called.check_headshot[ ()
{
self.ThinkTime = 0.1;
if(GetLastBoneHit()="bone_head") //change "bone_head" to whatever your actor's "head" bone is called
{
HighLevel("Death"); //kill pawn when headshot occurs
}
} ]
By adding line "Console(true);" to the Spawn order you can get rid of all the little scripting errors.
Pain is only psychological.
Oh right... I'm not sure about this but I think that line "HighLevel("Idle");" in the order IdleCheckHeadshot2 isn't needed. I'm not very good at this error finding stuff and usually my way of dealing with them is trial and error.
Try removing some lines that could cause the problem and test it and this way try to locate the problem.
Maybe someone more experienced could help us.
Try removing some lines that could cause the problem and test it and this way try to locate the problem.
Maybe someone more experienced could help us.
Pain is only psychological.
- fps
- Posts: 504
- Joined: Mon Sep 26, 2005 9:54 pm
- Location: in a magical land devoid of hope, happiness, and sanity.
I changed it along with some other things but I think that I just made it worse. There is also a problem with the Running to target order and I am starting to get a little fusturated with all of the wierd little glitches ive been coming across.
sometimes when I shoot the pawn in the head it twitches and doesent play the pain animation even though i set PAINPERCENT on 100.
Also I am using fredricos physics demo level to test these pawns and for some reason the bullets never hit exactly were i am aiming but rather off to one side. should i try a different test level?
Oh well.
can you think of any thing else, what about the Pain checking orders in the alert and other orders.
Thanks,
Fps
sometimes when I shoot the pawn in the head it twitches and doesent play the pain animation even though i set PAINPERCENT on 100.
Also I am using fredricos physics demo level to test these pawns and for some reason the bullets never hit exactly were i am aiming but rather off to one side. should i try a different test level?
Oh well.
can you think of any thing else, what about the Pain checking orders in the alert and other orders.
Thanks,
Fps
Hmm... that physics thing might cause some problems... I'm not sure though, maybe these physics-experts could give us more info.
Anyway I fixed that script of yours and added some stuff. The biggest new thing is that now the player can loot the pawn after killing it. Note that LOOTWEAPON and LOOTAMMO have to be player attributes and PAWNWEAPON is a weapon defined in the pawn.ini file.
I hope it works with physics as well...
Anyway I fixed that script of yours and added some stuff. The biggest new thing is that now the player can loot the pawn after killing it. Note that LOOTWEAPON and LOOTAMMO have to be player attributes and PAWNWEAPON is a weapon defined in the pawn.ini file.
I hope it works with physics as well...
- Attachments
-
- enemy.zip
- (4.41 KiB) Downloaded 74 times
Pain is only psychological.
Jumping back and forth between High and Low level orders will throw "errors" left and right. I've gotten errors returned on commands that were totally bug free and still worked fine even though the console returned an error. For example, a frequent one I see is 'Method IsKeyDown not found', which of course is a perfectly valid command.I tested the level like you said and the console command revealed a message that said somthing like;
Method headshotcheck2 : 7 highlevel not found
On a side note, jumping back and forth between Low and High Level is a major frame rate killer. I recommend running (and staying in) Low level as much as possible. My scripts Spawn in High Level then go to LowLevel until the pawn dies at which point I return to HighLevel.
There is very little that you might do in HighLevel that couldn't just as easily (and more efficiently) be done in LowLevel.
if there was a way to effectively acquire targets in lowlevel.
If you know the name of the entity you want to target, you can use...
SetTarget(EntiyName);
If you don't know the name, TraceToActor will return it. You can do several TraceToActor commands from different offsets to widen the range that is checked.
WhereIsEntity(EntityName);
LookAtPawn(EntityName,OffsetY);
and
IsEntityVsible(EntityName);
are also useful.
Yeah.. it would be easy if I had only the player and enemy pawns in my game but since I have ally pawns too it would become pretty complicated.
Now if I understood this it would be pretty much like this:
1) Use TraceToActor to scan for nearby pawns (that are in field of view)
2) If there is a pawn nearby, check if it is enemy somehow.
3) Target and start attacking the pawn if it is enemy.
That wouldn't be too hard I guess... but one more thing:
Let's assume pawn A stands still and pawn B stands 10 meters away from it. A is B's enemy and vice versa. Now... if pawn C, that is A's ally, is standing exactly between them, it prevents pawn A from "seeing" pawn B.
Thanks for the tips anyway. Maybe one day I will go deeper into this matter but not now.
Now if I understood this it would be pretty much like this:
1) Use TraceToActor to scan for nearby pawns (that are in field of view)
2) If there is a pawn nearby, check if it is enemy somehow.
3) Target and start attacking the pawn if it is enemy.
That wouldn't be too hard I guess... but one more thing:
Let's assume pawn A stands still and pawn B stands 10 meters away from it. A is B's enemy and vice versa. Now... if pawn C, that is A's ally, is standing exactly between them, it prevents pawn A from "seeing" pawn B.
Thanks for the tips anyway. Maybe one day I will go deeper into this matter but not now.
Pain is only psychological.
- fps
- Posts: 504
- Joined: Mon Sep 26, 2005 9:54 pm
- Location: in a magical land devoid of hope, happiness, and sanity.
Nevermind I works, my mistake.
Thanks,
Now does anybody have any Ideas about the multi attribute weapons damage -1 value checking mess for flashbangs ect...
PS.
For foe/allied pawns.
Couldent you give the pawns an attribute to check for such as "target" instead of "health" to fix the problem?
Allied pawns would have a "target" value of 1. enemy pawns would have health checked for and their group checked like regular pawns but they look for the "target" attribute instead of health.
Thanks,
Fps
Thanks,
Now does anybody have any Ideas about the multi attribute weapons damage -1 value checking mess for flashbangs ect...
PS.
For foe/allied pawns.
Couldent you give the pawns an attribute to check for such as "target" instead of "health" to fix the problem?
Allied pawns would have a "target" value of 1. enemy pawns would have health checked for and their group checked like regular pawns but they look for the "target" attribute instead of health.
Thanks,
Fps