commandai.cpp

来自「这是整套横扫千军3D版游戏的源码」· C++ 代码 · 共 1,512 行 · 第 1/3 页

CPP
1,512
字号
#include "StdAfx.h"
#include "CommandAI.h"
#include "FactoryCAI.h"
#include "LineDrawer.h"
#include "ExternalAI/GlobalAIHandler.h"
#include "ExternalAI/Group.h"
#include "Game/GameHelper.h"
#include "Game/SelectedUnits.h"
#include "Game/WaitCommandsAI.h"
#include "Game/UI/CommandColors.h"
#include "Game/UI/CursorIcons.h"
#include "Rendering/GL/myGL.h"
#include "Rendering/GL/glExtra.h"
#include "Lua/LuaCallInHandler.h"
#include "Lua/LuaRules.h"
#include "Sim/Misc/Feature.h"
#include "Sim/Misc/FeatureHandler.h"
#include "Sim/MoveTypes/MoveType.h"
#include "Sim/Units/UnitDef.h"
#include "Sim/Units/UnitDefHandler.h"
#include "Sim/Units/Unit.h"
#include "Sim/Units/UnitHandler.h"
#include "Sim/Units/UnitTypes/Factory.h"
#include "Sim/Weapons/WeaponDefHandler.h"
#include "Sim/Weapons/Weapon.h"
#include "Map/Ground.h"
#include "LoadSaveInterface.h"
#include "LogOutput.h"
#include "myMath.h"
#include "mmgr.h"
#include "creg/STL_Set.h"
#include "creg/STL_Deque.h"

#define TARGET_LOST_TIMER 120	// in calls to SlowUpdate() (approx. once every second)

CR_BIND(CCommandQueue, );

CR_REG_METADATA(CCommandQueue, (
				CR_MEMBER(queue),
				CR_MEMBER(tagCounter),
				CR_ENUM_MEMBER(queueType)
				));
CR_BIND_DERIVED(CCommandAI, CObject, );

CR_REG_METADATA(CCommandAI, (
				CR_MEMBER(stockpileWeapon),

				CR_MEMBER(possibleCommands),
				CR_MEMBER(commandQue),
				CR_MEMBER(nonQueingCommands),
				CR_MEMBER(lastUserCommand),
				CR_MEMBER(selfDCountdown),
				CR_MEMBER(lastFinishCommand),

				CR_MEMBER(owner),

				CR_MEMBER(orderTarget),
				CR_MEMBER(targetDied),
				CR_MEMBER(inCommand),
				CR_MEMBER(selected),
				CR_MEMBER(repeatOrders),
				CR_MEMBER(lastSelectedCommandPage),
				CR_MEMBER(unimportantMove),
				CR_MEMBER(targetLostTimer),
				CR_RESERVED(64),
				CR_POSTLOAD(PostLoad)
				));
CCommandAI::CCommandAI()
:	lastUserCommand(-1000),
	orderTarget(0),
	targetDied(false),
	inCommand(false),
	selected(false),
	owner(owner),
	stockpileWeapon(0),
	lastSelectedCommandPage(0),
	repeatOrders(false),
	lastFinishCommand(0),
	unimportantMove(false),
	targetLostTimer(TARGET_LOST_TIMER)
{}

