Perfect AI.s quesions

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.

Perfect AI.s quesions

Post by fps »

I have 2 questions about the latest prefect AI script.

Can the perfect Ai script be made to attack other pawns instead of the player?
and has an update of any kind been created?

I have been trying to use it in my game for allies and enemys and trying to add the headshot and loot stuff from earlier but it keeps crashing my game at the start so i am trying to redo it with the premodified (before i messed it up) version.

Oh and i cant seem to get the missile meele thing to work yet but that is probably my fault.

Thanks,
fps
MakerOfGames
Posts: 866
Joined: Fri Jul 08, 2005 4:27 am
Location: PA, USA

Post by MakerOfGames »

Do not get your hopes up but technically the script could be modded to have pawns attack eachother based on if they are a good or bad guy. To do this though, you will probably need to overhaul some/many sections of the script.

Now this might work without overhaul:
Assigning each pawn a team with scripting should be able to get it to have pawns attack pawns, unless it is specifily the player that it is programmed to attack. This may work because the script might have the bot it is controlling know its team members and attack the other team which would be only one person, the player, by default.

I don't know of any update for this script. I, however, will be scripting my own enemies etc so I havent looked at this script indepth, nor have I followed any information on it.
Think outside the box.
To go on an adventure, one must discard the comforts and safety of the known and trusted.
User avatar
steven8
Posts: 1487
Joined: Wed Aug 24, 2005 9:08 am
Location: Barberton, OH

Post by steven8 »

I have never been a scripter, but in the online help, I found this is pawn scripting:

SetGroup(char *Name );

Set the name of the group the Pawn belongs to Name.

HostilePlayer(bool State );

If State is true then the Pawn will include the player in its list of potential targets.

HostileSame(bool State );

If State is true then the Pawn will include all Pawns with the same group name as it in its list of potential targets.

HostileDifferent(bool State );

If State is true then the Pawn will include all Pawns with a different group name than it in its list of potential targets.

TargetGroup(char *Name );

If the Pawn is set to be hostile to Pawns of a different group this will cause the Pawn to include in its list of potential targets only those Pawns that are in the group Name.

It shows me that you can have pawns attack other pawns and groups of pawns. You just have to indentify them as being in a particular group. I downloaded the perfect ai script and tutorial, but haven't had a chance to look at it yet. I am at work now and wil ltake a look later.

Does this help? If you need more, just go the the online help.
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 »

O BOY!!!
An overhaul, thats great.
... oh wait, I almost forgot. I suck at scripting any thing complicated.
I accually tryed rewriting the target finding and attack commands before I posted this using the generic shooter pawn as a reference, but I crashes the game and freezes my computer to a black screen before it gets a chance to tell me whats wrong. It dosent even tell me whats wrong in the RF.log or what ever its called.
Could you help me?
If we could add the code I need to the PerfectAI.s to get it to attack the target types I define, then I would be able to move on past the basics of the script and add the headshot and loot stuff.
I keep thinking of these cool things I want to add to my scripts but I cant get the basics to work first so I get stuck.

Will you help me fix the script?

I think that this is the un screwed up version of the script.

Try it out and tell me what you think. you may need to change some sound definitions and stuff.

If you look at the run function stuff you can see that the script is specifically made to target the player and only the player when alerted, I just don't know how to make the pawn target anything else or the player depending on what it sees.



SCALE [1] // scale of actor
GROUP [solider] // name of monster group
BOXWIDTH [25] // 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 [200] // max distance monster can see at idle
ALERTSIGHTDIST [500] // max distance monster can see when alert
FOV [90] // field of view in degrees
ATTACKFOV [180] // attacking field of view
YAWSPEED [360] // speed of rotation in deg/sec
DAMAGEATTRIBUTE [health] // attribute damaged by attack
ALERTTRIGGER [AlertG] // name of alert trigger

// define the type of monster this will be

ATTACKTYPE [missile] // type of attack - melee or missile
PATROL [true] // false - ambush or true - patrol
PC [0] // make any visible point the next point

// idling and turning

STAND [Virgil_GUN_Idle] // idle animation
TURNL [Walk] // turn left animation
TURNR [Walk] // turn right animation

