buildercai.cpp

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

CPP
1,458
字号
#include "StdAfx.h"
#include "BuilderCAI.h"
#include "TransportCAI.h"
#include "LineDrawer.h"
#include "ExternalAI/Group.h"
#include "Game/GameHelper.h"
#include "Game/SelectedUnits.h"
#include "Game/Team.h"
#include "Game/UI/CommandColors.h"
#include "Game/UI/CursorIcons.h"
#include "LogOutput.h"
#include "Map/Ground.h"
#include "Rendering/GL/myGL.h"
#include "Rendering/GL/glExtra.h"
#include "Rendering/Textures/TextureHandler.h"
#include "Rendering/UnitModels/3DModelParser.h"
#include "Rendering/UnitModels/UnitDrawer.h"
#include "Lua/LuaRules.h"
#include "Sim/Misc/Feature.h"
#include "Sim/Misc/FeatureHandler.h"
#include "Sim/Misc/QuadField.h"
#include "Sim/MoveTypes/MoveType.h"
#include "Sim/Units/UnitSet.h"
#include "Sim/Units/UnitDefHandler.h"
#include "Sim/Units/UnitHandler.h"
#include "Sim/Units/UnitLoader.h"
#include "Sim/Units/UnitTypes/Builder.h"
#include "Sim/Units/UnitTypes/Building.h"
#include "Sim/Units/UnitTypes/Factory.h"
#include "Sim/Units/UnitTypes/TransportUnit.h"
#include "myMath.h"
#include "mmgr.h"
#include "creg/STL_Map.h"


CR_BIND_DERIVED(CBuilderCAI ,CMobileCAI , );

CR_REG_METADATA(CBuilderCAI , (
				CR_MEMBER(buildOptions),
				CR_MEMBER(building),
				CR_MEMBER(range3D),
//				CR_MEMBER(build),

				CR_MEMBER(cachedRadiusId),
				CR_MEMBER(cachedRadius),

				CR_MEMBER(buildRetries),

				CR_MEMBER(lastPC1),
				CR_MEMBER(lastPC2),
				CR_RESERVED(16),
				CR_POSTLOAD(PostLoad)
				));

// not adding to members, should repopulate itself
CUnitSet CBuilderCAI::reclaimers;


CBuilderCAI::CBuilderCAI()
: CMobileCAI(),
	building(false),
	range3D(true),
	cachedRadius(0),
	cachedRadiusId(0),
	buildRetries(0),
	lastPC1(-1),
	lastPC2(-1)
{}


