1195 lines
40 KiB
Plaintext
1195 lines
40 KiB
Plaintext
////////////////////////////////////////////////////////////////////////////
|
|
// Real Time Strategy - NWN - UNIT AI
|
|
//==========================================================================
|
|
// By Deva Bryson Winblood. 02/24/2003
|
|
////////////////////////////////////////////////////////////////////////////
|
|
#include "rts_header"
|
|
#include "rts_h_moving"
|
|
#include "rtsh_multiplay"
|
|
#include "antistuck_h"
|
|
#include "uc_h"
|
|
#include "prc_inc_util"
|
|
|
|
///////////////// PROTOTYPES ///////////////////////////////////////////////
|
|
|
|
void fnMainAIRoutine(); // Called if all interruption options are false
|
|
void fnRoam(int nS); // Roam about
|
|
void fnHarvest(int nS); // Harvest
|
|
void fnGuard(int nS); // Guard
|
|
void fnAttack(int nS); // Attack
|
|
void fnSeek(int nS); // Seek
|
|
void fnHeal(int nS); // Heal
|
|
void fnUpgrade(); // Upgrade
|
|
void fnFollow(object oD);// Follow command
|
|
void fnGuardBase(); // Guard The Base
|
|
void fnLightSensitive(); // apply sensitivity;
|
|
void fnBurning(); // apply burning damage
|
|
void fnScout(int nS); // Scout for items of interest
|
|
void fnFleeSunlight(); // run for cover at dawn
|
|
void fnRecon(); // reconnoiter command
|
|
void fnHealSelfNPC(); // healing routine for non-team NPCs
|
|
|
|
//======================================================[ M A I N ]=========
|
|
void main()
|
|
{
|
|
object oMod=GetModule();
|
|
int nDelay=GetLocalInt(oMod,"nAIDelay"); // delay before firing again
|
|
object oAttacker=GetLastAttacker(); // who attacked me last
|
|
float fDist=0.0;
|
|
int nDisableNonEssential=GetLocalInt(oMod,"nDisableNonEssential");
|
|
// string sNamer;
|
|
object oEnemy=GetNearestCreature(CREATURE_TYPE_PERCEPTION,PERCEPTION_SEEN,OBJECT_SELF,1,CREATURE_TYPE_REPUTATION,REPUTATION_TYPE_ENEMY,CREATURE_TYPE_IS_ALIVE,TRUE);
|
|
int nMState=GetLocalInt(OBJECT_SELF,"nMState");
|
|
if (!GetIsDead(oAttacker)) fDist=GetDistanceBetween(oAttacker,OBJECT_SELF);
|
|
if (nDisableNonEssential==TRUE) nDelay=nDelay+2; // add 2 seconds to AI processing time to reduce CPU usage
|
|
SetLocalInt(OBJECT_SELF,"nLastDelaySec",GetTimeSecond()); // set monitor for HBKICK
|
|
//sNamer="["+GetTag(OBJECT_SELF)+"]"+GetName(OBJECT_SELF);
|
|
// sNamer=sNamer+" MS:"+IntToString(nMState)+" TIME:"+IntToString(GetTimeSecond());
|
|
//SendMessageToPC(GetFirstPC(),sNamer);
|
|
if (!IsInConversation(OBJECT_SELF))
|
|
{ // !talking
|
|
if (fDist==0.0||fDist>20.0)
|
|
{ // attacker is not nearby
|
|
if ((oEnemy!=OBJECT_INVALID&&nMState!=1)||oEnemy==OBJECT_INVALID)
|
|
{ // There is either no enemy near or I am in HARVEST mode
|
|
if (GetLocalInt(OBJECT_SELF,"nAIBusy")!=TRUE)
|
|
fnMainAIRoutine();
|
|
} // There is either no enemy near or I am in HARVEST mode
|
|
} // attacker is not nearby
|
|
} // !talking
|
|
if (fnCheckForLight()>0&&GetNearestObjectByTag("RTS_SURFACE",OBJECT_SELF)!=OBJECT_INVALID)
|
|
{ // possible sun problems
|
|
if (GetIsDay())
|
|
{
|
|
if (fnCheckForLight()==1) fnLightSensitive();
|
|
else
|
|
{
|
|
fnBurning();
|
|
if (nMState!=13)
|
|
{ // flee dumbass
|
|
SetLocalInt(OBJECT_SELF,"nMState",13);
|
|
SetLocalInt(OBJECT_SELF,"nSState",0);
|
|
} // flee dumbass
|
|
}
|
|
}
|
|
else if ((GetIsDawn()||GetIsDay())&&nMState!=5)
|
|
{ // flee sunlight
|
|
if (nMState!=13)
|
|
{ // flee dumbass
|
|
SetLocalInt(OBJECT_SELF,"nMState",13);
|
|
SetLocalInt(OBJECT_SELF,"nSState",0);
|
|
} // flee dumbass
|
|
} // flee sunlight
|
|
} // possible sun problems
|
|
fDist=IntToFloat(nDelay)+(IntToFloat(Random(10))/10.0);
|
|
DelayCommand(fDist,ExecuteScript("rts_unit_ai",OBJECT_SELF));
|
|
}
|
|
//======================================================[ M A I N ]=========
|
|
|
|
|
|
//----------------------------------------- MAIN ROUTINE -------------------
|
|
void fnMainAIRoutine()
|
|
{ // Process the state machine
|
|
int nMState=GetLocalInt(OBJECT_SELF,"nMState"); // main state
|
|
int nSState=GetLocalInt(OBJECT_SELF,"nSState"); // sub state
|
|
object oDestWP=GetLocalObject(OBJECT_SELF,"oDestWP"); // Destination Waypoint
|
|
object oMe=OBJECT_SELF;
|
|
object oPer=oPer=GetNearestCreature(CREATURE_TYPE_PERCEPTION,PERCEPTION_SEEN,oMe,CREATURE_TYPE_REPUTATION,REPUTATION_TYPE_ENEMY,CREATURE_TYPE_IS_ALIVE,TRUE);
|
|
int nCHP=GetCurrentHitPoints(oMe);
|
|
string sID=GetLocalString(oMe,"sTeamID");
|
|
int nMHP=GetMaxHitPoints(oMe);
|
|
float fDist;
|
|
if (oPer==OBJECT_INVALID)
|
|
{ // I don't see an enemy
|
|
// check to see if viable to switch to heal
|
|
if (nCHP<(nMHP/2))
|
|
{ // 50+% damaged
|
|
if (nMState==2||nMState==11||nMState==0)
|
|
{ // okay to interrupt
|
|
nMState=9; // switch to HEAL
|
|
nSState=0;
|
|
SetLocalInt(oMe,"nMState",9);
|
|
SetLocalInt(oMe,"nSState",0);
|
|
} // okay to interrupt
|
|
} // 50+% damaged
|
|
} // I don't see an enemy
|
|
//SendMessageToPC(GetFirstPC(),GetTag(OBJECT_SELF)+"(M:"+IntToString(nMState)+",S:"+IntToString(nSState)+")");
|
|
switch(nMState)
|
|
{ // main state switch
|
|
case 1: { // HARVEST
|
|
//SetAILevel(oMe,AI_LEVEL_NORMAL);
|
|
//fnHarvest(nSState);
|
|
SetAILevel(oMe,AI_LEVEL_NORMAL);
|
|
ExecuteScript("rts_ai_harvest",OBJECT_SELF);
|
|
break;
|
|
} // HARVEST
|
|
case 2: { // GUARD
|
|
//SetAILevel(oMe,AI_LEVEL_LOW);
|
|
if (oPer==OBJECT_INVALID) SetAILevel(oMe,AI_LEVEL_DEFAULT);
|
|
else { SetAILevel(oMe,AI_LEVEL_NORMAL); }
|
|
//fnGuard(nSState);
|
|
ExecuteScript("rts_ai_guard",oMe);
|
|
break;
|
|
} // GUARD
|
|
case 3: { // ATTACK
|
|
SetAILevel(oMe,AI_LEVEL_NORMAL);
|
|
fnAttack(nSState);
|
|
break;
|
|
} // ATTACK
|
|
case 4: { // PROTECT
|
|
SetAILevel(oMe,AI_LEVEL_NORMAL);
|
|
fnFollow(oDestWP);
|
|
break;
|
|
} // PROTECT
|
|
case 5: { // FOLLOW
|
|
SetAILevel(oMe,AI_LEVEL_NORMAL);
|
|
fnFollow(oDestWP);
|
|
break;
|
|
} // FOLLOW
|
|
case 6: { // SEEK
|
|
if (oPer==OBJECT_INVALID) SetAILevel(oMe,AI_LEVEL_DEFAULT);
|
|
else { SetAILevel(oMe,AI_LEVEL_NORMAL); }
|
|
fnSeek(nSState);
|
|
break;
|
|
} // SEEK
|
|
case 7: { // STAND
|
|
if (oPer==OBJECT_INVALID) SetAILevel(oMe,AI_LEVEL_DEFAULT);
|
|
else { SetAILevel(oMe,AI_LEVEL_NORMAL); }
|
|
AssignCommand(oMe,ClearAllActions());
|
|
break;
|
|
} // STAND
|
|
case 8: { // ROAM
|
|
if (oPer==OBJECT_INVALID) SetAILevel(oMe,AI_LEVEL_DEFAULT);
|
|
else { SetAILevel(oMe,AI_LEVEL_NORMAL); }
|
|
fnRoam(nSState);
|
|
break;
|
|
} // ROAM
|
|
case 9: { // HEAL
|
|
if (oPer==OBJECT_INVALID) SetAILevel(oMe,AI_LEVEL_DEFAULT);
|
|
else { SetAILevel(oMe,AI_LEVEL_NORMAL); }
|
|
if (GetStringLength(sID)>2) fnHeal(nSState);
|
|
else
|
|
{ // non-team NPC heal
|
|
fnHealSelfNPC();
|
|
} // non-team NPC heal
|
|
break;
|
|
} // HEAL
|
|
case 10: { // UPGRADE
|
|
SetAILevel(oMe,AI_LEVEL_DEFAULT);
|
|
fnUpgrade();
|
|
break;
|
|
} // UPGRADE
|
|
case 11: { // Guard Base
|
|
if (oPer==OBJECT_INVALID) SetAILevel(oMe,AI_LEVEL_DEFAULT);
|
|
else { SetAILevel(oMe,AI_LEVEL_NORMAL); }
|
|
fnGuardBase();
|
|
break;
|
|
} // Guard Base
|
|
case 12: { // Scout
|
|
SetAILevel(oMe,AI_LEVEL_NORMAL);
|
|
//fnScout(nSState);
|
|
ExecuteScript("rts_ai_scout2",OBJECT_SELF);
|
|
break;
|
|
} // Scout
|
|
case 13: { // flee sunlight
|
|
SetAILevel(oMe,AI_LEVEL_NORMAL);
|
|
fnFleeSunlight();
|
|
} // flee sunlight
|
|
case 14: { // recon
|
|
SetAILevel(oMe,AI_LEVEL_NORMAL);
|
|
fnRecon();
|
|
break;
|
|
} // recon
|
|
case 16: { // bait
|
|
if (oPer==OBJECT_INVALID) SetAILevel(oMe,AI_LEVEL_DEFAULT);
|
|
else { SetAILevel(oMe,AI_LEVEL_NORMAL); }
|
|
break;
|
|
} // bait
|
|
case 17: { // raid
|
|
SetAILevel(oMe,AI_LEVEL_NORMAL);
|
|
ExecuteScript("ai_raid_v2",OBJECT_SELF);
|
|
break;
|
|
} // raid
|
|
case 18: { // control point capture - AI
|
|
SetAILevel(oMe,AI_LEVEL_NORMAL);
|
|
ExecuteScript("ai_cpc",OBJECT_SELF);
|
|
break;
|
|
} // control point capture - AI
|
|
case 19: { // harvester - AI
|
|
SetAILevel(oMe,AI_LEVEL_NORMAL);
|
|
ExecuteScript("ai_harvester",OBJECT_SELF);
|
|
break;
|
|
} // harvester - AI
|
|
case 20: { // item collector - ai
|
|
SetAILevel(oMe,AI_LEVEL_NORMAL);
|
|
ExecuteScript("ai_itemcollect",OBJECT_SELF);
|
|
break;
|
|
} // item collector - ai
|
|
case 21: { // guard area
|
|
SetAILevel(oMe,AI_LEVEL_DEFAULT);
|
|
ExecuteScript("ai_guard",OBJECT_SELF);
|
|
break;
|
|
} // guard area
|
|
case 22: { // scavenger bird
|
|
SetAILevel(oMe,AI_LEVEL_NORMAL);
|
|
ExecuteScript("ai_sbird",oMe);
|
|
break;
|
|
} // scavenger bird
|
|
case 23: { // Power reservoir Recover
|
|
SetAILevel(oMe,AI_LEVEL_NORMAL);
|
|
ExecuteScript("ai_powerres",oMe);
|
|
break;
|
|
} // Power Reservoir Recover
|
|
case 24: { // Mine
|
|
SetAILevel(oMe,AI_LEVEL_NORMAL);
|
|
ExecuteScript("ai_mine",oMe);
|
|
break;
|
|
} // Mine
|
|
case 25: { // Merchant
|
|
ExecuteScript("ai_merchant",oMe);
|
|
break;
|
|
} // Merchant
|
|
case 26: { // Merchant Helper
|
|
ExecuteScript("ai_merchanth",oMe);
|
|
break;
|
|
} // Merchant Helper
|
|
default: {
|
|
if (oPer==OBJECT_INVALID) SetAILevel(oMe,AI_LEVEL_DEFAULT);
|
|
else { SetAILevel(oMe,AI_LEVEL_NORMAL); }
|
|
break;
|
|
}
|
|
} // main state switch
|
|
} // fnMainAIRoutine()
|
|
|
|
|
|
void fnHealSelfNPC()
|
|
{ // try to heal self
|
|
object oMe=OBJECT_SELF;
|
|
object oItem;
|
|
int nMHP=GetMaxHitPoints(oMe);
|
|
int nCHP=GetCurrentHitPoints(oMe);
|
|
if (nCHP<nMHP)
|
|
{ // need some healing
|
|
if (GetHasSpell(SPELL_CURE_MINOR_WOUNDS)==TRUE)
|
|
ActionCastSpellAtObject(SPELL_CURE_MINOR_WOUNDS,oMe);
|
|
else if (GetHasSpell(SPELL_CURE_LIGHT_WOUNDS)==TRUE)
|
|
ActionCastSpellAtObject(SPELL_CURE_LIGHT_WOUNDS,oMe);
|
|
else if (GetHasSpell(SPELL_CURE_MODERATE_WOUNDS)==TRUE)
|
|
ActionCastSpellAtObject(SPELL_CURE_MODERATE_WOUNDS,oMe);
|
|
else if (GetHasSpell(SPELL_CURE_SERIOUS_WOUNDS)==TRUE)
|
|
ActionCastSpellAtObject(SPELL_CURE_SERIOUS_WOUNDS,oMe);
|
|
else if (GetHasSpell(SPELL_HEAL)==TRUE)
|
|
ActionCastSpellAtObject(SPELL_HEAL,oMe);
|
|
else if (GetHasSpell(SPELL_HEALING_CIRCLE)==TRUE)
|
|
ActionCastSpellAtObject(SPELL_HEALING_CIRCLE,oMe);
|
|
else if (GetHasSpell(SPELL_MASS_HEAL)==TRUE)
|
|
ActionCastSpellAtObject(SPELL_MASS_HEAL,oMe);
|
|
else if (GetHasSpell(SPELL_REGENERATE)==TRUE)
|
|
ActionCastSpellAtObject(SPELL_REGENERATE,oMe);
|
|
else if (GetHasSpell(SPELL_RESTORATION)==TRUE)
|
|
ActionCastSpellAtObject(SPELL_RESTORATION,oMe);
|
|
else if (GetHasSpell(SPELL_LESSER_RESTORATION)==TRUE)
|
|
ActionCastSpellAtObject(SPELL_LESSER_RESTORATION,oMe);
|
|
else if (GetItemPossessedBy(oMe,"NW_IT_MEDKIT001")!=OBJECT_INVALID)
|
|
{
|
|
oItem=GetItemPossessedBy(oMe,"NW_IT_MEDKIT001");
|
|
DestroyObject(oItem);
|
|
ActionPlayAnimation(ANIMATION_FIREFORGET_DRINK,1.0,1.0);
|
|
ActionCastSpellAtObject(SPELL_HEALINGKIT,oMe,METAMAGIC_ANY,TRUE,0,PROJECTILE_PATH_TYPE_DEFAULT,TRUE);
|
|
}
|
|
else if (GetItemPossessedBy(oMe,"NW_IT_MEDKIT002")!=OBJECT_INVALID)
|
|
{
|
|
oItem=GetItemPossessedBy(oMe,"NW_IT_MEDKIT002");
|
|
DestroyObject(oItem);
|
|
ActionPlayAnimation(ANIMATION_FIREFORGET_DRINK,1.0,1.0);
|
|
ActionCastSpellAtObject(SPELL_HEALINGKIT,oMe,METAMAGIC_ANY,TRUE,0,PROJECTILE_PATH_TYPE_DEFAULT,TRUE);
|
|
}
|
|
else if (GetItemPossessedBy(oMe,"NW_IT_MEDKIT003")!=OBJECT_INVALID)
|
|
{
|
|
oItem=GetItemPossessedBy(oMe,"NW_IT_MEDKIT003");
|
|
DestroyObject(oItem);
|
|
ActionPlayAnimation(ANIMATION_FIREFORGET_DRINK,1.0,1.0);
|
|
ActionCastSpellAtObject(SPELL_HEALINGKIT,oMe,METAMAGIC_ANY,TRUE,0,PROJECTILE_PATH_TYPE_DEFAULT,TRUE);
|
|
}
|
|
else if (GetItemPossessedBy(oMe,"NW_IT_MEDKIT004")!=OBJECT_INVALID)
|
|
{
|
|
oItem=GetItemPossessedBy(oMe,"NW_IT_MEDKIT004");
|
|
DestroyObject(oItem);
|
|
ActionPlayAnimation(ANIMATION_FIREFORGET_DRINK,1.0,1.0);
|
|
ActionCastSpellAtObject(SPELL_HEALINGKIT,oMe,METAMAGIC_ANY,TRUE,0,PROJECTILE_PATH_TYPE_DEFAULT,TRUE);
|
|
}
|
|
else if (GetItemPossessedBy(oMe,"NW_IT_MPOTION001")!=OBJECT_INVALID)
|
|
{
|
|
oItem=GetItemPossessedBy(oMe,"NW_IT_MPOTION001");
|
|
DestroyObject(oItem);
|
|
ActionPlayAnimation(ANIMATION_FIREFORGET_DRINK,1.0,1.0);
|
|
ActionCastSpellAtObject(SPELL_CURE_LIGHT_WOUNDS,oMe,METAMAGIC_ANY,TRUE,0,PROJECTILE_PATH_TYPE_DEFAULT,TRUE);
|
|
}
|
|
else if (GetItemPossessedBy(oMe,"NW_IT_MPOTION020")!=OBJECT_INVALID)
|
|
{
|
|
oItem=GetItemPossessedBy(oMe,"NW_IT_MPOTION020");
|
|
DestroyObject(oItem);
|
|
ActionPlayAnimation(ANIMATION_FIREFORGET_DRINK,1.0,1.0);
|
|
ActionCastSpellAtObject(SPELL_CURE_MODERATE_WOUNDS,oMe,METAMAGIC_ANY,TRUE,0,PROJECTILE_PATH_TYPE_DEFAULT,TRUE);
|
|
}
|
|
else if (GetItemPossessedBy(oMe,"NW_IT_MPOTION002")!=OBJECT_INVALID)
|
|
{
|
|
oItem=GetItemPossessedBy(oMe,"NW_IT_MPOTION002");
|
|
DestroyObject(oItem);
|
|
ActionPlayAnimation(ANIMATION_FIREFORGET_DRINK,1.0,1.0);
|
|
ActionCastSpellAtObject(SPELL_CURE_SERIOUS_WOUNDS,oMe,METAMAGIC_ANY,TRUE,0,PROJECTILE_PATH_TYPE_DEFAULT,TRUE);
|
|
}
|
|
else if (GetItemPossessedBy(oMe,"NW_IT_MPOTION003")!=OBJECT_INVALID)
|
|
{
|
|
oItem=GetItemPossessedBy(oMe,"NW_IT_MPOTION003");
|
|
DestroyObject(oItem);
|
|
ActionPlayAnimation(ANIMATION_FIREFORGET_DRINK,1.0,1.0);
|
|
ActionCastSpellAtObject(SPELL_CURE_CRITICAL_WOUNDS,oMe,METAMAGIC_ANY,TRUE,0,PROJECTILE_PATH_TYPE_DEFAULT,TRUE);
|
|
}
|
|
else if (GetItemPossessedBy(oMe,"NW_IT_MPOTION012")!=OBJECT_INVALID)
|
|
{
|
|
oItem=GetItemPossessedBy(oMe,"NW_IT_MPOTION012");
|
|
DestroyObject(oItem);
|
|
ActionPlayAnimation(ANIMATION_FIREFORGET_DRINK,1.0,1.0);
|
|
ActionCastSpellAtObject(SPELL_HEAL,oMe,METAMAGIC_ANY,TRUE,0,PROJECTILE_PATH_TYPE_DEFAULT,TRUE);
|
|
}
|
|
} // need some healing
|
|
else
|
|
{
|
|
SetLocalInt(oMe,"nMState",0);
|
|
}
|
|
} // fnHealSelfNPC()
|
|
|
|
void fnRoam(int nS)
|
|
{ // Do roaming based on Substate passed
|
|
object oMe=OBJECT_SELF;
|
|
object oDest=GetLocalObject(oMe,"oDestWP");
|
|
int nR;
|
|
object oWork;
|
|
string sID=GetLocalString(oMe,"sTeamID");
|
|
object oPC=GetLocalObject(GetModule(),"oTeamLead"+sID);
|
|
object oArea=GetArea(oMe);
|
|
object oLastAt=GetLocalObject(oMe,"oLastAt");
|
|
float fDist;
|
|
int nTMI=0;
|
|
switch (nS)
|
|
{ // states
|
|
case 0: { // choose destination
|
|
nR=d8();
|
|
//SendMessageToPC(oPC,"["+GetName(oMe)+"](Roam) Current Area:"+GetName(oArea)+" [choose destination]");
|
|
if (nR==1)
|
|
{ // leave area
|
|
nR=0;
|
|
oWork=fnRandomObject(oArea,OBJECT_TYPE_DOOR);
|
|
while((GetTransitionTarget(oWork)==OBJECT_INVALID||GetArea(GetTransitionTarget(oWork))==oLastAt)&&nTMI<MAX_AI_SCAN)
|
|
{ // find door to another area
|
|
nR++;
|
|
if (nR>1) nR=0;
|
|
nTMI++;
|
|
if (nR==0) oWork=fnRandomObject(oArea,OBJECT_TYPE_DOOR);
|
|
else oWork=fnRandomObject(oArea,OBJECT_TYPE_TRIGGER);
|
|
} // find door to another area
|
|
if (oWork==OBJECT_INVALID) SetLocalObject(oMe,"oLastAt",OBJECT_INVALID);
|
|
oWork=GetTransitionTarget(oWork);
|
|
if (oWork!=OBJECT_INVALID)
|
|
{ //
|
|
oWork=fnGetNearTransitionTarget(oWork);
|
|
} //
|
|
SetLocalObject(oMe,"oLastAt",GetArea(oMe));
|
|
if(d10()<2)
|
|
{ // make a comment
|
|
nR=GetAbilityScore(oMe,ABILITY_INTELLIGENCE);
|
|
if (nR>6&&nR<10) ActionSpeakString("Me leave here.");
|
|
else if (nR>9&&nR<13) ActionSpeakString("I need to go somewhere else.");
|
|
else if (nR>12) ActionSpeakString("I must be away from this region.");
|
|
} // make a comment
|
|
} // leave area
|
|
else
|
|
{ // roam in same area
|
|
nR=d4();
|
|
if (nR<3) oWork=fnRandomObject(oArea,OBJECT_TYPE_WAYPOINT);
|
|
else oWork=fnRandomObject(oArea,OBJECT_TYPE_PLACEABLE);
|
|
} // roam in same area
|
|
oDest=oWork;
|
|
//SendMessageToPC(oPC,"["+GetName(oMe)+"] Destination Chosen:"+GetTag(oDest)+" in Area("+GetName(GetArea(oDest))+")");
|
|
SetLocalObject(oMe,"oDestWP",oDest);
|
|
SetLocalInt(oMe,"nSState",1);
|
|
} // choose destination
|
|
case 1: { // move to destination
|
|
fDist=GetDistanceBetween(oDest,oMe);
|
|
//SendMessageToPC(oPC,"["+GetName(oMe)+"](Roam)Moving to:"+GetTag(oDest)+" in area:"+GetName(GetArea(oDest))+" distance:"+FloatToString(fDist));
|
|
if ((fDist==0.0&&GetArea(oDest)!=GetArea(oMe))||fDist>4.0)
|
|
{ // still a ways to go
|
|
nR=fnMoveToDestination(oDest,TRUE);
|
|
if (nR==-1)
|
|
{ // error
|
|
SetLocalObject(oMe,"oDestWP",OBJECT_INVALID);
|
|
SetLocalInt(oMe,"nSState",0); // error choose another destination
|
|
} // error
|
|
} // still a ways to go
|
|
else
|
|
{ // should be there
|
|
SetLocalInt(oMe,"nSState",0); // choose another destination
|
|
} // should be there
|
|
break;
|
|
} // move to destination
|
|
} // states
|
|
} // fnRoam()
|
|
|
|
//---------------------------------------------- GUARD -----------------------
|
|
void fnGuard(int nS)
|
|
{ // Guard
|
|
int nRun=GetLocalInt(OBJECT_SELF,"nRun");
|
|
object oMe=OBJECT_SELF;
|
|
object oDest=GetLocalObject(oMe,"oDestWP");
|
|
int nC=0;
|
|
int nR;
|
|
float fDist;
|
|
string sID=GetLocalString(oMe,"sTeamID");
|
|
object oMod=GetModule();
|
|
object oPC=GetLocalObject(oMod,"oTeamLead"+sID);
|
|
//SendMessageToPC(oPC," == fnGuard() ==");
|
|
while(oDest==OBJECT_INVALID&&nC<10)
|
|
{ // pick random guard point
|
|
nC++;
|
|
nR=Random(5)+1;
|
|
oDest=GetLocalObject(oMod,"oGUARD"+IntToString(nR)+sID);
|
|
} // pick random harvest point
|
|
if (oDest==OBJECT_INVALID)
|
|
oDest=GetLocalObject(oMod,"oGUARD4"+sID);
|
|
if (oDest==OBJECT_INVALID)
|
|
oDest=GetWaypointByTag(sID+"_VAULT");
|
|
if (oDest==OBJECT_INVALID)
|
|
SendMessageToPC(GetFirstPC(),"RTS ERROR: Missing Guard point #4 for Team ["+sID+"]");
|
|
switch(nS)
|
|
{ // main switch
|
|
case 0: { // move to guard post
|
|
fDist=GetDistanceToObject(oDest);
|
|
if (fDist!=-1.0&&fDist<3.5)
|
|
{ // already there
|
|
SetLocalInt(oMe,"nSState",1);
|
|
} // already there
|
|
else if (oDest!=OBJECT_INVALID)
|
|
fnMoveToDestination(oDest,TRUE);
|
|
else if (oDest==OBJECT_INVALID&&GetAbilityScore(oMe,ABILITY_INTELLIGENCE)>9)
|
|
{
|
|
AssignCommand(oMe,ActionSpeakString("I don't see the guard position anywhere."));
|
|
SetLocalInt(oMe,"nMState",11); // Guard the base
|
|
}
|
|
else if (oDest==OBJECT_INVALID) SetLocalInt(oMe,"nMState",11); // Guard the base
|
|
break;
|
|
} // Move to guard post
|
|
case 1: { // Pick nearby object and move
|
|
if (GetCurrentAction(oMe)==ACTION_INVALID)
|
|
{ // AI
|
|
if (d4()<3)
|
|
oDest=fnRandomObject(GetArea(oMe),OBJECT_TYPE_WAYPOINT);
|
|
else
|
|
oDest=fnRandomObject(GetArea(oMe),OBJECT_TYPE_PLACEABLE);
|
|
//SendMessageToPC(GetFirstPC(),"Pick random move:"+GetTag(oDest));
|
|
AssignCommand(oMe,ClearAllActions());
|
|
AssignCommand(oMe,ASActionMoveToObject(oDest,nRun,1.0));
|
|
DelayCommand(8.0,AssignCommand(oMe,ClearAllActions()));
|
|
DelayCommand(5.0,SetLocalInt(oMe,"nSState",0));
|
|
} // AI
|
|
break;
|
|
} // Pick nearby object and move
|
|
} // main switch
|
|
} // fnGuard()
|
|
|
|
//----------------------------------------------- ATTACK --------------------
|
|
void fnAttack(int nS)
|
|
{ // Attack
|
|
int nRun=GetLocalInt(OBJECT_SELF,"nRun");
|
|
object oMe=OBJECT_SELF;
|
|
object oDest=GetLocalObject(oMe,"oDestWP");
|
|
int nC=0;
|
|
int nR;
|
|
float fDist;
|
|
string sID=GetLocalString(oMe,"sTeamID");
|
|
object oMod=GetModule();
|
|
while(oDest==OBJECT_INVALID&&nC<10)
|
|
{ // pick random harvest point
|
|
nC++;
|
|
nR=Random(5)+1;
|
|
oDest=GetLocalObject(oMod,"oATTACK"+IntToString(nR)+sID);
|
|
} // pick random harvest point
|
|
switch(nS)
|
|
{ // main switch
|
|
case 0: { // move to guard post
|
|
fDist=GetDistanceBetween(oDest,OBJECT_SELF);
|
|
if (fDist!=0.0&&fDist<4.0)
|
|
{ // already there
|
|
SetLocalInt(oMe,"nSState",1);
|
|
} // already there
|
|
else if (oDest!=OBJECT_INVALID)
|
|
fnMoveToDestination(oDest,TRUE);
|
|
if (oDest==OBJECT_INVALID&&GetAbilityScore(oMe,ABILITY_INTELLIGENCE)>9)
|
|
{
|
|
AssignCommand(oMe,ActionSpeakString("I don't see the attack position anywhere."));
|
|
}
|
|
if (oDest==OBJECT_INVALID) SetLocalInt(oMe,"nMState",11); // Guard the base
|
|
break;
|
|
} // Move to guard post
|
|
case 1: { // Pick nearby object and move
|
|
if (GetCurrentAction(oMe)==ACTION_INVALID)
|
|
{ // AI
|
|
if (d4()<3)
|
|
oDest=fnRandomObject(GetArea(oMe),OBJECT_TYPE_WAYPOINT);
|
|
else
|
|
oDest=fnRandomObject(GetArea(oMe),OBJECT_TYPE_PLACEABLE);
|
|
//SendMessageToPC(GetFirstPC(),"Pick random move:"+GetTag(oDest));
|
|
AssignCommand(oMe,ClearAllActions());
|
|
AssignCommand(oMe,ASActionMoveToObject(oDest,nRun,1.0));
|
|
DelayCommand(8.0,AssignCommand(oMe,ClearAllActions()));
|
|
DelayCommand(5.0,SetLocalInt(oMe,"nSState",0));
|
|
} // AI
|
|
break;
|
|
} // Pick nearby object and move
|
|
} // main switch
|
|
} // fnAttack()
|
|
|
|
|
|
void fnSeek(int nS)
|
|
{ // Seek
|
|
int nState=nS;
|
|
object oMe=OBJECT_SELF;
|
|
int nRun=GetLocalInt(oMe,"nRun");
|
|
object oDest=GetLocalObject(oMe,"oDestWP");
|
|
object oArea=GetArea(oMe);
|
|
object oWork;
|
|
string sID=GetLocalString(oMe,"sTeamID");
|
|
object oPC=GetLocalObject(GetModule(),"oTeamLead"+sID);
|
|
int nAlt=0;
|
|
int nTMI=0;
|
|
object oLastAt=GetLocalObject(oMe,"oLastAt");
|
|
float fDist;
|
|
int nErr;
|
|
object oPer;
|
|
int nR; // random numbers
|
|
int nSeekMethod;
|
|
switch(nState)
|
|
{ // main state
|
|
case 0: { // listen for enemies
|
|
//SendMessageToPC(oPC,"["+GetName(oMe)+"](Seek)Area:"+GetName(oArea)+" [listen for enemies]");
|
|
AssignCommand(oMe,ActionPlayAnimation(ANIMATION_LOOPING_LOOK_FAR,1.0,2.0));
|
|
oPer=GetNearestCreature(CREATURE_TYPE_PERCEPTION,PERCEPTION_HEARD_AND_NOT_SEEN,oMe,CREATURE_TYPE_REPUTATION,REPUTATION_TYPE_ENEMY,CREATURE_TYPE_IS_ALIVE,TRUE);
|
|
if (oPer==OBJECT_INVALID)
|
|
{ // OI
|
|
SetLocalInt(oMe,"nSState",1);
|
|
} // OI
|
|
else
|
|
{
|
|
if (GetAbilityScore(oMe,ABILITY_INTELLIGENCE)>9&&d6()==1) AssignCommand(oMe,ActionSpeakString("I heard something.",TALKVOLUME_WHISPER));
|
|
SetLocalObject(oMe,"oDestWP",oPer);
|
|
SetLocalInt(oMe,"nSState",3);
|
|
}
|
|
break;
|
|
} // listen for enemies
|
|
case 1: { // no enemy - so pick a destination and do some walking
|
|
//SendMessageToPC(oPC,"["+GetName(oMe)+"](Seek)Area:"+GetName(oArea)+"[Choose destination]");
|
|
nR=d20();
|
|
if (nR<19)
|
|
{ // leave area
|
|
nR=0;
|
|
oWork=fnRandomObject(oArea,OBJECT_TYPE_DOOR);
|
|
while((GetTransitionTarget(oWork)==OBJECT_INVALID||GetArea(GetTransitionTarget(oWork))==oLastAt)&&nTMI<MAX_AI_SCAN)
|
|
{ // find door to another area
|
|
nR++;
|
|
if (nR>1) nR=0;
|
|
nTMI++;
|
|
if (nR==0) oWork=fnRandomObject(oArea,OBJECT_TYPE_DOOR);
|
|
else oWork=fnRandomObject(oArea,OBJECT_TYPE_TRIGGER);
|
|
} // find door to another area
|
|
if (oWork==OBJECT_INVALID) SetLocalObject(oMe,"oLastAt",OBJECT_INVALID);
|
|
oWork=GetTransitionTarget(oWork);
|
|
if (oWork!=OBJECT_INVALID)
|
|
{ //
|
|
oWork=fnGetNearTransitionTarget(oWork);
|
|
SetLocalObject(oMe,"oDestWP",oWork);
|
|
} //
|
|
SetLocalObject(oMe,"oLastAt",GetArea(oMe));
|
|
if(d10()<2)
|
|
{ // make a comment
|
|
nR=GetAbilityScore(oMe,ABILITY_INTELLIGENCE);
|
|
if (nR>6&&nR<10) ActionSpeakString("Me leave here.");
|
|
else if (nR>9&&nR<13) ActionSpeakString("I need to go somewhere else.");
|
|
else if (nR>12) ActionSpeakString("I must be away from this region.");
|
|
} // make a comment
|
|
} // leave area
|
|
else
|
|
{ // roam in same area
|
|
nR=d4();
|
|
if (nR<3) oWork=fnRandomObject(oArea,OBJECT_TYPE_WAYPOINT);
|
|
else oWork=fnRandomObject(oArea,OBJECT_TYPE_PLACEABLE);
|
|
} // roam in same area
|
|
oDest=oWork;
|
|
//SendMessageToPC(oPC,"["+GetName(oMe)+"](Seek)Area:"+GetName(oArea)+"Destination:"+GetTag(oDest)+" in area:"+GetName(GetArea(oDest)));
|
|
SetLocalObject(oMe,"oDestWP",oDest);
|
|
SetLocalInt(oMe,"nSState",2);
|
|
} // no enemy - so pick a destination and do some walking
|
|
case 2: { // Move to destination
|
|
fDist=GetDistanceBetween(oDest,oMe);
|
|
//SendMessageToPC(oPC,"["+GetName(oMe)+"](Seek)Area:"+GetName(oArea)+"Moving to Dest:"+GetTag(oDest)+"/area:"+GetName(GetArea(oDest))+" Dist:"+FloatToString(fDist)+" GD2O:"+FloatToString(GetDistanceToObject(oDest)));
|
|
if ((fDist!=0.0&&fDist<=3.0)||(fDist==0.0&&GetArea(oDest)==GetArea(oMe)))
|
|
{ // you made it
|
|
SetLocalObject(oMe,"oDestWP",OBJECT_INVALID);
|
|
SetLocalInt(oMe,"nSState",0);
|
|
} // you made it
|
|
else
|
|
{ // still moving
|
|
nErr=fnMoveToDestination(oDest,TRUE);
|
|
if (nErr==-1)
|
|
{
|
|
SetLocalObject(oMe,"oDestWP",OBJECT_INVALID);
|
|
SetLocalInt(oMe,"nSState",0);
|
|
}
|
|
} // still moving
|
|
break;
|
|
} // move to destination
|
|
case 3: { // enemy heard/seen
|
|
//SendMessageToPC(oPC,"["+GetName(oMe)+"](Seek) An enemy was heard moving towards.");
|
|
fDist=GetDistanceBetween(oDest,oMe);
|
|
if ((GetArea(oDest)!=GetArea(oMe))||fDist>39.9||GetIsDead(oDest))
|
|
{ // they are gone
|
|
SetLocalInt(oMe,"nSState",0);
|
|
SetLocalObject(oMe,"oDestWP",OBJECT_INVALID);
|
|
} // they are gone
|
|
else
|
|
{ // moving
|
|
nErr=fnMoveToDestination(oDest,TRUE);
|
|
if (nErr==-1)
|
|
{
|
|
SetLocalObject(oMe,"oDestWP",OBJECT_INVALID);
|
|
SetLocalInt(oMe,"nSState",0);
|
|
}
|
|
} // moving
|
|
break;
|
|
} // enemy heard/seen
|
|
} // main state
|
|
} // Seek
|
|
|
|
void fnHealThem(object oHeal)
|
|
{
|
|
int nAM=GetAbilityModifier(ABILITY_CONSTITUTION,oHeal);
|
|
if (nAM<1) nAM=0;
|
|
effect eHeal=EffectHeal(1+nAM);
|
|
SetLocalInt(oHeal,"nHealing",TRUE);
|
|
ApplyEffectToObject(DURATION_TYPE_INSTANT,eHeal,oHeal,1.0);
|
|
int nCHP=GetCurrentHitPoints(oHeal);
|
|
int nMHP=GetMaxHitPoints(oHeal);
|
|
if (nMHP>nCHP&&GetLocalInt(oHeal,"nMState")==9)
|
|
DelayCommand(30.0,fnHealThem(oHeal));
|
|
else
|
|
SetLocalInt(oHeal,"nHealing",FALSE);
|
|
} // fnHeal
|
|
|
|
//----------------------------------------------- HEAL ----------------------
|
|
void fnHeal(int nS)
|
|
{ // Heal
|
|
object oMod=GetModule();
|
|
int nState=nS;
|
|
object oMe=OBJECT_SELF;
|
|
string sID=GetLocalString(oMe,"sTeamID");
|
|
object oWP=GetWaypointByTag(sID+"_HEAL");
|
|
int nCHP=GetCurrentHitPoints(oMe);
|
|
int nMHP=GetMaxHitPoints(oMe);
|
|
int nErr;
|
|
float fDist;
|
|
if (oWP==OBJECT_INVALID) ActionSpeakString("There is no waypoint labeled "+sID+"_HEAL!");
|
|
if (nMHP==nCHP)
|
|
{
|
|
nState=0;
|
|
SetLocalInt(oMe,"nSState",0);
|
|
}
|
|
switch(nState)
|
|
{ // state
|
|
case 0: { // if wounded seek out healing point
|
|
if (nCHP<nMHP)
|
|
SetLocalInt(oMe,"nSState",1);
|
|
else
|
|
{ // what to do when healed
|
|
PRCForceRest(oMe);
|
|
SetLocalInt(oMe,"nMState",2); // Guard
|
|
SetLocalObject(oMe,"oDestWP",OBJECT_INVALID);
|
|
} // what to do when healed
|
|
break;
|
|
} // if wounded seek out healing point
|
|
case 1: { // seek out healing area
|
|
fDist=GetDistanceBetween(oMe,oWP);
|
|
if ((fDist!=0.0&&fDist<8.0)||(fDist==0.0&&GetArea(oMe)==GetArea(oWP)))
|
|
{ // close enough
|
|
SetLocalInt(oMe,"nSState",2);
|
|
} // close enough
|
|
else
|
|
{
|
|
nErr=fnMoveToDestination(oWP,TRUE);
|
|
}
|
|
break;
|
|
} // seek out healing area
|
|
case 2: { // heal
|
|
if (nCHP==nMHP)
|
|
{ // healed
|
|
SetLocalInt(oMe,"nSState",0);
|
|
SetLocalInt(oMe,"nHealing",FALSE);
|
|
} // healed
|
|
else
|
|
{ // do healing
|
|
if(GetLocalInt(oMe,"nHealing")!=TRUE)
|
|
fnHealThem(oMe);
|
|
ActionPlayAnimation(ANIMATION_LOOPING_DEAD_FRONT,1.0,4.0);
|
|
} // do healing
|
|
break;
|
|
} // heal
|
|
} // state
|
|
} // fnHeal()
|
|
|
|
//----------------------------------------------- UPGRADE -------------------
|
|
void fnUpgrade()
|
|
{ // Upgrade
|
|
object oMe=OBJECT_SELF;
|
|
ExecuteScript("rts_up_setup",oMe);
|
|
object oMod=GetModule();
|
|
string sID=GetLocalString(oMe,"sTeamID");
|
|
object oPC=GetLocalObject(oMe,"oUpgradePC");
|
|
object oSpeaker=GetLocalObject(oMe,"oSpeaker");
|
|
int nS=GetLocalInt(oMe,"nSState");
|
|
int nUpTo=GetLocalInt(oMe,"nUpTo");
|
|
int nGold;
|
|
int nMana;
|
|
int nSoul;
|
|
object oNew;
|
|
string sNID;
|
|
int nWork;
|
|
location lLoc=GetLocation(oMe);
|
|
effect eVFX=EffectVisualEffect(VFX_IMP_HEAD_NATURE);
|
|
int nMHP=GetMaxHitPoints(oMe);
|
|
int nCHP=GetCurrentHitPoints(oMe);
|
|
int nDmg=nMHP-nCHP;
|
|
int nN;
|
|
int nMe=GetLocalInt(oMe,"nUnitNum");
|
|
object oDestWP=GetLocalObject(oMe,"oDestWP");
|
|
int nTotalTime=GetLocalInt(oMe,"nBornTime");
|
|
int nKills=GetLocalInt(oMe,"nKills");
|
|
object oAttacker=GetLastAttacker();
|
|
float fDist=GetDistanceBetween(oAttacker,oMe);
|
|
effect eDmg=EffectDamage(nDmg);
|
|
int nRun=GetLocalInt(oMe,"nRun");
|
|
if (oSpeaker!=oPC&&GetIsObjectValid(oSpeaker)) oPC=oSpeaker;
|
|
DeleteLocalObject(oMe,"oSpeaker");
|
|
DeleteLocalObject(oMe,"oUpgradePC");
|
|
switch (nS)
|
|
{ // state
|
|
case 0: { // initiate upgrade conversation
|
|
ActionStartConversation(oPC,"rts_upgrade",TRUE);
|
|
break;
|
|
} // initiate upgrade conversation
|
|
case 2: { // upgrade
|
|
if (GetIsDead(oAttacker)||fDist==0.0||fDist>20.0) oAttacker=OBJECT_INVALID;
|
|
ApplyEffectToObject(DURATION_TYPE_TEMPORARY,eVFX,oMe,1.0);
|
|
eVFX=EffectCutsceneGhost();
|
|
ApplyEffectToObject(DURATION_TYPE_TEMPORARY,eVFX,oMe,2.0);
|
|
eVFX=EffectCutsceneImmobilize();
|
|
ApplyEffectToObject(DURATION_TYPE_TEMPORARY,eVFX,oMe,2.0);
|
|
DelayCommand(1.5,DestroyObject(oMe));
|
|
nN=GetUnitUpgradeNumber(sID,nMe,nUpTo);
|
|
sNID=GetUnitResRef(sID,nN);
|
|
oNew=CreateObject(OBJECT_TYPE_CREATURE,sNID,lLoc);
|
|
SetLocalInt(oNew,"nUnitNum",nN);
|
|
SetLocalInt(oNew,"nRun",nRun);
|
|
if (oAttacker!=OBJECT_INVALID)
|
|
{ // set last attacker
|
|
AssignCommand(oAttacker,ClearAllActions());
|
|
AssignCommand(oAttacker,ApplyEffectToObject(DURATION_TYPE_INSTANT,eDmg,oNew,1.0));
|
|
}// set last attacker
|
|
else
|
|
ApplyEffectToObject(DURATION_TYPE_INSTANT,eDmg,oNew,1.0);
|
|
TakeResourceCost(oPC,nN,1,nMe);
|
|
DelayCommand(5.0,SetLocalInt(oNew,"nBornTime",nTotalTime)); // set original time on the upgrade
|
|
SetLocalInt(oNew,"nKills",nKills); // Set kills accurate on upgrade
|
|
SetLocalObject(oNew,"oDestWP",oDestWP) ; // preserve following, or destination
|
|
break;
|
|
} // upgrade
|
|
} // state
|
|
} // fnUpgrade()
|
|
|
|
//----------------------------------------------- FOLLOW --------------------
|
|
void fnFollow(object oD)
|
|
{ // follow
|
|
object oMe=OBJECT_SELF;
|
|
int nFD=GetLocalInt(oMe,"nFollowDist");
|
|
float fD=3.0;
|
|
float fDist=GetDistanceBetween(oD,oMe);
|
|
string sID=GetLocalString(oMe,"sTeamID");
|
|
int nRun=GetLocalInt(oMe,"nRun");
|
|
int nStray=GetLocalInt(oMe,"nParm");
|
|
int bHeld=FALSE;
|
|
object oMod=GetModule();
|
|
effect eCSP=EffectCutsceneParalyze();
|
|
effect eP=EffectParalyze();
|
|
effect eT=GetFirstEffect(oMe);
|
|
while(!bHeld&&GetIsEffectValid(eT))
|
|
{ // check each effect
|
|
if (eT==eCSP||eT==eP) bHeld=TRUE;
|
|
eT=GetNextEffect(oMe);
|
|
} // check each effect
|
|
if (nFD==1) fD=7.0;
|
|
if (nFD==2) fD=5.0;
|
|
if (GetIsDead(oD)||oD==OBJECT_INVALID)
|
|
{ // no longer someone to follow
|
|
AssignCommand(oMe,ClearAllActions());
|
|
SetLocalInt(oMe,"nMState",2); // switch to GUARD mode
|
|
SetLocalInt(oMe,"nSState",0);
|
|
SetLocalObject(OBJECT_SELF,"oDestWP",GetLocalObject(oMod,"oGUARD5"+sID));
|
|
} // no longer someone to follow
|
|
else if (GetArea(oD)!=GetArea(oMe))
|
|
{ // target is in another area
|
|
if (GetTag(GetModule())=="HarvestofSouls")
|
|
{ // Harvest of Souls
|
|
if (GetTag(GetArea(oD))=="ThePlaneofInBetween")
|
|
{ // they are in the place of death
|
|
AssignCommand(oMe,ClearAllActions());
|
|
SetLocalInt(oMe,"nMState",2); // switch to GUARD mode
|
|
SetLocalInt(oMe,"nSState",0);
|
|
SetLocalObject(OBJECT_SELF,"oDestWP",GetLocalObject(oMod,"oGUARD5"+sID));
|
|
} // they are in the place of death
|
|
else if (!bHeld)
|
|
{ // jump to them
|
|
nStray++;
|
|
SetLocalInt(oMe,"nParm",nStray);
|
|
if (nStray>2)
|
|
{ // jump
|
|
AssignCommand(oMe,ClearAllActions());
|
|
AssignCommand(oMe,JumpToObject(oD));
|
|
} // jump
|
|
else
|
|
{ // run
|
|
if (GetDistanceBetween(oMe,oD)>fD||GetArea(oMe)!=GetArea(oD))
|
|
{ // move
|
|
AssignCommand(oMe,ASActionMoveToObject(oD,TRUE,fD));
|
|
} // move
|
|
} // run
|
|
} // jump to them
|
|
} // Harvest of Souls
|
|
else if (!bHeld)
|
|
{ // teleport to whom you are supposed to follow
|
|
nStray++;
|
|
SetLocalInt(oMe,"nParm",nStray);
|
|
if (nStray>2)
|
|
{ // jump
|
|
AssignCommand(oMe,ClearAllActions());
|
|
AssignCommand(oMe,JumpToObject(oD));
|
|
} // jump
|
|
else
|
|
{ // run
|
|
if (GetDistanceBetween(oMe,oD)>fD||GetArea(oMe)!=GetArea(oD))
|
|
{ // move
|
|
AssignCommand(oMe,ASActionMoveToObject(oD,TRUE,fD));
|
|
} // move
|
|
} // run
|
|
} // teleport to whom you are supposed to follow
|
|
} // target is in another area
|
|
else if (!bHeld)
|
|
{ // business as usual
|
|
SetLocalInt(oMe,"nParm",0);
|
|
if (fDist!=0.0&&fDist>=fD)
|
|
{
|
|
if (fDist>(fD*2.0)) nRun=TRUE; // RUN
|
|
if (GetDistanceBetween(oMe,oD)>fD||GetArea(oMe)!=GetArea(oD))
|
|
{ // move
|
|
AssignCommand(oMe,ASActionMoveToObject(oD,nRun,fD));
|
|
} // move
|
|
}
|
|
else if (fDist<(fD*0.75))
|
|
{ // too close
|
|
AssignCommand(oMe,ClearAllActions());
|
|
} // too close
|
|
} // business as usual
|
|
} // fnFollow
|
|
|
|
|
|
int fnNotTransition(object oD)
|
|
{ // check for transition
|
|
if (GetTransitionTarget(oD)!=OBJECT_INVALID) return TRUE;
|
|
return FALSE;
|
|
} // fnNotTransition
|
|
|
|
|
|
void fnGuardBase()
|
|
{ // Guard Base
|
|
object oMe=OBJECT_SELF;
|
|
int nS=GetLocalInt(oMe,"nSState");
|
|
object oDest=GetLocalObject(oMe,"oDestWP");
|
|
string sID=GetLocalString(oMe,"sTeamID");
|
|
int nErr;
|
|
int nC=0;
|
|
float fDist;
|
|
object oBase=GetWaypointByTag(sID+"_START");
|
|
switch(nS)
|
|
{ // main switch
|
|
case 0: { // are we in our own base
|
|
if (GetDistanceToObject(oBase)!=-1.0)
|
|
{ // we're home
|
|
nS=1;
|
|
SetLocalInt(oMe,"nSState",1);
|
|
} // we're home
|
|
else
|
|
{
|
|
nErr=fnMoveToDestination(oBase,TRUE);
|
|
}
|
|
break;
|
|
} // are we in our own base
|
|
case 1: { // choose a destination
|
|
nC=d12();
|
|
if (nC==1)
|
|
{ // seek vault
|
|
oDest=GetWaypointByTag(sID+"_VAULT");
|
|
} // seek vault
|
|
else if (nC==2)
|
|
{ // seek throne
|
|
oDest=GetWaypointByTag(sID+"_START");
|
|
} // seek throne
|
|
else if (nC==3)
|
|
{ // seek heal
|
|
oDest=GetWaypointByTag(sID+"_HEAL");
|
|
} // seek heal
|
|
else if (nC==4)
|
|
{ // LD1
|
|
oDest=GetWaypointByTag(sID+"_LD1");
|
|
} // LD1
|
|
else if (nC==5)
|
|
{ // LD2
|
|
oDest=GetWaypointByTag(sID+"_LD2");
|
|
} // LD2
|
|
else if (nC==6)
|
|
{ // seek random
|
|
if (d4()>2)
|
|
oDest=GetNearestObject(OBJECT_TYPE_WAYPOINT,oMe,d20());
|
|
else
|
|
oDest=GetNearestObject(OBJECT_TYPE_PLACEABLE,oMe,d20());
|
|
} // seek random
|
|
else
|
|
{ // animate
|
|
AssignCommand(oMe,ActionPlayAnimation(ANIMATION_LOOPING_LOOK_FAR,1.0,3.0));
|
|
} // animate
|
|
//SendMessageToPC(GetFirstPC(),"["+GetName(oMe)+"]Selected Destination:"+GetTag(oDest));
|
|
SetLocalObject(oMe,"oDestWP",oDest);
|
|
SetLocalInt(oMe,"nSState",2);
|
|
} // choose a destination
|
|
case 2: { // move to destination
|
|
fDist=GetDistanceToObject(oDest);
|
|
//SendMessageToPC(GetFirstPC(),"["+GetName(oMe)+"]Moving to Object:"+GetTag(oDest)+" Dist:"+FloatToString(fDist));
|
|
if (fDist!=-1.0&&fDist>3.0)
|
|
{ // keep moving
|
|
nErr=fnMoveToDestination(oDest,TRUE);
|
|
/*if (nErr=-1)
|
|
{ // error
|
|
SendMessageToPC(GetFirstPC(),"nErr==-1 for ["+GetName(oMe)+"]");
|
|
SetLocalInt(oMe,"nSState",1);
|
|
} */ // error
|
|
} // keep moving
|
|
else
|
|
{ // pick new destination
|
|
SetLocalInt(oMe,"nSState",1);
|
|
} // pick new destination
|
|
break;
|
|
} // move to destination
|
|
} // main switch
|
|
} // fnGuardBase();
|
|
|
|
void fnLightSensitive()
|
|
{ // light sensitivity
|
|
float fDur=IntToFloat(GetLocalInt(GetModule(),"nAIDelay"));
|
|
effect eAttackD=EffectAttackDecrease(2);
|
|
ApplyEffectToObject(DURATION_TYPE_TEMPORARY,eAttackD,OBJECT_SELF,fDur);
|
|
object oMe=OBJECT_SELF;
|
|
if (GetAbilityScore(oMe,ABILITY_INTELLIGENCE)>9&&d20()==1)
|
|
AssignCommand(oMe,ActionSpeakString("This sunlight is horrible!"));
|
|
} // fnLightSensitive()
|
|
|
|
void fnBurning()
|
|
{ // burning in the sun
|
|
effect eDmg=EffectDamage(d20(),DAMAGE_TYPE_FIRE,DAMAGE_POWER_PLUS_FIVE);
|
|
effect eBurn=EffectVisualEffect(VFX_IMP_FLAME_M);
|
|
ApplyEffectToObject(DURATION_TYPE_INSTANT,eDmg,OBJECT_SELF,1.0);
|
|
ApplyEffectToObject(DURATION_TYPE_TEMPORARY,eBurn,OBJECT_SELF,4.0);
|
|
} // fnBurning()
|
|
|
|
|
|
void fnFleeSunlight()
|
|
{ // flee the sun
|
|
object oMe=OBJECT_SELF;
|
|
object oDest=GetLocalObject(oMe,"oDestWP");
|
|
int nS=GetLocalInt(oMe,"nSState");
|
|
float fDist;
|
|
int nErr;
|
|
switch(nS)
|
|
{ // Main switch
|
|
case 0: { // speak and set destination
|
|
ClearAllActions();
|
|
if (d6()<4) ActionSpeakString("The sun is rising and I must seek cover.");
|
|
else ActionSpeakString("I can feel the coming of the day and must flee.");
|
|
oDest=fnRandomObject(GetArea(oMe),OBJECT_TYPE_DOOR);
|
|
if (GetTransitionTarget(oDest)!=OBJECT_INVALID)
|
|
{ // test it
|
|
oDest=fnExposed(oDest);
|
|
} // test it
|
|
else
|
|
{
|
|
oDest=OBJECT_INVALID;
|
|
}
|
|
if (oDest==OBJECT_INVALID)
|
|
{ // check a trigger
|
|
oDest=fnRandomObject(GetArea(oMe),OBJECT_TYPE_TRIGGER);
|
|
if (GetTransitionTarget(oDest)!=OBJECT_INVALID)
|
|
{ // test it
|
|
oDest=fnExposed(oDest);
|
|
} // test it
|
|
else
|
|
{
|
|
oDest=OBJECT_INVALID;
|
|
}
|
|
} // check a trigger
|
|
SetLocalObject(oMe,"oDestWP",oDest);
|
|
if (oDest!=OBJECT_INVALID)
|
|
SetLocalInt(oMe,"nSState",1); // move
|
|
break;
|
|
} // speak and set destination
|
|
case 1: { // move
|
|
fDist=GetDistanceToObject(oDest);
|
|
if (fDist==-1.0||fDist>4.0)
|
|
{ // move
|
|
SetLocalInt(oMe,"nRun",TRUE);
|
|
nErr=fnMoveToDestination(oDest,TRUE);
|
|
} // move
|
|
else
|
|
{ // should be there
|
|
SetLocalInt(oMe,"nSState",2);
|
|
} // should be there
|
|
break;
|
|
} // move
|
|
case 2: { // wait for dusk - Go to GUARD LAIR at Dusk
|
|
if (GetIsDusk()||GetIsNight())
|
|
{ // switch to Guard Lair
|
|
ClearAllActions();
|
|
SetLocalInt(oMe,"nMState",11);
|
|
SetLocalInt(oMe,"nSState",0);
|
|
} // switch to Guard Lair
|
|
else
|
|
ActionPlayAnimation(ANIMATION_LOOPING_DEAD_FRONT,1.0,8.0);
|
|
break;
|
|
} // wait for dusk - go to GUARD LAIR at Dusk
|
|
} // main switch
|
|
} // flee the sun
|
|
|
|
void fnRecon()
|
|
{ // reconnoiter
|
|
object oMe=OBJECT_SELF;
|
|
int nParm=GetLocalInt(oMe,"nParm");
|
|
float fAheadDist=3.0;
|
|
float fDX;
|
|
float fDY;
|
|
float fDZ;
|
|
float fMX=0.0;
|
|
float fMY=0.0;
|
|
string sID=GetLocalString(oMe,"sTeamID");
|
|
object oMod=GetModule();
|
|
object oLead=GetLocalObject(oMe,"oDestWP"); // who to follow
|
|
location lRecon;
|
|
float fFacing=GetFacing(oLead);
|
|
vector vVecPC=GetPosition(oLead);
|
|
float fLX=vVecPC.x;
|
|
float fLY=vVecPC.y;
|
|
float fLZ=vVecPC.z;
|
|
vector vNewVec;
|
|
int nRun=GetLocalInt(oMe,"nRun");
|
|
float fDist=GetDistanceBetween(oLead,oMe);
|
|
int nReconV=GetLocalInt(oMe,"nRecon");
|
|
if (GetIsInCombat(oMe)!=TRUE&&IsInConversation(oMe)!=TRUE)
|
|
{ // not talking or fighting
|
|
//SendMessageToPC(GetFirstPC(),GetName(oMe)+" called fnRecon()");
|
|
if ((fDist!=0.0&&GetArea(oLead)==GetArea(oMe))&&(nReconV!=TRUE))
|
|
SetLocalInt(oMe,"nRecon",TRUE);
|
|
if (GetLocalInt(oMe,"nRecon")==TRUE)
|
|
{ // was near object when recon initiated
|
|
if (oLead!=OBJECT_INVALID)
|
|
{ // valid leader
|
|
if (nParm==2) fAheadDist=7.0;
|
|
else if (nParm==3) fAheadDist=10.0;
|
|
else if (nParm==4) fAheadDist=15.0;
|
|
else if (nParm==5) fAheadDist=20.0;
|
|
else if (nParm==6) fAheadDist=30.0;
|
|
//SendMessageToPC(GetFirstPC(),"["+GetName(oMe)+"]Recon:("+FloatToString(fLX)+","+FloatToString(fLY)+","+FloatToString(fLZ)+") Ahead Dist:"+FloatToString(fAheadDist));
|
|
if (fFacing>335.0||fFacing<25.0)
|
|
{ // east
|
|
fMX=1.0;
|
|
} // east
|
|
else if (fFacing>291.0&&fFacing<335.1)
|
|
{ // south east
|
|
fMX=0.75;
|
|
fMY=-0.75;
|
|
} // south east
|
|
else if (fFacing>250.0&&fFacing<291.1)
|
|
{ // south
|
|
fMY=-1.0;
|
|
} // south
|
|
else if (fFacing>200.0&&fFacing<250.1)
|
|
{ // south west
|
|
fMX=-0.75;
|
|
fMY=-0.75;
|
|
} // south west
|
|
else if (fFacing>160.0&&fFacing<200.1)
|
|
{ // west
|
|
fMX=-1.0;
|
|
} // west
|
|
else if (fFacing>110.0&&fFacing<160.1)
|
|
{ // north west
|
|
fMX=-0.75;
|
|
fMY=0.75;
|
|
} // north west
|
|
else if (fFacing>70.0&&fFacing<110.1)
|
|
{ // north
|
|
fMY=1.0;
|
|
} // north
|
|
else
|
|
{ // north east
|
|
fMY=0.75;
|
|
fMX=0.75;
|
|
} // north east
|
|
fDX=fLX+(fMX*fAheadDist);
|
|
fDY=fLY+(fMY*fAheadDist);
|
|
fDZ=fLZ;
|
|
if (fDX<0.0) fDX=0.0;
|
|
if (fDY<0.0) fDY=0.0;
|
|
//fnDebug(" MULTIPLIERS: X:"+FloatToString(fMX)+" Y:"+FloatToString(fMY));
|
|
//fnDebug(" Facing:"+FloatToString(fFacing)+" Recon to:"+FloatToString(fDX)+","+FloatToString(fDY)+","+FloatToString(fDZ));
|
|
vNewVec=Vector(fDX,fDY,fDZ);
|
|
//vNewVec=VectorNormalize(vNewVec);
|
|
if (GetArea(oLead)!=GetArea(oMe))
|
|
{ // jump to leader
|
|
AssignCommand(oMe,ClearAllActions());
|
|
AssignCommand(oMe,JumpToObject(oLead));
|
|
} // jump to leader
|
|
else
|
|
{ // same area
|
|
lRecon=Location(GetArea(oMe),vNewVec,fFacing);
|
|
AssignCommand(oMe,ClearAllActions());
|
|
AssignCommand(oMe,ActionMoveToLocation(lRecon,nRun));
|
|
} // same area
|
|
} // valid leader
|
|
} // was near object when recon initiated
|
|
else
|
|
{ // cannot recon on that unit - prevent cheating
|
|
if(GetAbilityScore(oMe,ABILITY_INTELLIGENCE)>7)
|
|
AssignCommand(oMe,SpeakString("I do not see that recon leader here. I therefor can no longer recon."));
|
|
SetLocalInt(oMe,"nMState",2); // Go into guard mode
|
|
SetLocalInt(oMe,"nSState",0);
|
|
SetLocalObject(oMe,"oDestWP",GetLocalObject(oMod,"oGUARD4"+sID));
|
|
} // cannot recon on that unit - prevent cheating
|
|
} // not fighting or talking
|
|
else
|
|
{ // fighting or talking
|
|
|
|
} // fighting or talking
|
|
} // fnRecon()
|