// when in pain
TARGETSOUND [pawn/Hey1.wav]
PAIN [pain_chest] // pain animations
PAIN1 [pain_leg_left]
PAIN2 [rollright]
PAIN3 [pain_chest]
PAINPERCENT [50] // percentage of time pain is shown
PAINSOUND [pawn/pain1.wav] // sounds played when in pain
PAINSOUND1 [pawn/pain2.wav]
PAINSOUND2 [pawn/pain3.wav]
PAINSOUND3 [pawn/pain4.wav]
HEADASHOT [pawn/headshot.wav]

// when dying

DIE [die21] // dying animations
DIE1 [die7]
DIE2 [die8]
DIE3 [die27]
DIEHEADSHOT [die5]
DIEHOLD [false] // time corpse still appears
DIEFADE [false] // fadeout time of corpse
DIESOUND [pawn/Die1.wav] // sounds played when dying
DIESOUND1 [pawn/Die2.wav]
DIESOUND2 [pawn/Die3.wav]
DIESOUND3 [pawn/Die4.wav]

// when running to attack

RUN [run] // running animation
RUNSPEED [200] // average run speed
RUNSOUNDDELAY [0] // delay between making sounds when running to attack
RUNSOUND [pawn/punch2.wav] // sound played while running to attack

// when walking while patroling

WALK [walk] // walking animation
WALKSPEED [100] // average walking speed
WALKSOUND [pawn/punch1.wav] // walking sound

// the projectile attack mode

MISSILEATTACK [shootrifle1] // missile attacking animations
MISSILEATTACK1 [crouchshootrifle2]
MISSILEATTACK2 [halfcrouchaimrifle1]
SHOOTUP [shootrifle2]
SHOOTDOWN [Shoot1]
FIREDELAY [0.3] // delay after animation starts before projectile launch
FIREDELAY1 [0.3]
FIREDELAY2 [0.3]
MISSILESOUND [weapon/m4a1/m4a1burst.wav] // sounds played when shooting
MISSILESOUND1 [weapon/m4a1/m4a1burst.wav]
MISSILESOUND2 [weapon/m4a1/m4a1burst.wav]
MISSILERANGE [50] // max distance to start missile attack
PROJECTILE [] // projectile name
FIREBONE [Bip01 R Finger1] // projectile launch bone
OFFSETX [0] // launch offsets
OFFSETY [0]
OFFSETZ [0]
ATTACKDELAY [0.3] // time between shots
SKILL [5] // skill level 1 to 10

// the melee attack mode

MELEEATTACK [punchleft] // melee attacking animations
MELEEATTACK1 [punchjumpkick]
MELEEATTACK2 [punchspinkick]
MELEESOUND [pawn/punchhit1.wav] // sounds played when melee attacking
MELEESOUND1 [pawn/kickhit1.wav]
MELEESOUND2 [pawn/kickhit2.wav]
MELEERANGE [55] // max distance to start melee attack
MAXMELEERANGE [20] // max distance for melee mode
MINMELEEDAMAGE [10] // minimum amount of damage per melee attack
MAXMELEEDAMAGE [10] // maximum amount of damage per melee attack
MELEEDELAY [2] // number of seconds between melee damages
MELEEDAMAGESOUND [pawn/punchwall2.wav] // sound played when damage is done

// search for enemy

LOSTTIME [10] // time to search for enemy before giving up
POINTRADIUS [20] // radius from point when considered there

// obstacle avoidance forces for jumping

FORCEUP [15] // obstacle avoidance jump speed
FORCEFORWARD [20] // obstacle avoidance forward speed
FORCESIDE [20] // obstacle avoidance sideways speed

// local variables - do not change


RUNFUNC [monster_run_start] // monster run to attack function
MISSILEFUNC [monster_missile_start] // monster missile function
MELEEFUNC [monster_melee_start] // monster melee function
LOSTFUNC [monster_lost_target_start] // monster lost target function

AS_NONE [0]
AS_MELEE [1]
AS_MISSILE [2]
AS_STRAIGHT [3]
attack_delay [0]
melee_time [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]
run_sound_time [0]
missile_sound [NULL]

