//----------------------------------------------------------------------------- // Copyright (c) 2012 GarageGames, LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to // deal in the Software without restriction, including without limitation the // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or // sell copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // AIPlayer callbacks // The AIPlayer class implements the following callbacks: // // PlayerData::onStop(%this,%obj) // PlayerData::onMove(%this,%obj) // PlayerData::onReachDestination(%this,%obj) // PlayerData::onMoveStuck(%this,%obj) // PlayerData::onTargetEnterLOS(%this,%obj) // PlayerData::onTargetExitLOS(%this,%obj) // PlayerData::onAdd(%this,%obj) // // Since the AIPlayer doesn't implement it's own datablock, these callbacks // all take place in the PlayerData namespace. //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // Demo Pathed AIPlayer. //----------------------------------------------------------------------------- function DemoPlayer::onReachDestination(%this,%obj) { //echo( %obj @ " onReachDestination" ); // Moves to the next node on the path. // Override for all player. Normally we'd override this for only // a specific player datablock or class of players. if (%obj.path !$= "") { if (%obj.currentNode == %obj.targetNode) %this.onEndOfPath(%obj,%obj.path); else %obj.moveToNextNode(); } } function DemoPlayer::onMoveStuck(%this,%obj) { //echo( %obj @ " onMoveStuck" ); } function DemoPlayer::onTargetExitLOS(%this,%obj) { //echo( %obj @ " onTargetExitLOS" ); } function DemoPlayer::onTargetEnterLOS(%this,%obj) { //echo( %obj @ " onTargetEnterLOS" ); } function DemoPlayer::onEndOfPath(%this,%obj,%path) { %obj.nextTask(); } function DemoPlayer::onEndSequence(%this,%obj,%slot) { echo("Sequence Done!"); %obj.stopThread(%slot); %obj.nextTask(); } //----------------------------------------------------------------------------- // AIPlayer static functions //----------------------------------------------------------------------------- function AIPlayer::spawnAtLocation(%name, %spawnPoint) { // Create the demo player object %player = new AiPlayer() { dataBlock = DemoPlayer; path = ""; }; MissionCleanup.add(%player); %player.setShapeName(%name); %player.setTransform(%spawnPoint); return %player; } function AIPlayer::spawnOnPath(%name, %path) { // Spawn a player and place him on the first node of the path if (!isObject(%path)) return 0; %node = %path.getObject(0); %player = AIPlayer::spawnAtLocation(%name, %node.getTransform()); return %player; } //----------------------------------------------------------------------------- // AIPlayer methods //----------------------------------------------------------------------------- function AIPlayer::followPath(%this,%path,%node) { // Start the player following a path if (!isObject(%path)) { %this.path = ""; return; } if (%node > %path.getCount() - 1) %this.targetNode = %path.getCount() - 1; else %this.targetNode = %node; if (%this.path $= %path) %this.moveToNode(%this.currentNode); else { %this.path = %path; %this.moveToNode(0); } } function AIPlayer::moveToNextNode(%this) { %pathNodeCount=%this.path.getCount(); %slowdown=0; %targetNode=%this.currentNode + 1; if (%this.path.isLooping) { %targetNode %= %pathNodeCount; } else { if (%targetNode >= %pathNodeCount-1) { %targetNode=%pathNodeCount-1; if (%currentNode < %targetNode) %slowdown=1; } } %this.moveToNode(%targetNode, %slowdown); } function AIPlayer::moveToNode(%this,%index,%slowdown) { // Move to the given path node index %this.currentNode = %index; %node = %this.path.getObject(%index); %this.setMoveDestination(%node.getTransform(),%slowdown); } //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- function AIPlayer::pushTask(%this,%method) { if (%this.taskIndex $= "") { %this.taskIndex = 0; %this.taskCurrent = -1; } %this.task[%this.taskIndex] = %method; %this.taskIndex++; if (%this.taskCurrent == -1) %this.executeTask(%this.taskIndex - 1); } function AIPlayer::clearTasks(%this) { %this.taskIndex = 0; %this.taskCurrent = -1; } function AIPlayer::nextTask(%this) { if (%this.taskCurrent != -1) if (%this.taskCurrent < %this.taskIndex - 1) %this.executeTask(%this.taskCurrent++); else %this.taskCurrent = -1; } function AIPlayer::executeTask(%this,%index) { %this.taskCurrent = %index; eval(%this.getId() @"."@ %this.task[%index] @";"); } //----------------------------------------------------------------------------- function AIPlayer::singleShot(%this) { // The shooting delay is used to pulse the trigger %this.setImageTrigger(0, true); %this.setImageTrigger(0, false); %delay = %this.getDataBlock().shootingDelay; if (%delay $= "") %delay = 1000; %this.trigger = %this.schedule(%delay, singleShot); } //----------------------------------------------------------------------------- function AIPlayer::wait(%this, %time) { %this.schedule(%time * 1000, "nextTask"); } function AIPlayer::done(%this,%time) { %this.schedule(0, "delete"); } function AIPlayer::fire(%this,%bool) { if (%bool) { cancel(%this.trigger); %this.singleShot(); } else cancel(%this.trigger); %this.nextTask(); } function AIPlayer::aimAt(%this,%object) { echo("Aim: "@ %object); %this.setAimObject(%object); %this.nextTask(); } function AIPlayer::animate(%this,%seq) { //%this.stopThread(0); //%this.playThread(0,%seq); %this.setActionThread(%seq); } // ---------------------------------------------------------------------------- // Some handy getDistance/nearestTarget functions for the AI to use // ---------------------------------------------------------------------------- function AIPlayer::getTargetDistance(%this, %target) { echo("\c4AIPlayer::getTargetDistance("@ %this @", "@ %target @")"); $tgt = %target; %tgtPos = %target.getPosition(); %eyePoint = %this.getWorldBoxCenter(); %distance = VectorDist(%tgtPos, %eyePoint); echo("Distance to target = "@ %distance); return %distance; } function AIPlayer::getNearestPlayerTarget(%this) { echo("\c4AIPlayer::getNearestPlayerTarget("@ %this @")"); %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; %playerPos = %client.player.getPosition(); %tempDist = VectorDist(%playerPos, %botPos); if (%i == 0) { %dist = %tempDist; %index = %i; } else { if (%dist > %tempDist) { %dist = %tempDist; %index = %i; } } } return %index; } //----------------------------------------------------------------------------- function AIPlayer::think(%player) { // Thinking allows us to consider other things... %player.schedule(500, think); } function AIPlayer::spawn(%path) { %player = AIPlayer::spawnOnPath("Shootme", %path); if (isObject(%player)) { %player.followPath(%path, -1); // slow this sucker down, I'm tired of chasing him! %player.setMoveSpeed(0.5); //%player.mountImage(xxxImage, 0); //%player.setInventory(xxxAmmo, 1000); //%player.think(); return %player; } else return 0; }