CBuilderCAI::CBuilderCAI(CUnit* owner)
: CMobileCAI(owner),
	building(false),
	range3D(owner->unitDef->buildRange3D),
	cachedRadius(0),
	cachedRadiusId(0),
	buildRetries(0),
	lastPC1(-1),
	lastPC2(-1)
{
	CommandDescription c;
	if(owner->unitDef->canRepair){
		c.id=CMD_REPAIR;
		c.action="repair";
		c.hotkey="r";
		c.type=CMDTYPE_ICON_UNIT_OR_AREA;
		c.name="Repair";
		c.mouseicon=c.name;
		c.tooltip="Repair: Repairs another unit";
		possibleCommands.push_back(c);
	} else if(owner->unitDef->canAssist){
		c.id=CMD_REPAIR;
		c.action="assist";
		c.hotkey="r";
		c.type=CMDTYPE_ICON_UNIT_OR_AREA;
		c.name="Assist";
		c.mouseicon=c.name;
		c.tooltip="Assist: Help build something";
		possibleCommands.push_back(c);
	}

	if(owner->unitDef->canReclaim){
		c.id=CMD_RECLAIM;
		c.action="reclaim";
		c.hotkey="e";
		c.type=CMDTYPE_ICON_UNIT_FEATURE_OR_AREA;
		c.name="Reclaim";
		c.mouseicon=c.name;
		c.tooltip="Reclaim: Sucks in the metal/energy content of a unit/feature and add it to your storage";
		possibleCommands.push_back(c);
	}

	if(owner->unitDef->canRestore){
		c.id=CMD_RESTORE;
		c.action="restore";
		c.hotkey="";
		c.type=CMDTYPE_ICON_AREA;
		c.name="Restore";
		c.mouseicon=c.name;
		c.tooltip="Restore: Restores an area of the map to its original height";
		c.params.push_back("200");
		possibleCommands.push_back(c);
	}
	c.params.clear();

	if(owner->unitDef->canResurrect){
		c.id=CMD_RESURRECT;
		c.action="resurrect";
		c.hotkey="";
		c.type=CMDTYPE_ICON_UNIT_FEATURE_OR_AREA;
		c.name="Resurrect";
		c.mouseicon=c.name;
		c.tooltip="Resurrect: Resurrects a unit from a feature";
		possibleCommands.push_back(c);
	}
	if(owner->unitDef->canCapture){
		c.id=CMD_CAPTURE;
		c.action="capture";
		c.hotkey="";
		c.type=CMDTYPE_ICON_UNIT_OR_AREA;
		c.name="Capture";
		c.mouseicon=c.name;
		c.tooltip="Capture: Captures a unit from the enemy";
		possibleCommands.push_back(c);
	}
	CBuilder* fac=(CBuilder*)owner;

	map<int, string>::const_iterator bi;
	for (bi = fac->unitDef->buildOptions.begin(); bi != fac->unitDef->buildOptions.end(); ++bi) {
		const string name = bi->second;
		const UnitDef* ud = unitDefHandler->GetUnitByName(name);
		if (ud == NULL) {
		  string errmsg = "MOD ERROR: loading ";
		  errmsg += name.c_str();
		  errmsg += " for ";
		  errmsg += owner->unitDef->name;
			throw content_error(errmsg);
		}
		CommandDescription c;
		c.id=-ud->id; //build options are always negative
		c.action="buildunit_" + StringToLower(ud->name);
		c.type=CMDTYPE_ICON_BUILDING;
		c.name=name;
		c.mouseicon=c.name;

		char tmp[500];
		sprintf(tmp,"\nHealth %.0f\nMetal cost %.0f\nEnergy cost %.0f Build time %.0f",ud->health,ud->metalCost,ud->energyCost,ud->buildTime);
		c.tooltip = string("Build: ") + ud->humanName + " - " + ud->tooltip + tmp;

		possibleCommands.push_back(c);
		buildOptions[c.id]=name;
	}
	uh->AddBuilderCAI(this);
}

CBuilderCAI::~CBuilderCAI()
{
	RemoveUnitFromReclaimers(owner);
	uh->RemoveBuilderCAI(this);
}

void CBuilderCAI::PostLoad()
{
	if (!commandQue.empty()) {
		Command& c = commandQue.front();
		float3 curPos = owner->pos;

		map<int, string>::iterator boi = buildOptions.find(c.id);
		if (boi != buildOptions.end()) {
			build.Parse(c);
			build.pos = helper->Pos2BuildPos(build);
		}
	}
}


inline bool CBuilderCAI::ObjInBuildRange(const CWorldObject* obj) const
{
	const CBuilder* builder = (CBuilder*)owner;
	const float immDistSqr = f3SqLen(owner->pos - obj->pos);
	const float buildDist = builder->buildDistance + obj->radius - 9.0f;
	return (immDistSqr < (buildDist * buildDist));
}


inline bool CBuilderCAI::OutOfImmobileRange(const Command& cmd) const
{
	if (owner->unitDef->canmove) {
		return false; // unit can move
	}
	if (((cmd.options & INTERNAL_ORDER) == 0) || (cmd.params.size() != 1)) {
		return false; // not an internal object targetted command
	}

	const int id = (int)cmd.params[0];
	CWorldObject* obj = NULL;
	if (id < 0) {
		return false;
	}
	else if (id < MAX_UNITS) {
		obj = uh->units[id];
	}
	else {
		// features don't move, but maybe the unit was transported?
		const CFeatureSet& fset = featureHandler->GetActiveFeatures();
		CFeatureSet::const_iterator it = fset.find(id - MAX_UNITS);
		if (it != fset.end()) {
			obj = *it;
		}
	}
	if (obj == NULL) {
		return false;
	}
	
	switch (cmd.id) {
		case CMD_REPAIR:
		case CMD_RECLAIM:
		case CMD_RESURRECT:
		case CMD_CAPTURE: {
			if (!ObjInBuildRange(obj)) {
				return true;
			}
			break;
		}
	}
	return false;
}