CCommandAI::CCommandAI(CUnit* owner)
:	lastUserCommand(-1000),
	orderTarget(0),
	targetDied(false),
	inCommand(false),
	selected(false),
	owner(owner),
	stockpileWeapon(0),
	lastSelectedCommandPage(0),
	repeatOrders(false),
	lastFinishCommand(0),
	unimportantMove(false),
	targetLostTimer(TARGET_LOST_TIMER)
{
	owner->commandAI=this;
	CommandDescription c;
	c.id=CMD_STOP;
	c.action="stop";
	c.type=CMDTYPE_ICON;
	c.name="Stop";
	c.mouseicon=c.name;
	c.hotkey="s";
	c.tooltip="Stop: Cancel the units current actions";
	possibleCommands.push_back(c);

	if (isAttackCapable()) {
		c.id=CMD_ATTACK;
		c.action="attack";
		c.type=CMDTYPE_ICON_UNIT_OR_MAP;
		c.name="Attack";
		c.mouseicon=c.name;
		c.hotkey="a";
		c.tooltip="Attack: Attacks an unit or a position on the ground";
		possibleCommands.push_back(c);
	}

	if (owner->unitDef->canDGun) {
		c.id=CMD_DGUN;
		c.action="dgun";
		c.type=CMDTYPE_ICON_MAP;
		c.name="DGun";
		c.mouseicon=c.name;
		c.hotkey="d";
		c.tooltip="DGun: Attacks using the units special weapon";
		possibleCommands.push_back(c);
	}

 	c.id=CMD_WAIT;
	c.action="wait";
 	c.type=CMDTYPE_ICON;
 	c.name="Wait";
 	c.mouseicon=c.name;
 	c.hotkey="w";
 	c.onlyKey=true;
 	c.tooltip="Wait: Tells the unit to wait until another units handles him";
 	possibleCommands.push_back(c);
//	nonQueingCommands.insert(CMD_WAIT);

	c.id = CMD_TIMEWAIT;
	c.action = "timewait";
	c.type = CMDTYPE_NUMBER;
	c.name = "TimeWait";
	c.mouseicon=c.name;
	c.hotkey = "";
	c.onlyKey = true;
	c.tooltip = "TimeWait: Wait for a period of time before continuing";
	c.params.push_back("1");  // min
	c.params.push_back("60"); // max
	possibleCommands.push_back(c);
	c.params.clear();

	// only for games with 2 ally teams  --  checked later
	c.id = CMD_DEATHWAIT;
	c.action = "deathwait";
	c.type = CMDTYPE_ICON_UNIT_OR_RECTANGLE;
	c.name = "DeathWait";
	c.mouseicon=c.name;
	c.hotkey = "";
	c.onlyKey = true;
	c.tooltip = "DeathWait: Wait until units die before continuing";
	possibleCommands.push_back(c);

	c.id = CMD_SQUADWAIT;
	c.action = "squadwait";
	c.type = CMDTYPE_NUMBER;
	c.name = "SquadWait";
	c.mouseicon=c.name;
	c.hotkey = "";
	c.onlyKey = true;
	c.tooltip = "SquadWait: Wait for a number of units to arrive before continuing";
	c.params.push_back("2");   // min
	c.params.push_back("100"); // max
	possibleCommands.push_back(c);
	c.params.clear();

	c.id = CMD_GATHERWAIT;
	c.action = "gatherwait";
	c.type = CMDTYPE_ICON;
	c.name = "GatherWait";
	c.mouseicon=c.name;
	c.hotkey = "";
	c.onlyKey = true;
	c.tooltip = "GatherWait: Wait until all units arrive before continuing";
	possibleCommands.push_back(c);

	if (owner->unitDef->canSelfD) {
		c.id = CMD_SELFD;
		c.action = "selfd";
		c.type = CMDTYPE_ICON;
		c.name = "SelfD";
		c.mouseicon = c.name;
		c.hotkey = "Ctrl+d";
		c.onlyKey = true;
		c.tooltip = "SelfD: Tells the unit to self destruct";
		possibleCommands.push_back(c);
	}
//	nonQueingCommands.insert(CMD_SELFD);

	c.onlyKey=false;
	c.hotkey="";

	if (!owner->unitDef->noAutoFire) {
		if(!owner->unitDef->weapons.empty() || owner->unitDef->type=="Factory"/* || owner->unitDef->canKamikaze*/){
			c.id=CMD_FIRE_STATE;
			c.action="firestate";
			c.type=CMDTYPE_ICON_MODE;
			c.name="Fire state";
			c.mouseicon=c.name;
			c.params.push_back("2");
			c.params.push_back("Hold fire");
			c.params.push_back("Return fire");
			c.params.push_back("Fire at will");
			c.tooltip="Fire State: Sets under what conditions an\n unit will start to fire at enemy units\n without an explicit attack order";
			possibleCommands.push_back(c);
			nonQueingCommands.insert(CMD_FIRE_STATE);
		}
	} else {
		owner->fireState = 0;
	}

	if (owner->unitDef->canmove || owner->unitDef->builder) {
		c.params.clear();
		c.id=CMD_MOVE_STATE;
		c.action="movestate";
		c.type=CMDTYPE_ICON_MODE;
		c.name="Move state";
		c.mouseicon=c.name;
		c.params.push_back("1");
		c.params.push_back("Hold pos");
		c.params.push_back("Maneuver");
		c.params.push_back("Roam");
		c.tooltip="Move State: Sets how far out of its way\n an unit will move to attack enemies";
		possibleCommands.push_back(c);
		owner->moveState=1;
		nonQueingCommands.insert(CMD_MOVE_STATE);
	} else {
		owner->moveState=0;
	}

	if (owner->unitDef->canRepeat) {
		c.params.clear();
		c.id=CMD_REPEAT;
		c.action="repeat";
		c.type=CMDTYPE_ICON_MODE;
		c.name="Repeat";
		c.mouseicon=c.name;
		c.params.push_back("0");
		c.params.push_back("Repeat off");
		c.params.push_back("Repeat on");
		c.tooltip="Repeat: If on the unit will continously\n push finished orders to the end of its\n order que";
		possibleCommands.push_back(c);
		nonQueingCommands.insert(CMD_REPEAT);
	}

	if (owner->unitDef->highTrajectoryType>1) {
		c.params.clear();
		c.id=CMD_TRAJECTORY;
		c.action="trajectory";
		c.type=CMDTYPE_ICON_MODE;
		c.name="Trajectory";
		c.mouseicon=c.name;
		c.params.push_back("0");
		c.params.push_back("Low traj");
		c.params.push_back("High traj");
		c.tooltip="Trajectory: If set to high, weapons that\n support it will try to fire in a higher\n trajectory than usual (experimental)";
		possibleCommands.push_back(c);
		nonQueingCommands.insert(CMD_TRAJECTORY);
	}

	if (owner->unitDef->onoffable) {
		c.params.clear();
		c.id=CMD_ONOFF;
		c.action="onoff";
		c.type=CMDTYPE_ICON_MODE;
		c.name="Active state";
		c.mouseicon=c.name;
		c.hotkey="x";

		if (owner->unitDef->activateWhenBuilt) {
			c.params.push_back("1");
		} else {
			c.params.push_back("0");
		}

		c.params.push_back(" Off ");
		c.params.push_back(" On ");

		c.tooltip="Active State: Sets the active state of the unit to on or off";
		possibleCommands.push_back(c);
		nonQueingCommands.insert(CMD_ONOFF);
		c.hotkey="";
	}

	if (owner->unitDef->canCloak) {
		c.params.clear();
		c.id=CMD_CLOAK;
		c.action="cloak";
		c.type=CMDTYPE_ICON_MODE;
		c.name="Cloak state";
		c.mouseicon=c.name;
		c.hotkey="k";

		if (owner->unitDef->startCloaked) {
			c.params.push_back("1");
		} else {
			c.params.push_back("0");
		}

		c.params.push_back("UnCloaked");
		c.params.push_back("Cloaked");

		c.tooltip="Cloak State: Sets wheter the unit is cloaked or not";
		possibleCommands.push_back(c);
		nonQueingCommands.insert(CMD_CLOAK);
		c.hotkey="";
	}
}