// spawn pawn and do setup work

Spawn[ ()
{
Console(true);
Scale(SCALE); // scale the actor
if(BOXWIDTH > 0)
{
BoxWidth(BOXWIDTH*SCALE); // 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(360,"Bip01 Head"); // 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
AvoidOrder("Avoidance"); // avoid obstacles
AddTriggerOrder("IdleToAlert", ALERTTRIGGER, 0); // go to alert when triggered
RotateToPoint(STAND, YAWSPEED, false, ""); // go to point if any specified
MoveToPoint(WALK, WALKSPEED*SCALE, WALKSOUND);
if(PATROL = true)
{
NewOrder("Patrol");
}
else
{
NewOrder("Idle");
} ]

// avoid objects when doing a MoveToPoint

Avoidance[ ()
{
if(random(1,10)<3) // backup and move sideways sometimes
{
MoveBackward(WALK, WALKSPEED*SCALE, (WALKSPEED/2)*SCALE, "");
MoveRight(WALK, WALKSPEED*SCALE, (WALKSPEED/2)*SCALE, "");
}
else
{
Jump(RUN, FORCEUP*SCALE, true, "");
if(random(1,10)<6)
{
Move("", FORCEFORWARD*SCALE, (FORCEFORWARD/2)*SCALE, 0, 90, 0, "");
}
else
{
Move("", FORCEFORWARD*SCALE, (FORCEFORWARD/2)*SCALE, 0, -90, 0, "");
}
}
Return();
} ]

// idle in place waiting for something to happen

Idle[ ()
{
PlayAnimation(STAND, true, "");
}
else
{
NewOrder("FoundTarget");
}
} ]

// walk the beat from point to point

Patrol[ ()
{
NextPoint();
RotateMoveToPoint(WALK, YAWSPEED, WALKSPEED*SCALE, false, WALKSOUND);
MoveToPoint(WALK, WALKSPEED*SCALE, WALKSOUND);
RestartOrder();
} ]

// show pain at idle then trigger to alert

IdlePain[ ()
{

NewOrder("FoundTarget");
} ]

// start shifting from idle to alert

IdleToAlert[ ()
{
NewOrder("FoundTarget");
} ]

// look around at alert looking for enemy

Alert[ ()
{
NewOrder("FoundTarget");
} ]

// show pain at alert

AlertPain[ ()
{
NewOrder("FoundTarget");
} ]

// timed out at alert

AlertToIdle[ ()
{
if(PATROL = true)
{
NewOrder("FoundTarget");
}
else
{
NewOrder("FoundTarget");
}
} ]

// found a target to attack

FoundTarget[ ()
{
SetFOV(360,"Bip01 Head"); // set field of view
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
FindPointOrder("Patrol"); // do point stuff
NextPoint(); // recent addition - sets a clear path - avoides bouncing here
if(PATROL = true)
{
NewOrder("Patrol");
}
else
{
NewOrder("Idle");
}
} ]

// you died

Death[ ()
{
//SetNoCollision(); // remove bounding box so there are no collisions with corpse
Remove = false; // remove actor
RemoveWeapon = false; // remove the current weapon actor.
return 0;
}]

// Low level attack routines

// Start of run to attack

monster_run_start[ ()
{
TargetPlayer();
UpdateEnemyVis(true); //always aware of player position
UpdateTarget();
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 obstacle avoidance
attack_state = AS_NONE; // not attacking yet
melee_time = time;
run_sound_time = time;
} ]

// run to enemy to attack

monster_run[ ()
{
self.ThinkTime = 0;
if(self.health<=1)
{
self.think = "death_biz"; // in pain
return 0;
}
if((self.in_pain = true) and (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;
}
if(run_sound_time < time)
{
if(random(1,10)>7)
{
run_sound_time = time + RUNSOUNDDELAY;
PlaySound(RUNSOUND);
}
}
ai_run(random((RUNSPEED-2)*SCALE,(RUNSPEED+2)*SCALE)); // run toward enemy
} ]

// start of pain while running

monster_run_pain_start[ ()
{
switch(random(1,4)) // play one of 4 pain animations
{
case 1
{
Animate(PAIN);
PlaySound("PAINSOUND", 500);
}
case 2
{
Animate(PAIN1);
PlaySound("PAINSOUND1", 500);
}
case 3
{
Animate(PAIN2);
PlaySound("PAINSOUND2", 500);
}
case 4
{
Animate(PAIN3);
PlaySound("PAINSOUND3", 500);
}
}
SetHoldAtEnd(true); // set to stop at animation end
self.ThinkTime = 0;
self.think = "monster_run_pain";
} ]

// wait for animation to stop

monster_run_pain[ ()
{
self.ThinkTime = 0;
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";
run_sound_time = time + RUNSOUNDDELAY;
} ]

// go to last known location of enemy

monster_lost_target[ ()
{
self.ThinkTime = 0;
if(lost_time<time)
{
HighLevel("LostTarget"); // timed out while looking
return 0;
}
self.ThinkTime = 0;
if(self.health<=0)
{
self.think = "death_biz"; // in pain
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)
{
PlaySound("TARGETSOUND", 500);
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", 500);
}
case 2
{
Animate(PAIN1);
PlaySound("PAINSOUND1", 500);
}
case 3
{
Animate(PAIN2);
PlaySound("PAINSOUND2", 500);
}
case 4
{
Animate(PAIN3);
PlaySound("PAINSOUND3", 500);
}
}
SetHoldAtEnd(true); // set to stop at end
self.ThinkTime = 0;
self.think = "monster_lost_pain";
} ]

// wait till animation is done

monster_lost_pain[ ()
{
self.ThinkTime = 0;
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;
}
} ]

