//////////////////////////////////////////////////////////////////////////// // 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 (nCHP1) 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)&&nTMI1) 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 (nCHP20.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()