$debugSwitch[onStuck] = false; $debugSwitch[unBlock] = false; $debugSwitch[checkForThreat] = false; $debugSwitch[DoScan] = false; $debugSwitch[openFire] = false; $debugSwitch[ceaseFire] = false; $debugSwitch[onTargetEnterLOS] = false; $debugSwitch[onTargetExitLOS] = false; $debugSwitch[spawn] = false; $debugSwitch[Equip] = false; $debugSwitch[GetTargetRange] = false; $debugSwitch[getClosestEnemy] = false; $debugSwitch[CreateBots] = false; $debugSwitch[pickAISpawn] = false; $debugSwitch[GetBearing] = false; $debugSwitch[GetHeading] = false; $debugSwitch[GetRelativeBearing] = false; $debugSwitch[CheckArcOfSight] = false; $debugSwitch[Misc] = false; $ARC_OF_SIGHT = 120; $MIN_SCAN_GAP = 1000; $MAX_SCAN_GAP = 20000; $MIN_TRIGGER_HOLD = 100; $MAX_TRIGGER_HOLD = 200; $MIN_ITCHY_FINGER = 2000; $MAX_ITCHY_FINGER = 10000; $MAX_THREAT_ENGAGE_RANGE = 100; $MAX_AGGRESSIVENESS = 100; $MAX_ATTENTION_LEVEL = 100; $MAX_ALERTNESS = 100; $STATIONARY = 0.0; $cardinalDirection[0] = "0 10000 0"; // N $cardinalDirection[1] = "6000 6000 0"; // NE $cardinalDirection[2] = "10000 0 0"; // E $cardinalDirection[3] = "6000 -6000 0"; // SE $cardinalDirection[4] = "0 -10000 0"; // S $cardinalDirection[5] = "-6000 -6000 0";// SW $cardinalDirection[6] = "-6000 0 0"; // W $cardinalDirection[7] = "-6000 6000 0"; // NW $NUM_CARDINALS = 8; datablock PlayerData(AIGuardDB : LightMaleHumanArmor) { className = "NPC"; maxInv[CrossbowAmmo] = 500000; }; function AIGuardDB::checkForThreat(%this,%obj) { DebugPrint( "%this:"@%this@"~AIGuardDB::checkForThreat (from:"@%obj@")", "checkForThreat"); // this code used to make the guard fire // modified by Maxim from Finney text on 3/12/06 if(!isObject(%obj)) return; %idx = %obj.getClosestEnemy(); if (%idx < 0) return 0; %target = ClientGroup.getObject( %idx ); if ( !%obj.CheckArcOfSight(%target.player) ) %testRange = %obj.range / 2; else return; if ( %obj.GetTargetRange(%target.player) < %testRange) { return %target.player; } DebugPrint( "no threat (from:"@%obj@")", "checkForThreat"); return 0; } function AIGuardDB::DoScan(%this,%obj) { DebugPrint("%this:"@%this@"~AIGuardDB::DoScan (from:"@%obj@")", "DoScan"); if(!isObject(%obj)) return; cancel(%this.scheduledCheck); if (%obj.attentionLevel<=0) // if attentionLevel is non-zero, keep looking in same direction %obj.attentionLevel=0; else %obj.attentionLevel--; if (%obj.attentionLevel==0) // if attentionLevel is non-zero, keep looking in same direction { %look = getRandom($NUM_CARDINALS-1); if (%this.look != %look) %this.look = %look; else { %this.look = %look + 1; if (%this.look < $NUM_CARDINALS-1) %this.look++; else %this.look = 0; } } %obj.setAimLocation($cardinalDirection[%this.look]); if ( (%tgtPlayer = %this.checkForThreat(%obj)) != 0) { if (%obj.currentTarget) { if (%obj.currentTarget==%tgtPlayer) { DebugPrint( "STILL A THREAT (from:"@%obj@")", "DoScan"); %obj.setAimObject( %tgtPlayer ); %obj.attentionLevel = %obj.attention; } else { DebugPrint( "CHANGED THREAT (from:"@%obj@")", "DoScan"); %obj.currentTarget = %tgtPlayer; %obj.setAimObject( %obj.currentTarget ); } } else { DebugPrint( "NEW THREAT!! (from:"@%obj@")", "DoScan"); %obj.setAimObject( %tgtPlayer ); %obj.currentTarget = %tgtPlayer; %obj.attentionLevel = %obj.attention; } } else { if (%obj.getAimObject) { %obj.clearAim(); DebugPrint( "> %obj.clearAim (from:"@%obj@")", "DoScan"); %obj.currentTarget = 0; // forget this target } %this.nextScan = %this.schedule($MIN_SCAN_GAP+getRandom($MAX_SCAN_GAP/%this.alertness), "doScan", %obj); } } function AIGuardDB::onTargetEnterLOS(%this,%obj) { // If an aim target object is set, this method is invoked when // that object becomes visible. DebugPrint( "%this:"@%this@"~AIGuardDB::onTargetEnterLOS LOS TARGET ! (from:"@%obj@")", "onTargetEnterLOS"); if(!isObject(%obj)) return; %obj.attentionLevel = %this.attention; %this.schedule($MIN_ITCHY_FINGER+getRandom($MAX_ITCHY_FINGER), "pauseFire", %obj); %this.schedule($MIN_SCAN_GAP+getRandom($MAX_SCAN_GAP/%this.alertness), "doScan", %obj); %obj.setImageTrigger(0,true); } function AIGuardDB::onTargetExitLOS(%this,%obj) { // If an aim target object is set, this method is invoked when // the object is no longer visible. DebugPrint( "%this:"@%this@"~AIGuardDB::onTargetExitLOS Fuhgetaboutit (from:"@%obj@")", onTargetExitLOS); if(!isObject(%obj)) return; %obj.setImageTrigger(0,false); %obj.clearAim(); DebugPrint( "> %obj.clearAim (from:"@%obj@")", "onTargetExitLOS"); %obj.currentTarget = 0; // forget this target %this.schedule($MIN_SCAN_GAP, "doScan", %obj); } function AIGuardDB::pauseFire(%this,%obj) { DebugPrint("%this:"@%this@"~AIGuardDB::ceaseFire (from:"@%obj@")", pauseFire); if(!isObject(%obj)) return; %obj.setImageTrigger(0,false); %this.schedule($MIN_TRIGGER_HOLD+getRandom($MAX_TRIGGER_HOLD), "ceaseFire", %obj); } function AIGuardDB::ceaseFire(%this,%obj) { DebugPrint( "%this:"@%this@"~AIGuardDB::ceaseFire (from:"@%obj@")", ceaseFire); if(!isObject(%obj)) return; %obj.setImageTrigger(0,false); %obj.clearAim(); %obj.currentTarget = 0; // forget this target } function spawnbot(%index,%role) { DebugPrint( "%index:"@%index@"%role:"@%role@"~", spawnbot); %me = new AIPlayer() { dataBlock = AIGuardDB; aiPlayer = true; }; MissionCleanup.add(%me); AIGroup.add(%me); %me.setAimObject( 0 ); %me.look = 0; %me.range = 100; %spawn=pickAISpawn(%index,%role); %me.range = %spawn.range < $MAX_THREAT_ENGAGE_RANGE ? %spawn.range : $MAX_THREAT_ENGAGE_RANGE; %me.attention = %spawn.attention < $MAX_ATTENTION_LEVEL ? %spawn.attention/5 : $MAX_ATTENTION_LEVEL/5; %me.alertness = %spawn.alertness < $MAX_ALERTNESS ? %spawn.alertness/10 : $MAX_ALERTNESS/10; %me.aggression = %spawn.aggressiveness < $MAX_AGGRESSIVENESS ? %spawn.aggressiveness * 0.015 : $MAX_AGGRESSIVENESS * 0.015; %me.index = %index; %me.setTransform(%spawn.getTransform()); %me.setEnergyLevel(60); %me.role = %role; %me.setShapeName(%me.getName() SPC %me.role); %me.attentionLevel = 0; %me.nextBlockCheck = 0; %me.conformToGround = 0; %me.Equip(CrossBow,CrossBowAmmo); %me.setMoveSpeed($STATIONARY); echo("Added [" SPC %me SPC "] :" SPC %me.role SPC "#" SPC %me.index ); %me.setAimLocation( $cardinalDirection[%me.look]); %me.getDataBlock().schedule(2000, "doScan", %me); return %me; } function AIPlayer::Equip(%this,%weaponDBName,%ammoDBName) { DebugPrint( "%this:"@%this@"~AIPlayer::Equip", Equip); %weapon = new Item() { dataBlock = %weaponDBName; }; %ammo = new Item() { dataBlock = %ammoDBName; }; DebugPrint("weapon:"@%weaponDBName, "Equip"); DebugPrint("ammo:"@%ammoDBName, "Equip"); MissionCleanup.add(%weapon); MissionCleanup.add(%ammo); %weaponImageName = %weaponDBName @"Image"; %this.mountImage(%weaponImageName,0); %this.setInventory(%ammoDBName,1000); %this.use(%weaponDBName); } function AIPlayer::GetTargetRange(%this, %target) { DebugPrint( "%this:"@%this@"~AIPlayer::GetTargetRange", GetTargetRange); %tgtPos = %target.getPosition(); %eyePoint = %this.getWorldBoxCenter(); %distance = VectorDist(%tgtPos, %eyePoint); DebugPrint("Actual range to target: " @ %distance , GetTargetRange); return %distance; } function AIPlayer::getClosestEnemy(%this) { DebugPrint( "%this:"@%this@"~AIPlayer::getClosestEnemy", getClosestEnemy); %index = -1; %botPos = %this.getPosition(); %count = ClientGroup.getCount(); for(%i = 0; %i < %count; %i++) { %client = ClientGroup.getObject(%i); if (%client.player $= "" || %client.player == 0 ) return -1; %playPos = %client.player.getPosition(); %tempDist = VectorDist(%playPos, %botPos); if(%i == 0) { %distance = %tempDist; %index = %i; } else { if(%distance > %tempDist) { %distance = %tempDist; %index = %i; } } } return %index; } function CreateBots() { new SimSet (AIGroup); warn("# # # # # # # # # # # # Creating NPC characters # # # # # # # # # # # # "); if ( (%role=nameToID("MissionGroup/AIDropPoints/Guard")) >= 0 ) { %count=%role.getCount(); for ( %i = 0; %i < %count; %i++) { spawnbot(%i,"Guard"); } } } function pickAISpawn(%index,%role) { %groupName = "MissionGroup/AIDropPoints/" @ %role; %group = nameToID(%groupName); if (%group != -1) { %count = %group.getCount(); if (%count != 0) { // %index = getRandom(%count-1); %spawn = %group.getObject(%index); return %spawn; } else DebugPrint("No spawn points found in " @ %groupName, "pickAISpawn"); } else DebugPrint("Missing spawn points group " @ %groupName, "pickAISpawn"); return %spawn; } // Return the bearing angle of a target's position (%there) // from an object's position (%here) function AIPlayer::GetBearing(%this, %that) { DebugPrint("%this:"@%this@"~AIPlayer::GetBearing",GetBearing,AI_Whisper); %here = %this.getPosition(); %there = %that.getPosition(); %xHere = getWord(%here,0); %yHere = getWord(%here,1); DebugPrint("xhere:"@%xHere SPC "yHere"@%yHere,GetBearing,AI_Whisper); %xThere = getWord(%there,0); %yThere = getWord(%there,1); DebugPrint("xThere:"@%xThere SPC "yThere"@%yThere,GetBearing,AI_Whisper); %x = %xThere - %xHere; DebugPrint("x:"@%x,AI_Whisper); %y = %yThere - %yHere; DebugPrint("y:"@%y,AI_Whisper); if (%x!=0 ) { %slope = %y/%x; DebugPrint("slope:"@%slope,GetBearing,AI_Whisper); %angle=mRadToDeg(mATan(%slope,-1) );//-1 is only 2nd arg that works DebugPrint("Angle in:" SPC %angle,GetBearing,AI_WhisperLoud); } else { DebugPrint("vertical",GetBearing,AI_Whisper); %angle = 90.000; } if( (%x>=0) && (%y>=0) ) // target in quadrant 1, 0-89.999 degrees %adjustment = -90; else if( (%x>=0) && (%y<0) ) //quadrant 2, 90-179.999 degrees %adjustment = 270; else if( (%x<0) && (%y<0) ) // quadrant 3, 180-269.999 degrees %adjustment = 90; else //quadrant 4, 270-359.999 degrees %adjustment = 90+360; %angle += %adjustment; return %angle; } function AIPlayer::GetHeading(%this) { DebugPrint("%this:"@%this@"~AIPlayer::GetHeading",GetHeading,AI_Whisper); %hdg = GetWord( %this.rotation,3); if (GetWord( %this.rotation,2)$="-1") %hdg = 360-%hdg; return %hdg; } function AIPlayer::GetRelativeBearing(%this,%that) { DebugPrint("%this:"@%this@"~AIPlayer::GetRelativeBearing",GetRelativeBearing,AI_Whisper); %azimuth = %this.GetBearing(%that); DebugPrint("ANGLE:"@%azimuth,AI_Alert); %heading = %this.GetHeading(); DebugPrint("HEADING:"@%heading,GetRelativeBearing,AI_Alert); if (%heading >= %azimuth) { %bearing = %heading - %azimuth; %bearing *= -1; } else { %bearing = %azimuth - %heading; } DebugPrint("BEARING:"@%bearing,GetRelativeBearing,AI_Alert); return %bearing; } function AIPlayer::CheckArcOfSight(%this,%that) { DebugPrint("%this:"@%this@"~AIPlayer::CheckArcOfSight",CheckArcOfSight,AI_Whisper); DebugPrint("%that:"@%that,GetRelativeBearing,AI_Whisper); %relbearing = %this.GetRelativeBearing(%this,%that); DebugPrint("relbearing:"@%relbearing,CheckArcOfSight,AI_Alert); if ( (%relbearing > -($ARC_OF_SIGHT/2)) && (%relbearing < -($ARC_OF_SIGHT/2)) ) %result = true; else %result = false; DebugPrint("result:"@%result,CheckArcOfSight,AI_Alert); return %result; } function showDebug(%which) { echo($debugSwitch[%which]); } function showAllDebug() { echo("onStuck:"@$debugSwitch[onStuck]); echo("unBlock:"@$debugSwitch[unBlock]); echo("checkForThreat:"@$debugSwitch[checkForThreat]); echo("DoScan:"@$debugSwitch[DoScan]); echo("openFire:"@$debugSwitch[openFire]); echo("ceaseFire:"@$debugSwitch[ceaseFire]); echo("onTargetEnterLOS:"@$debugSwitch[onTargetEnterLOS]); echo("onTargetExitLOS:"@$debugSwitch[onTargetExitLOS]); echo("spawn:"@$debugSwitch[spawn]); echo("Equip:"@$debugSwitch[Equip]); echo("GetTargetRange:"@$debugSwitch[GetTargetRange]); echo("getClosestEnemy:"@$debugSwitch[getClosestEnemy]); echo("CreateBots:"@$debugSwitch[CreateBots]); echo("pickAISpawn:"@$debugSwitch[pickAISpawn]); echo("GetBearing:"@$debugSwitch[GetBearing]); echo("GetHeading:"@$debugSwitch[GetHeading]); echo("GetRelativeBearing:"@$debugSwitch[GetRelativeBearing]); echo("CheckArcOfSight:"@$debugSwitch[CheckArcOfSight]); echo("Misc:"@$debugSwitch[Misc]); } function clearAllDebug() { $debugSwitch[onStuck] = false; $debugSwitch[unBlock] = false; $debugSwitch[checkForThreat] = false; $debugSwitch[DoScan] = false; $debugSwitch[openFire] = false; $debugSwitch[ceaseFire] = false; $debugSwitch[onTargetEnterLOS] = false; $debugSwitch[onTargetExitLOS] = false; $debugSwitch[spawn] = false; $debugSwitch[Equip] = false; $debugSwitch[GetTargetRange] = false; $debugSwitch[getClosestEnemy] = false; $debugSwitch[CreateBots] = false; $debugSwitch[pickAISpawn] = false; $debugSwitch[GetBearing] = false; $debugSwitch[GetHeading] = false; $debugSwitch[GetRelativeBearing] = false; $debugSwitch[CheckArcOfSight] = false; $debugSwitch[Misc] = false; } function setAllDebug() { $debugSwitch[onStuck] = true; $debugSwitch[unBlock] = true; $debugSwitch[checkForThreat] = true; $debugSwitch[DoScan] = true; $debugSwitch[openFire] = true; $debugSwitch[ceaseFire] = true; $debugSwitch[onTargetEnterLOS] = true; $debugSwitch[onTargetExitLOS] = true; $debugSwitch[spawn] = true; $debugSwitch[Equip] = true; $debugSwitch[GetTargetRange] = true; $debugSwitch[getClosestEnemy] = true; $debugSwitch[CreateBots] = true; $debugSwitch[pickAISpawn] = true; $debugSwitch[GetBearing] = true; $debugSwitch[GetHeading] = true; $debugSwitch[GetRelativeBearing] = true; $debugSwitch[CheckArcOfSight] = true; $debugSwitch[Misc] = true; } function DebugPrint(%theMsg,%facility,%priority) { if ($debugSwitch[%facility]) { %theMsg = %priority@"~~~"@%theMsg; if (%priority $= "") { echo(%theMsg); } else { switch$ (%priority) { case "AI_Panic": error("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); error("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); error(%theMsg); error("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); error("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); case "AI_Alert": error(%theMsg); case "AI_AlertLoud": error("~~~~~~~~~~~~~~" SPC %theMsg SPC "~~~~~~~~~~~~~~"); case "AI_Normal": echo(%theMsg); case "AI_NormalLoud": echo("~~~~~~~~~~~~~~" SPC %theMsg SPC "~~~~~~~~~~~~~~"); case "AI_Whisper": warn(%theMsg); case "AI_WhisperLoud": warn("~~~~~~~~~~~~~~" SPC %theMsg SPC "~~~~~~~~~~~~~~"); default: echo(%theMsg); } } } }