CheckForPoints[()
{
self.ThinkTime=0; // Process every frame
if(self.point_vis)
NextPoint();
{
// point is visible - go to high level for nav and targetting
HighLevel("LostTarget");
return 0;
}
else
{
// Point not visible - cycle points
PC=PC+1; // Increment point counter variable
if(PC > 110)
{
// Point count has exceeded max - set to 0. (Max 110)
PC=1;
}
NextPoint();
}

}]
// start of missile attack

monster_missile_start[ ()
{
switch(random(1,3)) // play one of 3 missile animations
{
case 1
{
Animate(MISSILEATTACK);
fire_delay = time + FIREDELAY; // set firing delay
PlaySound("MISSILESOUND", 500);
}
case 2
{
Animate(MISSILEATTACK1);
fire_delay = time + FIREDELAY1; // set firing delay
PlaySound("MISSLIESOUND1", 500);
}
case 3
{
Animate(MISSILEATTACK2);
fire_delay = time + FIREDELAY2; // set firing delay
PlaySound("MISSLIESOUND2", 500);
}
}
TargetPlayer();
UpdateEnemyVis(true); //always aware of player position
UpdateTarget();
self.ideal_yaw = enemy_yaw; // set direction to run
SetHoldAtEnd(true);
self.ThinkTime = 0.3;
self.think = "monster_missile";
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.3;
if(self.health<=0)
{
self.think = "death_biz";
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(self.player_Y>self.current_Y + SCALE)
{
Animate(SHOOTUP);
FireProjectile(PROJECTILE, FIREBONE, OFFSETX, OFFSETY, OFFSETZ, DAMAGEATTRIBUTE);
PlaySound("MISSILESOUND1", 500);
SetHoldAtEnd(false);
self.ThinkTime = 0.3;
}
if(self.player_Y<self.current_Y + SCALE)
{
Animate(SHOOTDOWN);
FireProjectile(PROJECTILE, FIREBONE, OFFSETX, OFFSETY, OFFSETZ, DAMAGEATTRIBUTE);
PlaySound("MISSILESOUND", 500);
SetHoldAtEnd(false);
self.ThinkTime = 0.3;
}
if(enemy_range>MISSILERANGE)
{
Animate(MISSILEATTACK);
FireProjectile(PROJECTILE, FIREBONE, OFFSETX, OFFSETY, OFFSETZ, DAMAGEATTRIBUTE);
fire_delay = time + FIREDELAY; // set firing delay
PlaySound("MISSILESOUND1", 500);
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);
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
PlaySound("MISSLIESOUND2", 500);
}
if(self.animate_at_end = true)
{
if(attack_delay < time)
{
//self.think = RUNFUNC; // no
self.think = "monster_missile_start";
SetHoldAtEnd(false);
self.ThinkTime = 0;
}
}
} ]
death_biz[ ()
{
Animate(DIE);
PlaySound("DIESOUND", 500);
SetHoldAtEnd(true); // set to stop at end
self.ThinkTime = 0.1;
self.think = "Death";
return 0;
} ]
// start of showing pain