CCommandAI::~CCommandAI()
{
	if(orderTarget){
		DeleteDeathDependence(orderTarget);
		orderTarget=0;
	}
}

void CCommandAI::PostLoad()
{
	selected=false;//HACK: selected list does not serialized
}

vector<CommandDescription>& CCommandAI::GetPossibleCommands()
{
	return possibleCommands;
}


bool CCommandAI::isAttackCapable() const
{
	const UnitDef* ud = owner->unitDef;
	return (ud->canAttack && (!ud->weapons.empty() || ud->canKamikaze || (ud->type == "Factory")));
}


static inline bool isCommandInMap(const Command& c)
{
	if (c.params.size() >= 3 &&
			(c.params[0] < 0.f || c.params[2] < 0.f
			 || c.params[0] > gs->mapx*SQUARE_SIZE
			 || c.params[2] > gs->mapy*SQUARE_SIZE))
		return false;
	return true;
}

bool CCommandAI::AllowedCommand(const Command& c)
{
	// check if the command is in the map first
	switch (c.id) {
		case CMD_MOVE:
		case CMD_ATTACK:
		case CMD_AREA_ATTACK:
		case CMD_RECLAIM:
		case CMD_REPAIR:
		case CMD_RESURRECT:
		case CMD_PATROL:
		case CMD_RESTORE:
		case CMD_FIGHT:
		case CMD_DGUN:
		case CMD_UNLOAD_UNIT:
		case CMD_UNLOAD_UNITS:
			if (!isCommandInMap(c)) { return false; }
			break;
		default:
			// build commands
			if (c.id < 0 && !isCommandInMap(c)) { return false; }
			break;
	}

	const UnitDef* ud = owner->unitDef;
	int maxHeightDiff = 200;

	switch (c.id) {
		case CMD_ATTACK:
			maxHeightDiff = 10;
		case CMD_DGUN: {
			if (!isAttackCapable())
				return false;

			if (c.params.size() == 3) {
				// check if attack ground is really attack ground
				if (fabs(c.params[1] - ground->GetHeight2(c.params[0], c.params[2])) >
					maxHeightDiff) {
					return false;
				}
			}
			break;
		}

		case CMD_MOVE:      if (!ud->canmove)       return false; break;
		case CMD_FIGHT:     if (!ud->canFight)      return false; break;
		case CMD_GUARD:     if (!ud->canGuard)      return false; break;
		case CMD_PATROL:    if (!ud->canPatrol)     return false; break;
		case CMD_CAPTURE:   if (!ud->canCapture)    return false; break;
		case CMD_RECLAIM:   if (!ud->canReclaim)    return false; break;
		case CMD_RESTORE:   if (!ud->canRestore)    return false; break;
		case CMD_RESURRECT: if (!ud->canResurrect)  return false; break;
		case CMD_REPAIR: {
			if (!ud->canRepair && !ud->canAssist)   return false; break;
		}
	}

	if ((c.id == CMD_RECLAIM) && (c.params.size() == 1)) {
		const int unitID = (int) c.params[0];
		if (unitID < MAX_UNITS) { // not a feature
			CUnit* unit = uh->units[unitID];
			if (unit && !unit->unitDef->reclaimable)
				return false;
		} else {
			const CFeatureSet& fset = featureHandler->GetActiveFeatures();
			CFeatureSet::const_iterator f = fset.find(unitID - MAX_UNITS);
			if (f != fset.end() && !(*f)->def->reclaimable)
				return false;
		}
	}

	if ((c.id == CMD_REPAIR) && (c.params.size() == 1)) {
		CUnit* unit = uh->units[(int) c.params[0]];
		if (unit && ((unit->beingBuilt && !ud->canAssist) || (!unit->beingBuilt && !ud->canRepair)))
			return false;
	}

	if (c.id == CMD_FIRE_STATE && (c.params.empty() || ud->noAutoFire || (ud->weapons.empty() && ud->type != "Factory")))
		return false;
	if (c.id == CMD_MOVE_STATE && (c.params.empty() || (!ud->canmove && !ud->builder)))
		return false;
	if (c.id == CMD_REPEAT && (c.params.empty() || !ud->canRepeat))
		return false;
	if (c.id == CMD_TRAJECTORY && (c.params.empty() || ud->highTrajectoryType < 2))
		return false;
	if (c.id == CMD_ONOFF && (c.params.empty() || !ud->onoffable || owner->beingBuilt))
		return false;
	if (c.id == CMD_CLOAK && (c.params.empty() || !ud->canCloak))
		return false;
	if (c.id == CMD_STOCKPILE && !stockpileWeapon)
		return false;

	return true;
}