float CBuilderCAI::GetUnitDefRadius(const UnitDef* ud, int cmdId)
{
	float radius;
	if (cachedRadiusId == cmdId) {
		radius = cachedRadius;
	} else {
		radius = ud->LoadModel(owner->team)->radius;
		cachedRadius = radius;
		cachedRadiusId = cmdId;
	}
	return radius;
}


void CBuilderCAI::CancelRestrictedUnit(const std::string& buildOption)
{
	ENTER_MIXED;
	if (owner->team == gu->myTeam) {
		logOutput.Print("%s: Build failed, unit type limit reached", owner->unitDef->humanName.c_str());
		logOutput.SetLastMsgPos(owner->pos);
	}
	ENTER_SYNCED;
	FinishCommand();
}


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

	if ((c.id == CMD_GUARD) &&
	    (c.params.size() == 1) && ((int)c.params[0] == owner->id)) {
		return;
	}

	if(!(c.options & SHIFT_KEY) && nonQueingCommands.find(c.id)==nonQueingCommands.end()){
		building=false;
		CBuilder* fac=(CBuilder*)owner;
		fac->StopBuild();
	}

	map<int,string>::iterator boi = buildOptions.find(c.id);
	if (boi != buildOptions.end()) {
		if (c.params.size() < 3) {
			return;
		}
		BuildInfo bi;
		bi.pos = float3(c.params[0],c.params[1],c.params[2]);
		if(c.params.size()==4) bi.buildFacing=int(c.params[3]);
		bi.def = unitDefHandler->GetUnitByName(boi->second);
		bi.pos=helper->Pos2BuildPos(bi);
		if (!owner->unitDef->canmove) {
			const CBuilder* builder = (CBuilder*)owner;
			const float dist = f3Len(builder->pos - bi.pos);
			const float radius = GetUnitDefRadius(bi.def, c.id);
			if (dist > (builder->buildDistance + radius - 8.0f)) {
				return;
			}
		}
		CFeature* feature;
		if(!uh->TestUnitBuildSquare(bi,feature,owner->allyteam)) {
			if (!feature && owner->unitDef->canAssist) {
				int yardxpos=int(bi.pos.x+4)/SQUARE_SIZE;
				int yardypos=int(bi.pos.z+4)/SQUARE_SIZE;
				CSolidObject* s;
				CUnit* u;
				if((s=readmap->GroundBlocked(yardypos*gs->mapx+yardxpos)) &&
				   (u=dynamic_cast<CUnit*>(s)) &&
				   u->beingBuilt && (u->buildProgress == 0.0f) &&
				   (!u->soloBuilder || (u->soloBuilder == owner))) {
					Command c2;
					c2.id = CMD_REPAIR;
					c2.params.push_back(u->id);
					c2.options = c.options | INTERNAL_ORDER;
					CMobileCAI::GiveCommandReal(c2);
					CMobileCAI::GiveCommandReal(c);
				}
			}
			return;
		}
	}
	CMobileCAI::GiveCommandReal(c);
}


void CBuilderCAI::SlowUpdate()
{
	if (commandQue.empty()) {
		CMobileCAI::SlowUpdate();
		return;
	}

	if (owner->stunned) {
		return;
	}

	CBuilder* fac = (CBuilder*)owner;
	Command& c = commandQue.front();

	if (OutOfImmobileRange(c)) {
		FinishCommand();
	}

	map<int, string>::iterator boi = buildOptions.find(c.id);
	if (boi != buildOptions.end()) {
		const UnitDef* ud = unitDefHandler->GetUnitByName(boi->second);
		const float radius = GetUnitDefRadius(ud, c.id);
		if (inCommand) {
			if (building) {
				if (f3Dist(build.pos, fac->pos) > fac->buildDistance+radius-8.0f) {
					owner->moveType->StartMoving(build.pos, fac->buildDistance*0.5f+radius);
				} else {
					StopMove();
					owner->moveType->KeepPointingTo(build.pos, (fac->buildDistance+radius)*0.6f, false);	//needed since above startmoving cancels this
				}
				if(!fac->curBuild && !fac->terraforming){
					building=false;
					StopMove();				//cancel the effect of KeepPointingTo
					FinishCommand();
				}
				// This can only be true if two builders started building
				// the restricted unit in the same simulation frame
				else if(uh->unitsByDefs[owner->team][build.def->id].size() > build.def->maxThisUnit){ //unit restricted
					building = false;
					fac->StopBuild();
					CancelRestrictedUnit(boi->second);
				}
			} else {
				build.Parse(c);
				build.pos = helper->Pos2BuildPos(build);
				const float dist = f3Dist(build.pos, fac->pos);
				if ((dist < (fac->buildDistance * 0.6f + radius)) ||
				    (!owner->unitDef->canmove && (dist <= (fac->buildDistance+radius-8.0f)))) {
					StopMove();
					if(luaRules && !luaRules->AllowUnitCreation(build.def, owner, &build.pos)) {
						FinishCommand();
					}
					else if(uh->unitsByDefs[owner->team][build.def->id].size() >= build.def->maxThisUnit){ //unit restricted
						CancelRestrictedUnit(boi->second);
					}
					else if(uh->maxUnits>(int)gs->Team(owner->team)->units.size()){ //max unitlimit reached
						buildRetries++;
						owner->moveType->KeepPointingTo(build.pos, fac->buildDistance*0.7f+radius, false);
						if (fac->StartBuild(build) || (buildRetries > 20)) {
							building=true;
						} else {
							ENTER_MIXED;
							if ((owner->team == gu->myTeam) && !(buildRetries & 7)) {
								logOutput.Print("%s: Build pos blocked",owner->unitDef->humanName.c_str());
								logOutput.SetLastMsgPos(owner->pos);
							}
							ENTER_SYNCED;
							helper->BuggerOff(build.pos,radius);
							NonMoving();
						}
					}
				} else {
					if (owner->moveType->progressState == CMoveType::Failed) {
						if (++buildRetries > 5) {
							StopMove();
							FinishCommand();
						}
					}
					SetGoal(build.pos,owner->pos, fac->buildDistance*0.4f+radius);
				}
			}
		} else {		//!inCommand
			BuildInfo bi;
			bi.pos.x=floor(c.params[0]/SQUARE_SIZE+0.5f)*SQUARE_SIZE;
			bi.pos.z=floor(c.params[2]/SQUARE_SIZE+0.5f)*SQUARE_SIZE;
			bi.pos.y=c.params[1];
			CFeature* f=0;
			if (c.params.size()==4)
				bi.buildFacing = int(c.params[3]);
			bi.def = unitDefHandler->GetUnitByName(boi->second);

			uh->TestUnitBuildSquare(bi,f,owner->allyteam);
			if (f) {
				if (!owner->unitDef->canReclaim || !f->def->reclaimable) {
					// FIXME user shouldn't be able to queue buildings on top of features
					// in the first place (in this case).
					StopMove();
					FinishCommand();
				} else {
					Command c2;
					c2.id=CMD_RECLAIM;
					c2.options=0;
					c2.params.push_back(f->id+MAX_UNITS);
					commandQue.push_front(c2);
					SlowUpdate(); //this assumes that the reclaim command can never return directly without having reclaimed the target
				}
			} else {
				inCommand=true;
				SlowUpdate();
			}
		}
		return;
	}

	switch (c.id) {
		case CMD_STOP:      { ExecuteStop(c);      return; }
		case CMD_REPAIR:    { ExecuteRepair(c);    return; }
		case CMD_CAPTURE:   { ExecuteCapture(c);   return; }
		case CMD_GUARD:     { ExecuteGuard(c);     return; }
		case CMD_RECLAIM:   { ExecuteReclaim(c);   return; }
		case CMD_RESURRECT: { ExecuteResurrect(c); return; }
		case CMD_PATROL:    { ExecutePatrol(c);    return; }
		case CMD_FIGHT:     { ExecuteFight(c);     return; }
		case CMD_RESTORE:   { ExecuteRestore(c);   return; }
		default: {
			CMobileCAI::SlowUpdate();
			return;
		}
	}
}


void CBuilderCAI::FinishCommand(void)
{
	if (commandQue.front().timeOut == INT_MAX) {
		buildRetries = 0;
	}
	CMobileCAI::FinishCommand();
}


void CBuilderCAI::ExecuteStop(Command& c)
{
	CBuilder* fac=(CBuilder*)owner;
	building=false;
	fac->StopBuild();
	CMobileCAI::ExecuteStop(c);
}


void CBuilderCAI::ExecuteRepair(Command& c)

⌨️ 快捷键说明

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