monster_missile_pain_start[ ()
{
switch(random(1,4)) // play one of 4 pain animations
{
case 1
{
Animate(PAIN);
PlaySound("PAINSOUND", 500);
}
case 2
{
Animate(PAIN1);
PlaySound("PAINSOUND1", 500);
}
case 3
{
Animate(PAIN2);
PlaySound("PAINSOUND2", 500);
}
case 4
{
Animate(PAIN3);
PlaySound("PAINSOUND3", 500);
}
}
SetHoldAtEnd(true); // set to stop at end
self.ThinkTime = 0;
self.think = "monster_missile_pain";
} ]

// wait until animation is done

monster_missile_pain[ ()
{
self.ThinkTime = 0;
if(self.animate_at_end = true) // animation is done
{
self.think = "monster_missile_start"; // go back to missile attack
SetHoldAtEnd(false);
self.ThinkTime = 0;
}
} ]

// start of melee attack

monster_melee_start[ ()
{
switch(random(1,3)) // play one of 3 melee animations
{
case 1
{
Animate(MELEEATTACK);
}
case 2
{
Animate(MELEEATTACK1);
}
case 3
{
Animate(MELEEATTACK2);
}
}
SetHoldAtEnd(true); // set to stop at end
self.ThinkTime = 0.1;
self.think = "monster_melee";
} ]

// melee attack

monster_melee[ ()
{
self.ThinkTime = 0;
if(self.health<=0)
{
PlaySound("DIESOUND1", 500);
self.think = "death_biz"; // in pain
return 0;
}
if((self.in_pain = true) and (random(1,100)<PAINPERCENT))
{
SetHoldAtEnd(false);
self.think = "monster_melee_pain_start"; // in pain
return 0.1;
}
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);
}
case 2
{
Animate(MELEEATTACK1);
}
case 3
{
Animate(MELEEATTACK2);
}
}
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
}
} ]

// start of showing pain

monster_melee_pain_start[ ()
{
switch(random(1,4)) // play one of 4 pain animations
{
case 1
{
Animate(PAIN);
PlaySound("PAINSOUND", 500);
}
case 2
{
Animate(PAIN1);
PlaySound("PAINSOUND1", 500);
}
case 3
{
Animate(PAIN2);
PlaySound("PAINSOUND2", 500);
}
case 4
{
Animate(PAIN3);
PlaySound("PAINSOUND3", 500);
}
}
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.1;
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();
}
if (attack_state = AS_MISSILE) // do missile attack
{
ai_run_missile();
}

attack_state = AS_NONE;

if (CheckAnyAttack()) // check if you can start the actual attack
{
return 0;
}
if(RUNSPEED > 0)
{
walk_movetogoal(dist); // else move toward the enemy
}
} ]

// check if ready to do actual attacking

CheckAnyAttack[ ()
{
if(enemy_range<MAXMELEERANGE)
{
if(enemy_range<(MELEERANGE*SCALE)) // inside melee range
{
attack_state = AS_MELEE; // do a melee attack
return true;
}
if(enemy_range>(MELEERANGE*SCALE)) // outside melee range
{
attack_state = AS_MISSILE; // additional function - do a missile attack
return true;
}
return false;
}

if(enemy_range<MISSILERANGE)
{
if(attack_delay>time)
{
return false;
}
if(enemy_range<MISSILERANGE)
{
attack_state = AS_MISSILE; // do a missile attack
return true;
}
}
return false;
} ]

// 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);
}
} ]

// 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 naviagte to enemy