void CCommandAI::GiveCommand(const Command& c, bool fromSynced)
{
	if (luaRules && !luaRules->AllowCommand(owner, c, fromSynced)) {
		return;
	}
	this->GiveCommandReal(c); // send to the sub-classes
}


void CCommandAI::GiveCommandReal(const Command& c)
{
	if (!AllowedCommand(c)) {
		return;
	}

	GiveAllowedCommand(c);
}


inline void CCommandAI::SetCommandDescParam0(const Command& c)
{
	vector<CommandDescription>::iterator cdi;
	for (cdi = possibleCommands.begin(); cdi != possibleCommands.end(); ++cdi) {
		if (cdi->id == c.id) {
			char t[10];
			SNPRINTF(t, 10, "%d", (int)c.params[0]);
			cdi->params[0] = t;
			return;
		}
	}
}


bool CCommandAI::ExecuteStateCommand(const Command& c)
{
	switch (c.id) {
		case CMD_FIRE_STATE: {
			owner->fireState = (int)c.params[0];
			SetCommandDescParam0(c);
			selectedUnits.PossibleCommandChange(owner);
			return true;
		}
		case CMD_MOVE_STATE: {
			owner->moveState = (int)c.params[0];
			SetCommandDescParam0(c);
			selectedUnits.PossibleCommandChange(owner);
			return true;
		}
		case CMD_REPEAT: {
			repeatOrders = !!c.params[0];
			SetCommandDescParam0(c);
			selectedUnits.PossibleCommandChange(owner);

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?