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 + -
显示快捷键?