walk_movetogoal[ (dist)
{
if(IsFalling = true)
{
return 0; // don't move while falling
}
if(dist < 0)
{
return 0;
}
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(FORCEUP*SCALE); // jump up, forward and to side
ForceForward(FORCEFORWARD*SCALE);
if(random(1,10)<6)
{
ForceRight(FORCESIDE*SCALE);
}
else
{
ForceLeft(FORCESIDE*SCALE);
}
}
}
}
}
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;
} ]

}
User avatar
steven8
Posts: 1487
Joined: Wed Aug 24, 2005 9:08 am
Location: Barberton, OH

Post by steven8 »

Unfortunately, I am not a great sripter either. I will look at this deeper for you ,and try to help out all I can, but maybe this may help some:

SIGHTDIST [200] // max distance monster can see at idle
ALERTSIGHTDIST [500] // max distance monster can see when alert
FOV [90] // field of view in degrees
ATTACKFOV [180] // attacking field of view

If you limit things like sight distance, Field Of View and Attack Field Of View, your Pawn will not run as much of a risk at seeing more than one entity at a time, and not be unsure of who to attack. I'll also take a look to see what I can see.

I have not read al lthrough the perfectai tutorial or script. I'll do that as well as look at yours.
Steve Dilworth - Resisting change since 1965!
shadow
Posts: 81
Joined: Mon Jul 25, 2005 5:37 am

Post by shadow »

The perfect ai script is not a very good script to modify since there are many parts of the script that doesn't make sense.
It was chopped by the original poster, in a strange convoluted way to make it work for him.

for example most of the high level orders are not necessary since they just point to the low level section of the script.

this line - AddPainOrder("IdlePain", 100);
..go's to -> IdlePain[ (){NewOrder("FoundTarget");} ]
... which then points to the LOW Level section

Theres lots of this throughout the script which is unecessary and confusing for beginning scripters.

Also when you make changes to a script, only make changes to one area at a time in order to know where its broken when it doesn't run.
If you make changes all over the script and it doesn't run, it becomes difficult to trace the error.

Pickles ebook has a nice section on scripting to understand the basics.
shadow
Posts: 81
Joined: Mon Jul 25, 2005 5:37 am

Post by shadow »

I'm not sure if you already know this, in order to have 2 different groups of monsters attack each other(hostile to each other) you must have 2 different named scripts.

So one group of pawns uses a script perfectaisolder1
has this set in the script- GROUP [solder2] // name of monster group

Then the other group of pawns would use script perfectai2
with this set in the script- GROUP [solder1] // name of monster group

Its been a long time since I set up different groups.

and this must be set to true for both scripts
- HOSTILEDIFFERENT [true] // hostile to different Pawn group
shadow
Posts: 81
Joined: Mon Jul 25, 2005 5:37 am

Post by shadow »

To you final question about melee and missile...

at first glance it would seem that your mellee range [55] is greater than your missile range [50]

your missile range has to be greater than your melee range to work

The melee range vs missile range (check both in one script)is something that I wrote to enhance a script several years ago and copied to this script.
If I have time tonight I check this script out in more detail to see whats going on

hope that helps
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 »

Most of the sight distance values and things on top of the script were being edited as I went along while i was working and it does appear that i over looked a lot of simple things. i understand that i need 2 different scripts to get them to attack oneanother but thanks for asking. :) As for the changing from high to low so often, that does seem very unnessary for most any script because it also causes frame rate problems. I also want to try to leave the script as "open-ended" as possible so that possibly in the futere i could change from:

seeing the target-> running to the target-> shooting the target.

TO somthing like this:



Seeing the target->(if-then)(1 else 2 else 3)
(1) -> Running to the target-> ect...
(2) -> Running to cover-> wait for target to approach-> ect...
(3) -> yelling for help -> ect...

High level to low level transitions may be the only way to get this to work but first the script needs to be simple enough to work, so i say we try to get the unneeded transitions out.

PS.
My (piece of junk) game computer is temporarly out of commission so the only things i have access to are the files that i have backed up on disks (most all of them, including a working folder of the RF install) and this computer with internet that dosent belong to me nor does it have any file transfer hardware. I could put a small install of the RF engine under a folder and test the scripts there but I cant always get on this computer when i need to or effectivly get large files off of it. if we finish fixing the scripts then i will save a copy on my folder on this computer in case i need to post it here again but i will need to print off the script and type any canges by hand for my version. and i am ok with that but it may take me some time to respond to posts and to test scripts so please be patient if i have not responded in a few days i am not trying to be rude.

