📄 unit.cpp
字号:
delete localmodel;
localmodel = modelParser->CreateLocalModel(model, &cob->pieces);
SetLODCount(0);
if (unitDef->isAirBase) {
airBaseHandler->RegisterAirBase(this);
}
luaCallIns.UnitGiven(this, oldteam);
globalAI->UnitGiven(this, oldteam);
return true;
}
void CUnit::ChangeTeamReset()
{
Command c;
// clear the commands (newUnitCommands for factories)
c.id = CMD_STOP;
commandAI->GiveCommand(c);
// clear the build commands for factories
CFactoryCAI* facAI = dynamic_cast<CFactoryCAI*>(commandAI);
if (facAI) {
c.options = RIGHT_MOUSE_KEY; // clear option
CCommandQueue& buildCommands = facAI->commandQue;
CCommandQueue::iterator it;
std::vector<Command> clearCommands;
for (it = buildCommands.begin(); it != buildCommands.end(); ++it) {
c.id = it->id;
clearCommands.push_back(c);
}
for (int i = 0; i < (int)clearCommands.size(); i++) {
facAI->GiveCommand(clearCommands[i]);
}
}
// deactivate to prevent the old give metal maker trick
c.id = CMD_ONOFF;
c.params.push_back(0); // always off
commandAI->GiveCommand(c);
c.params.clear();
// reset repeat state
c.id = CMD_REPEAT;
c.params.push_back(0);
commandAI->GiveCommand(c);
c.params.clear();
// reset cloak state
if (unitDef->canCloak) {
c.id = CMD_CLOAK;
c.params.push_back(0); // always off
commandAI->GiveCommand(c);
c.params.clear();
}
// reset move state
if (unitDef->canmove || unitDef->builder) {
c.id = CMD_MOVE_STATE;
c.params.push_back(1);
commandAI->GiveCommand(c);
c.params.clear();
}
// reset fire state
if (!unitDef->noAutoFire &&
(!unitDef->weapons.empty() || (unitDef->type == "Factory"))) {
c.id = CMD_FIRE_STATE;
c.params.push_back(2);
commandAI->GiveCommand(c);
c.params.clear();
}
// reset trajectory state
if (unitDef->highTrajectoryType > 1) {
c.id = CMD_TRAJECTORY;
c.params.push_back(0);
commandAI->GiveCommand(c);
c.params.clear();
}
}
bool CUnit::AttackUnit(CUnit *unit,bool dgun)
{
bool r = false;
haveDGunRequest = dgun;
userAttackGround = false;
commandShotCount = 0;
SetUserTarget(unit);
std::vector<CWeapon*>::iterator wi;
for(wi = weapons.begin(); wi != weapons.end(); ++wi){
(*wi)->haveUserTarget = false;
(*wi)->targetType = Target_None;
if(dgun || !unitDef->canDGun || !(*wi)->weaponDef->manualfire)
if((*wi)->AttackUnit(unit, true))
r = true;
}
return r;
}
bool CUnit::AttackGround(const float3 &pos, bool dgun)
{
bool r=false;
haveDGunRequest=dgun;
SetUserTarget(0);
userAttackPos=pos;
userAttackGround=true;
commandShotCount=0;
std::vector<CWeapon*>::iterator wi;
for(wi=weapons.begin();wi!=weapons.end();++wi){
(*wi)->haveUserTarget=false;
if(dgun || !unitDef->canDGun || !(*wi)->weaponDef->manualfire)
if((*wi)->AttackGround(pos,true))
r=true;
}
return r;
}
void CUnit::SetLastAttacker(CUnit* attacker)
{
if(gs->Ally(team, attacker->team)){
return;
}
if(lastAttacker && lastAttacker!=userTarget)
DeleteDeathDependence(lastAttacker);
lastAttack=gs->frameNum;
lastAttacker=attacker;
if(attacker)
AddDeathDependence(attacker);
}
void CUnit::DependentDied(CObject* o)
{
if (o == userTarget) { userTarget = NULL; }
if (o == soloBuilder) { soloBuilder = NULL; }
if (o == transporter) { transporter = NULL; }
if (o == lastAttacker) { lastAttacker = NULL; }
incomingMissiles.remove((CMissileProjectile*)o);
CSolidObject::DependentDied(o);
}
void CUnit::SetUserTarget(CUnit* target)
{
if(userTarget && lastAttacker!=userTarget)
DeleteDeathDependence(userTarget);
userTarget=target;
for(vector<CWeapon*>::iterator wi=weapons.begin();wi!=weapons.end();++wi)
(*wi)->haveUserTarget=false;
if(target){
AddDeathDependence(target);
}
}
void CUnit::Init(const CUnit* builder)
{
relMidPos=model->relMidPos;
midPos=pos+frontdir*relMidPos.z + updir*relMidPos.y + rightdir*relMidPos.x;
losHeight=relMidPos.y+radius*0.5f;
height = model->height; //TODO: This one would be much better to have either in Constructor or UnitLoader!//why this is always called in unitloader
currentFuel=unitDef->maxFuel;
//All ships starts on water, all other on ground.
//TODO: Improve this. There might be cases when this is not correct.
if(unitDef->movedata && unitDef->movedata->moveType==MoveData::Hover_Move){
physicalState = Hovering;
} else if(floatOnWater) {
physicalState = Floating;
} else {
physicalState = OnGround;
}
//All units are set as ground-blocking.
blocking = true;
if(pos.y+model->height<1) //some torp launchers etc is exactly in the surface and should be considered uw anyway
isUnderWater=true;
if(!unitDef->canKamikaze || unitDef->type!="Building") //semi hack to make mines not block ground
Block();
UpdateTerrainType();
luaCallIns.UnitCreated(this, builder);
globalAI->UnitCreated(this); // FIXME -- add builder?
}
void CUnit::UpdateTerrainType()
{
if (curTerrainType != lastTerrainType) {
cob->Call(COBFN_SetSFXOccupy, curTerrainType);
lastTerrainType = curTerrainType;
}
}
void CUnit::CalculateTerrainType()
{
//Optimization: there's only about one unit that actually needs this information
if (!cob->HasScriptFunction(COBFN_SetSFXOccupy))
return;
if (transporter) {
curTerrainType = 0;
return;
}
float height = ground->GetApproximateHeight(pos.x, pos.z);
//Deep sea?
if (height < -5) {
if (upright)
curTerrainType = 2;
else
curTerrainType = 1;
}
//Shore
else if (height < 0) {
if (upright)
curTerrainType = 1;
}
//Land
else {
curTerrainType = 4;
}
}
bool CUnit::SetGroup(CGroup* newGroup)
{
if (group != 0) {
group->RemoveUnit(this);
}
group=newGroup;
if(group){
if(!group->AddUnit(this)){
group=0; //group ai didnt accept us
return false;
} else { // add us to selected units if group is selected
if(selectedUnits.selectedGroup == group->id)
selectedUnits.AddUnit(this);
}
}
return true;
}
bool CUnit::AddBuildPower(float amount, CUnit* builder)
{
if (amount >= 0.0f) { // build / repair
if (!beingBuilt && (health >= maxHealth)) {
return false;
}
lastNanoAdd = gs->frameNum;
const float part = amount / buildTime;
if (beingBuilt) {
const float metalUse = (metalCost * part);
const float energyUse = (energyCost * part);
if ((gs->Team(builder->team)->metal >= metalUse) &&
(gs->Team(builder->team)->energy >= energyUse) &&
(!luaRules || luaRules->AllowUnitBuildStep(builder, this, part))) {
if (builder->UseMetal(metalUse)) { //just because we checked doesn't mean they were deducted since upkeep can prevent deduction
if (builder->UseEnergy(energyUse)) {
health += (maxHealth * part);
buildProgress += part;
if (buildProgress >= 1.0f) {
if (health > maxHealth) {
health = maxHealth;
}
FinishedBuilding();
}
}
else {
builder->UseMetal(-metalUse); //refund the metal if the energy cannot be deducted
}
}
return true;
} else {
// update the energy and metal required counts
gs->Team(builder->team)->metalPull += metalUse;
gs->Team(builder->team)->energyPull += energyUse;
}
return false;
}
else {
if (health < maxHealth) {
health += maxHealth * part;
if (health > maxHealth) {
health = maxHealth;
}
return true;
}
return false;
}
}
else { // reclaim
if (isDead) {
return false;
}
const float part = amount / buildTime;
if (luaRules && !luaRules->AllowUnitBuildStep(builder, this, part)) {
return false;
}
restTime = 0;
health += maxHealth * part;
if (beingBuilt) {
builder->AddMetal(metalCost*-part);
buildProgress+=part;
if(buildProgress<0 || health<0){
KillUnit(false, true, NULL);
return false;
}
} else {
if (health < 0) {
builder->AddMetal(metalCost);
KillUnit(false, true, NULL);
return false;
}
}
return true;
}
return false;
}
void CUnit::FinishedBuilding(void)
{
beingBuilt = false;
buildProgress = 1.0f;
if (soloBuilder) {
DeleteDeathDependence(soloBuilder);
soloBuilder = NULL;
}
if (!(immobile && (mass == 100000))) {
mass = unitDef->mass; //set this now so that the unit is harder to move during build
}
ChangeLos(realLosRadius, realAirLosRadius);
const bool oldCloak = isCloaked;
if (unitDef->startCloaked) {
wantCloak = true;
isCloaked = true;
}
if (unitDef->windGenerator > 0.0f) {
// start pointing in direction of wind
if (wind.GetCurrentStrength() > unitDef->windGenerator) {
cob->Call(COBFN_SetSpeed, (int)(unitDef->windGenerator * 3000.0f));
} else {
cob->Call(COBFN_SetSpeed, (int)(wind.GetCurrentStrength() * 3000.0f));
}
cob->Call(COBFN_SetDirection,
(int)GetHeadingFromVector(-wind.GetCurrentDirection().x,
-wind.GetCurrentDirection().z));
}
if (unitDef->activateWhenBuilt) {
Activate();
}
SetMetalStorage(unitDef->metalStorage);
SetEnergyStorage(unitDef->energyStorage);
// Sets the frontdir in sync with heading.
frontdir = GetVectorFromHeading(heading) + float3(0, frontdir.y, 0);
if (unitDef->isAirBase) {
airBaseHandler->RegisterAirBase(this);
}
luaCallIns.UnitFinished(this);
globalAI->UnitFinished(this);
if (oldCloak != isCloaked) {
luaCallIns.UnitCloaked(this); // do this after the UnitFinished call-in
}
if (unitDef->isFeature && uh->morphUnitToFeature) {
UnBlock();
CFeature* f =
featureHandler->CreateWreckage(pos, wreckName, heading, buildFacing,
0, team, allyteam, false, "");
if (f) {
f->blockHeightChanges = true;
}
KillUnit(false, true, NULL);
}
}
// Called when a unit's Killed script finishes executing
static void CUnitKilledCB(int retCode, void* p1, void* p2)
{
CUnit* self = (CUnit *)p1;
self->deathScriptFinished = true;
self->delayedWreckLevel = retCode;
}
void CUnit::KillUnit(bool selfDestruct, bool reclaimed, CUnit* attacker)
{
if(isDead)
return;
if(dynamic_cast<CAirMoveType*>(moveType) && !beingBuilt){
if(!selfDestruct && !reclaimed && gs->randFloat()>recentDamage*0.7f/maxHealth+0.2f){
((CAirMoveType*)moveType)->SetState(CAirMoveType::AIRCRAFT_CRASHING);
health=maxHealth*0.5f;
return;
}
}
isDead=true;
luaCallIns.UnitDestroyed(this, attacker);
globalAI->UnitDestroyed(this, attacker);
blockHeightChanges=false;
if(unitDef->isCommander)
gs->Team(team)->CommanderDied(this);
gs->Team(this->lineage)->LeftLineage(this);
if (!reclaimed && !beingBuilt) {
string exp;
if (selfDestruct)
exp = unitDef->selfDExplosion;
else
exp = unitDef->deathExplosion;
if (!exp.empty()) {
const WeaponDef* wd = weaponDefHandler->GetWeapon(exp);
if (wd) {
helper->Explosion(
midPos, wd->damages, wd->areaOfEffect, wd->edgeEffectiveness,
wd->explosionSpeed, this, true, wd->damages[0] > 500? 1: 2,
false, wd->explosionGenerator, 0, ZeroVector, wd->id
);
// play explosion sound
if (wd->soundhit.getID(0) > 0) {
// HACK: loading code doesn't set sane defaults for explosion sounds, so we do it here
// NOTE: actually no longer true, loading code always ensures that sound volume != -1
float volume = wd->soundhit.getVolume(0);
sound->PlaySample(wd->soundhit.getID(0), pos, (volume == -1)? 5.0f: volume);
}
}
}
if (selfDestruct)
recentDamage += maxHealth * 2;
vector<int> args;
args.push_back((int) (recentDamage / maxHealth * 100));
args.push_back(0);
cob->Call(COBFN_Killed, args, &CUnitKilledCB, this, NULL);
UnBlock();
delayedWreckLevel = args[1];
// featureHandler->CreateWreckage(pos,wreckName, heading, args[1],-1,true);
} else {
deathScriptFinished=true;
}
if(beingBuilt || dynamic_cast<CAirMoveType*>(moveType) || reclaimed)
uh->DeleteUnit(this);
else{
speed=ZeroVector;
deathCountdown=5;
stunned=true;
paralyzeDamage=1000000;
if(health<0)
health=0;
}
}
bool CUnit::UseMetal(float metal)
{
if(metal<0){
AddMetal(-metal);
return true;
}
gs->Team(team)->metalPull += metal;
bool canUse=gs->Team(team)->UseMetal(metal);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -