Has anybody figured out that pawn multiAtttributeCheck thing

Topics regarding Scripting with Reality Factory
User avatar
fps
Posts: 504
Joined: Mon Sep 26, 2005 9:54 pm
Location: in a magical land devoid of hope, happiness, and sanity.

Has anybody figured out that pawn multiAtttributeCheck thing

Post by fps »

Has anybody figured out how to have a pawn charecter check for multiple damage attributes at the same time yet.
I know the new version of the engine can do it but i can't get it to work.
I want to have bullets that do different damages-that trigger different pain orders in the pawn. and some of them use the hit point "last bone hit" system.
Can anybody help me?

Thankyou,
User avatar
AndyCR
Posts: 1449
Joined: Wed Jul 06, 2005 5:08 pm
Location: Colorado, USA
Contact:

Post by AndyCR »

not sure on the other part, havent tried, but have you set bonelevel to true in the ini definition for the projectile you are hitting the pawn with before you try to check what bone was hit?
User avatar
Juutis
Posts: 1511
Joined: Thu Jan 12, 2006 12:46 pm
Location: Finland

Post by Juutis »

I haven't been able to get those multiple attributes to work either.

1 question: Does saving game save the pawn attributes too or only the player attributes? I could get rid of many player attributes involved in saving the pawns' states if it does...
Pain is only psychological.
User avatar
fps
Posts: 504
Joined: Mon Sep 26, 2005 9:54 pm
Location: in a magical land devoid of hope, happiness, and sanity.

Post by fps »

I did set the bone level collision thing for my pawns but don't understand were in the script to put the lastbonehit order.

as for Juutis, i am pretty sure you can use a save pawn sate order to save the...pawns....state.. but i dont think that pawn attributes are saved.


Thanks,
fps


PS. still trying to get the pawn multiattribute thing working and need help if anybody has gotten it to work yet.
User avatar
Juutis
Posts: 1511
Joined: Thu Jan 12, 2006 12:46 pm
Location: Finland

Post by Juutis »

An example of the GetLastBoneHit() -order that checks if the pawn has just got a headshot (in other words, if it has been hit to bone called "bone_head"):
monster_missile_pain_start[ ()
{
Animate(PAIN); // play pain animation
SetHoldAtEnd(true); // set to stop at end
self.ThinkTime = 0.1;
check_headshot();
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;
}
}

check_headshot[ ()
{
self.ThinkTime = 0.1;

if(GetLastBoneHit()="bone_head") //check if "bone_head" was hit
{
HighLevel("Death"); //kill pawn when headshot occurs
}
} ]
In order for this to work you have to add the "check_headshot();" line to every pain order of your pawn so it checks always when it is hurt.

By changing the bone and the effect (in this case the pawn dies) you can have nice behaviour to your pawns. For example if you shoot him in the feet he falls or when you shoot him in the arm he drops his weapon etc etc...

I hope I made it clear enough... :roll:
Pain is only psychological.
User avatar
fps
Posts: 504
Joined: Mon Sep 26, 2005 9:54 pm
Location: in a magical land devoid of hope, happiness, and sanity.

Post by fps »

wow, thank you. :)
will this work basicly as it is now (with some simple edits), or is it a psudo script?
Either way its very help full and i cant wait to try it out.

Thanks again,
Fps
User avatar
Juutis
Posts: 1511
Joined: Thu Jan 12, 2006 12:46 pm
Location: Finland

Post by Juutis »

Well... that piece of script is just an example of how to use that, if you are good at scripting you can use my script as a guideline and modify your own script to suit your needs.

If, however, you are inexperienced in scripting I could try to give a detailed explanation of how to get that thing to work.
Pain is only psychological.
User avatar
fps
Posts: 504
Joined: Mon Sep 26, 2005 9:54 pm
Location: in a magical land devoid of hope, happiness, and sanity.

Post by fps »

I have done scripting before but i am not very good at it. a more descriptive explanation of how to get the bone hit detection to work would be gratly appreciated.

I just thought of somthing else also, would i be able to attach a small pawn to all of my enemy pawns so that the smaller pawns detect's a different damage type then health (flame thrower damage), and sends a command back to the enemy pawn it is attached to that tells it to go to a specific order in the enemy pawns script (flame thrower burned die)?

Thanks,
fps
User avatar
Juutis
Posts: 1511
Joined: Thu Jan 12, 2006 12:46 pm
Location: Finland

Post by Juutis »

Ok... I'll give it a shot. :)

So, the basic idea is to make the pawn check if it has been hit to certain bone(s) every time it loses health. This can be accomplished using the highlevel command AddPainOrder() and the read-only variable self.in_pain when executing lowlevel orders.

I think the lowlevel check is easier so I will start with it...

Every time the pawn loses health the variable self.in_pain changes its value to true. So basically pawn has to check if self.in_pain is true ALWAYS when executing lowlevel orders. If you are using the generic scripts this should be already done, but since the PAINPERCENT thing may prevent the execution of the pain order, we need to make some changes to it. Something like this should be found in almost every lowlevel order:
if((self.in_pain = true) and (random(1,100)<PAINPERCENT))
{
self.think = "monster_lost_pain_start"; // in pain
return 0;
}
If these are changed to:
if(self.in_pain = true)
{
check_headshot(); //the order from that other post of mine

if(random(1,100)<PAINPERCENT)
{
self.think = "monster_lost_pain_start"; // in pain
return 0;
}
}
Now the "check_headshot" order(which can be found in my second post to this topic) is executed always when the pawn is in lowlevel and takes damage but pain animation order only certain % of times. Note that when self.in_pain is called it automatically goes back to false.

If you are not using the generic scripts, check them and try to understand what I mean... :D

And now to highlevel... they require a bit more work since the GetLastBoneHit() is lowlevel. I'll give just a quick example because I have to go soon. :wink: (Again if you're not familiar with the generic scripts, I recommend you to check them)

So, here's a piece of script for the Idle order in the generic scripts. In fact the actual Idle order doesn't need any changes but again the pain orders do... every time the pawn is damaged the order set by AddPainOrder() is executed. So adding a couple of orders like these
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
} ]
and changing all AddPainOrder commands, that have "IdlePain" in the Order field, to "AddPainOrder("IdleCheckHeadshot1",100)", should take care of it.
You'll have to do different orders for all highlevel orders (Idle, Alert etc...).

Ok, the end probably is a bit unclear but I REALLY gotta go. :(

I hope this helps... I'm not good in explaining these things :)

Oh, and by the way, those scripts may have some errors and stuff because i just made them up.
Pain is only psychological.
User avatar
fps
Posts: 504
Joined: Mon Sep 26, 2005 9:54 pm
Location: in a magical land devoid of hope, happiness, and sanity.

Post by fps »

I am using a modified generic script, the latest missile meele script to be precise, although i cant figure out how to get the meele part working.

Lets start over with the generic missile script and add from there.

this is going to be kind of long.


{

// 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 [TurnL] // turn left animation
TURNR [TurnR] // turn right animation

PAIN [Pain1] // pain animations
PAIN1 [Pain2]
PAIN2 [Pain1]
PAIN3 [Pain2]
PAINPERCENT [50] // percentage of time pain is shown

DIE [Die] // dying animations
DIE1 [Die]
DIE2 [Die]
DIE3 [Die]
DIEHOLD [15] // time corpse still appears
DIEFADE [10] // fadeout time of corpse

RUN [Run] // running animation
RUNSPEED [100] // average run speed

MISSILEATTACK [Shoot1] // missile attacking animation
MISSILERANGE [300] // max distance to start missile attack
PROJECTILE [ball] // 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("IdlePain", 100); // show pain and trigger alert
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("AlertPain", PAINPERCENT); // show pain
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");
} ]

// 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) 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));
}
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) and (random(1,100)<PAINPERCENT))
{
self.think = "monster_missile_pain_start"; // in pain
SetHoldAtEnd(false);
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;
} ]

}



I still need to add some stuff but i am out of time also, can you pont out were?

Thanks,
Fps
User avatar
Juutis
Posts: 1511
Joined: Thu Jan 12, 2006 12:46 pm
Location: Finland

Post by Juutis »

First of all, you need to change the "AddPainOrder("IdlePain", 100)" command in order "Spawn", "AlertToIdle" and "LostTarget" to "AddPainOrder("IdleCheckHeadshot1", 100)" so when the pawn is damaged while it is in the Idle order it executes the headshot check.

Secondly, that Alert order needs its own headshotcheck. So we need to add two more orders:
AlertCheckHeadshot1[ ()
{
LowLevel("AlertCheckHeadshot2");
} ]

AlertCheckHeadshot2[ ()
{
check_headshot();

if(random(1,100)<PAINPERCENT)
{
HighLevel("AlertPain"); // in pain
}

HighLevel("Alert"); //back to alert if no pain animation is to be played
} ]
...and again change that AddPainOrder command in the "IdleToAlert" order to "AddPainOrder("AlertCheckHeadshot1", 100)"

That should take care of the highlevel... and now to lowlevel.

Ok... if my eyes are not fooling me totally I could seriously claim that you don't have the "check_headshot" order there at all... which is after all the most important to have :)

So add this:
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
}
} ]
aaaaand these lines in the order "monster_missile"
if((self.in_pain = true) and (random(1,100)<PAINPERCENT))
{
self.think = "monster_missile_pain_start"; // in pain
SetHoldAtEnd(false);
return 0;
}
need to be replaced by:
if(self.in_pain = true)
{
check_headshot();

if(random(1,100)<PAINPERCENT))
{
self.think = "monster_missile_pain_start"; // in pain
return 0;
}
Same thing in order "monster_lost_target" but here the "self.think = "monster_missile_pain_start";" has to be changed to "self.think = "monster_lost_pain_start";"

While quickly viewing that script I noticed one little error: in order "monster_run" there's one "(" symbol too much in the "if((self.in_pain = true)" line. You'll probably find more of them in my pieces of script but try to correct them. :D

Again, I hope my poor advice helps you even a tiny bit. :)



By the way, about those multiple attributes (about which this whole topic is after all :P)... You can in fact add multiple attributes with the AttributeOrder -command. However, only the last attribute added acts as the "health" attribute, which can be checked by self.health. I've managed to get my pawns to check what kind of projectile hit them (flame, plasm, grenade etc...) but this involves some hardcore scripting and scripted weapons... and it still needs some fixing.
Pain is only psychological.
User avatar
fps
Posts: 504
Joined: Mon Sep 26, 2005 9:54 pm
Location: in a magical land devoid of hope, happiness, and sanity.

Post by fps »

thank you for your assistance. i will test this script as soon as i can and let you know how it is working.
:)

about that last part, would it be possible to check for an attribute that makes the pawn go right to and order if detected instead of decreasing a health attribute?

I could still use if for flash bangs and catching fire.


Thanks,
Fps
User avatar
Juutis
Posts: 1511
Joined: Thu Jan 12, 2006 12:46 pm
Location: Finland

Post by Juutis »

Basically it would be possible in lowlevel but highlevel it causes some problems. As you probably know, lowlevel orders are executed so that every line is read for example every 0.1 seconds and so it would be easy to check all the time if a certain attribute has decreased, but in highlevel where executing one command can take several seconds before moving on to the next one it can't be done. It wouldn't be so very cool if you throw a flashbang at an enemy and he reacts like after 2 seconds, right? I have found no other way to change the pawn's order instantly in highlevel than using PainOrder command (...and some others but they don't help in this matter), so I solved it by always shooting in fact two projectiles at the same time: the first damages the pawn's health attribute and the second damages some other attribute like "flame" or "plasm" and so the pawn takes damage, executes pain order and checks if some of these secondary attributes has taken damage.
Pain is only psychological.
User avatar
fps
Posts: 504
Joined: Mon Sep 26, 2005 9:54 pm
Location: in a magical land devoid of hope, happiness, and sanity.

Post by fps »

i guess i could use the scripted weapons with 2 projectiles but i am certian that there is another way. what about triggering orders when a special number of health points are taken off, for example the flashbang does .5 damage to health and if the pawn checks and the health goes down only .5 it will execute the "blinded" order.

Would that work?

thanks,
Fps
User avatar
Juutis
Posts: 1511
Joined: Thu Jan 12, 2006 12:46 pm
Location: Finland

Post by Juutis »

Good idea, I believe that would work but I think that pawn attributes are integers and they can't decrease by .5... but setting flashbang damage to 1 should work. Though if you use explosions for other weapons, with bad luck one of these explosions could deal that exactly one damage to the pawn and it gets "blinded".

A tip: A pawn that is veryveryvery near death can die if it takes just 1 damage so you might want to add +1 health every time it detects a flashbang explosion.
Pain is only psychological.
Post Reply