Thank you everyone for being so eager to help, I am going to try to work some of the minor bugs out of the script today. keep me posted if anyone has some more ideas. :)
User avatar
steven8
Posts: 1487
Joined: Wed Aug 24, 2005 9:08 am
Location: Barberton, OH

Post by steven8 »

I promise not to be upset with you for taking awhile to respond if promise not to be upset with me. I am having some problem with the site reading the cookies on my machine, and I totally lose posts since last visit. I'll click that link, and it'll come up and say, nothing found, and when I go back to the main forum index, all of the new markers are gone. I'm working hunt and peck. I use my posts and see which ones I haven't answered yet and so forth.

Anyway, it sounds like mr. shadow knows what he is doing. Together we can learn some good stuff here.

I've copied over your script and will be looking at it to, to see if I can help or raise some intelligent questions.
Steve Dilworth - Resisting change since 1965!
shadow
Posts: 81
Joined: Mon Jul 25, 2005 5:37 am

Post by shadow »

I hope to have my own pawn script out, (completed) within the next month.
Its been in the works for a long time, but should be a great starting point for you.

I find its easy to make a pawn script, but much harder to make one for multiple pawns.

For example if the player walks into a room with 4 pawns, you don't want the pawns to all fire at the exact same time and do the exact same animation.

It just doesn't look real. (I used lots of random variables for strafe directions and random variables for starting the fire animation)

Also its difficult to chase the player with out colliding into other pawns or shooting each other while firing at the player. (I have that worked out now)

I think I have most of the bugs worked out and it general it works amazing.
Must really be seen to be appreciated.

So far my pawns will dodge/strafe very effectively, fire into empty doorways, (just in case the player comes back and after loosing sight) and melee when close.

The pawn also effectively searches for the closest script point to walk towards, in the general area of the player.

Still to add is for the pawn to look for cover, and also to negotiate stairs.

I posted this so it forces me to clean up and complete the script for everyone to use.
I'm off for a week camping starting tomorrow but will update after I get back.
User avatar
Juutis
Posts: 1511
Joined: Thu Jan 12, 2006 12:46 pm
Location: Finland

Post by Juutis »

Hey that sounds amazing! I've used a lot of different random variables for my pawns too, those little strafe movements and shooting bursts. It is neat to watch those little fellows shoot bursts of 3-6 bullets with an uzi. :D

It also adds reality to the pawns' behaviours if you add a little random delay after the pawn has seen the player, before attacking. Reaction time, you know. :wink:
That way you can rush to a room and wipe out three out of four bad guys with a SMG before they even know what hit them. :D

The problem with my pawns has always been the fact that they've been "blind" to their surroundings. Bumping into each others, running towards walls, clumsy cover-finding etc... so I'm looking forward to see how you have dealt with these problems.

I think you will solve some major problems of all those non-scripter guys. Those basic scripts of RF aren't really too good, so it's about time to get a good and working enemy AI script!

Best of luck to you! :D
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 would be extreamly eagar to try this script especially if you finish it like you said. it sounds by far better than what we are working with now so please feel free to post it. :D
I am very interested in seeing how you did some of this stuff.
It sounds very cool.

good luck in finishing it, write back soon and tell us your progress so far.

Thanks
MakerOfGames
Posts: 866
Joined: Fri Jul 08, 2005 4:27 am
Location: PA, USA

Post by MakerOfGames »

Wow that new pawn script sounds awsome! Its those little things like the enemies fireing differently that makes you feel more in the game. I cant wait to see it.
Think outside the box.
To go on an adventure, one must discard the comforts and safety of the known and trusted.
shadow
Posts: 81
Joined: Mon Jul 25, 2005 5:37 am

Post by shadow »

Hi all
I havn't forgotten about you guys.
Been extremely busy the last little while. To many things on my plate
Anyways, I'll work on it tomorrow and thursday to get it finished then uploaded.
Post Reply