void CScriptMoveType::TrackSlope() { owner->frontdir = GetVectorFromHeading(owner->heading); owner->updir = ground->GetSmoothNormal(owner->pos.x, owner->pos.z); owner->rightdir = owner->frontdir.cross(owner->updir); owner->rightdir.Normalize(); owner->frontdir = owner->updir.cross(owner->rightdir); }
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); if (unitDef->startCloaked) { wantCloak = true; isCloaked = true; } if (unitDef->windGenerator>0) { if (wind.curStrength > unitDef->windGenerator) { cob->Call(COBFN_SetSpeed, (int)(unitDef->windGenerator * 3000.0f)); } else { cob->Call(COBFN_SetSpeed, (int)(wind.curStrength * 3000.0f)); } cob->Call(COBFN_SetDirection, (int)GetHeadingFromVector(-wind.curDir.x, -wind.curDir.z)); } if (unitDef->activateWhenBuilt) { Activate(); } gs->Team(team)->metalStorage += unitDef->metalStorage; gs->Team(team)->energyStorage += 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 (unitDef->isFeature) { UnBlock(); CFeature* f = featureHandler->CreateWreckage(pos, wreckName, heading, buildFacing, 0, team, false, ""); if (f) { f->blockHeightChanges = true; } KillUnit(false, true, 0); } }
void CFeature::CalculateTransform() { updir = (!def->upright)? ground->GetNormal(pos.x, pos.z): UpVector; frontdir = GetVectorFromHeading(heading); rightdir = (frontdir.cross(updir)).Normalize(); frontdir = (updir.cross(rightdir)).Normalize(); transMatrix = CMatrix44f(pos, -rightdir, updir, frontdir); }
void CScriptMoveType::SetHeading(short heading) { owner->heading = heading; if (!trackSlope) { owner->frontdir = GetVectorFromHeading(heading); owner->updir = UpVector; owner->rightdir = owner->frontdir.cross(UpVector); } }
void CBuilding::ForcedMove(const float3& newPos, int facing) { buildFacing = facing; pos = helper->Pos2BuildPos(BuildInfo(unitDef, newPos, buildFacing)); speed = ZeroVector; heading = GetHeadingFromFacing(buildFacing); frontdir = GetVectorFromHeading(heading); CUnit::ForcedMove(pos); unitLoader.FlattenGround(this); }
void CBuilding::ForcedMove(const float3& newPos) { heading = GetHeadingFromFacing(buildFacing); frontdir = GetVectorFromHeading(heading); SetVelocity(ZeroVector); Move(CGameHelper::Pos2BuildPos(BuildInfo(unitDef, newPos, buildFacing), true), false); UpdateMidAndAimPos(); CUnit::ForcedMove(pos); unitLoader->FlattenGround(this); }
void CFeature::ForcedSpin(const float3& newDir) { float3 updir = UpVector; if (updir == newDir) { //FIXME perhaps save the old right,up,front directions, so we can // reconstruct the old upvector and generate a better assumption for updir updir -= GetVectorFromHeading(heading); } float3 rightdir = newDir.cross(updir).Normalize(); updir = rightdir.cross(newDir); transMatrix = CMatrix44f(pos, -rightdir, updir, newDir); heading = GetHeadingFromVector(newDir.x, newDir.z); }
void CFeature::CalculateTransform () { float3 frontDir=GetVectorFromHeading(heading); float3 upDir; if (def->upright) upDir = float3(0.0f,1.0f,0.0f); else upDir = ground->GetNormal(pos.x,pos.z); float3 rightDir=frontDir.cross(upDir); rightDir.Normalize(); frontDir=upDir.cross(rightDir); frontDir.Normalize (); transMatrix = CMatrix44f (pos,-rightDir,upDir,frontDir); }
bool CWeapon::TryTargetHeading(short heading, float3 pos, bool userTarget, CUnit* unit) { float3 tempfrontdir(owner->frontdir); float3 temprightdir(owner->rightdir); short tempHeadding = owner->heading; owner->heading = heading; owner->frontdir = GetVectorFromHeading(owner->heading); owner->rightdir = owner->frontdir.cross(owner->updir); weaponPos=owner->pos+owner->frontdir*relWeaponPos.z+owner->updir*relWeaponPos.y+owner->rightdir*relWeaponPos.x; weaponMuzzlePos=owner->pos+owner->frontdir*relWeaponMuzzlePos.z+owner->updir*relWeaponMuzzlePos.y+owner->rightdir*relWeaponMuzzlePos.x; bool val = TryTarget(pos, userTarget, unit); owner->frontdir = tempfrontdir; owner->rightdir = temprightdir; owner->heading = tempHeadding; weaponPos=owner->pos+owner->frontdir*relWeaponPos.z+owner->updir*relWeaponPos.y+owner->rightdir*relWeaponPos.x; weaponMuzzlePos=owner->pos+owner->frontdir*relWeaponMuzzlePos.z+owner->updir*relWeaponMuzzlePos.y+owner->rightdir*relWeaponMuzzlePos.x; return val; }
void CTAAirMoveType::UpdateBanking(bool noBanking) { SyncedFloat3 &frontdir = owner->frontdir; SyncedFloat3 &updir = owner->updir; if (!owner->upright) { float wantedPitch = 0; if (aircraftState == AIRCRAFT_FLYING && flyState == FLY_ATTACKING && circlingPos.y < owner->pos.y) { wantedPitch = (circlingPos.y - owner->pos.y) / circlingPos.distance(owner->pos); } currentPitch = currentPitch * 0.95f + wantedPitch * 0.05f; } frontdir = GetVectorFromHeading(owner->heading); frontdir.y = currentPitch; frontdir.Normalize(); float3 rightdir(frontdir.cross(UpVector)); //temp rightdir rightdir.Normalize(); float wantedBank = 0.0f; if (!noBanking && bankingAllowed) wantedBank = rightdir.dot(deltaSpeed)/accRate*0.5f; const float limit = std::min(1.0f,goalPos.SqDistance2D(owner->pos)*Square(0.15f)); if (Square(wantedBank) > limit) { wantedBank = math::sqrt(limit); } else if (Square(wantedBank) < -limit) { wantedBank = -math::sqrt(limit); } //Adjust our banking to the desired value if (currentBank > wantedBank) { currentBank -= std::min(0.03f, currentBank - wantedBank); } else { currentBank += std::min(0.03f, wantedBank - currentBank); } // Calculate a suitable upvector updir = rightdir.cross(frontdir); updir = updir * cos(currentBank) + rightdir * sin(currentBank); owner->rightdir = frontdir.cross(updir); }
void CFeature::ForcedSpin(const float3& newDir) { /* heading = GetHeadingFromVector(newDir.x, newDir.z); CalculateTransform(); if (def->drawType >= DRAWTYPE_TREE) { treeDrawer->DeleteTree(pos); treeDrawer->AddTree(def->drawType - 1, pos, 1.0f); } */ float3 updir = UpVector; if (updir == newDir) { //FIXME perhaps save the old right,up,front directions, so we can // reconstruct the old upvector and generate a better assumption for updir updir -= GetVectorFromHeading(heading); } float3 rightdir = newDir.cross(updir).Normalize(); updir = rightdir.cross(newDir); transMatrix = CMatrix44f(pos, -rightdir, updir, newDir); heading = GetHeadingFromVector(newDir.x, newDir.z); }
void CTAAirMoveType::UpdateBanking(bool noBanking) { float3 &frontdir = owner->frontdir; float3 &updir = owner->updir; float wantedPitch=0; if(aircraftState==AIRCRAFT_FLYING && flyState==FLY_ATTACKING && circlingPos.y<owner->pos.y){ wantedPitch=(circlingPos.y-owner->pos.y)/circlingPos.distance(owner->pos); } currentPitch=currentPitch*0.95+wantedPitch*0.05; frontdir = GetVectorFromHeading(owner->heading); frontdir.y=currentPitch; frontdir.Normalize(); float3 rightdir(frontdir.cross(UpVector)); //temp rightdir rightdir.Normalize(); float wantedBank = 0.0f; if (!noBanking) wantedBank = rightdir.dot(deltaSpeed)/accRate*0.5; float limit=min(1.0f,goalPos.distance2D(owner->pos)*0.15f); if(wantedBank>limit) wantedBank=limit; else if(wantedBank<-limit) wantedBank=-limit; //Adjust our banking to the desired value if (currentBank > wantedBank) currentBank -= min(0.03f, currentBank - wantedBank); else currentBank += min(0.03f, wantedBank - currentBank); //Calculate a suitable upvector updir= rightdir.cross(frontdir); updir = updir*cos(currentBank)+rightdir*sin(currentBank); owner->rightdir = frontdir.cross(updir); }
CUnit* CUnitLoader::LoadUnit(const string& name, float3 pos, int side, bool build, int facing) { CUnit* unit; START_TIME_PROFILE; UnitDef* ud= unitDefHandler->GetUnitByName(name); if(!ud) throw content_error("Couldn't find unittype " + name); string type = ud->type; if(!build){ pos.y=ground->GetHeight2(pos.x,pos.z); if(ud->floater && pos.y<0) pos.y = -ud->waterline; } bool blocking = false; //Used to tell if ground area shall be blocked of not. //unit = new CUnit(pos, side); if (side < 0) side = MAX_TEAMS-1; if(type=="GroundUnit"){ unit=new CUnit; blocking = true; } else if (type=="Transport"){ unit=new CTransportUnit; blocking = true; } else if (type=="Building"){ unit=new CBuilding; blocking = true; } else if (type=="Factory"){ unit=new CFactory; blocking = true; } else if (type=="Builder"){ unit=new CBuilder; blocking = true; } else if (type=="Bomber" || type=="Fighter"){ unit=new CUnit; } else if (type == "MetalExtractor") { unit = new CExtractorBuilding; blocking = true; } else { logOutput << "Unknown unit type " << type.c_str() << "\n"; return 0; } unit->UnitInit (ud, side, pos); unit->beingBuilt=build; unit->xsize = (facing&1)==0 ? ud->xsize : ud->ysize; unit->ysize = (facing&1)==1 ? ud->xsize : ud->ysize; unit->buildFacing = facing; unit->power=ud->power; unit->maxHealth=ud->health; unit->health=ud->health; //unit->metalUpkeep=ud->metalUpkeep*16.0f/GAME_SPEED; //unit->energyUpkeep=ud->energyUpkeep*16.0f/GAME_SPEED; unit->controlRadius=(int)(ud->controlRadius/SQUARE_SIZE); unit->losHeight=ud->losHeight; unit->metalCost=ud->metalCost; unit->energyCost=ud->energyCost; unit->buildTime=ud->buildTime; unit->aihint=ud->aihint; unit->tooltip=ud->humanName + " " + ud->tooltip; unit->armoredMultiple=max(0.0001f,ud->armoredMultiple); //armored multiple of 0 will crash spring unit->wreckName=ud->wreckName; unit->realLosRadius=(int) (ud->losRadius); unit->realAirLosRadius=(int) (ud->airLosRadius); unit->upright=ud->upright; unit->radarRadius=ud->radarRadius/(SQUARE_SIZE*8); unit->sonarRadius=ud->sonarRadius/(SQUARE_SIZE*8); unit->jammerRadius=ud->jammerRadius/(SQUARE_SIZE*8); unit->sonarJamRadius=ud->sonarJamRadius/(SQUARE_SIZE*8); unit->seismicRadius = ud->seismicRadius /(SQUARE_SIZE*8); unit->seismicSignature = ud->seismicSignature; unit->hasRadarCapacity=unit->radarRadius || unit->sonarRadius || unit->jammerRadius || unit->sonarJamRadius || unit->seismicRadius; unit->stealth=ud->stealth; unit->category=ud->category; unit->armorType=ud->armorType; unit->floatOnWater = ud->floater || (ud->movedata && ((ud->movedata->moveType == MoveData::Hover_Move) || (ud->movedata->moveType == MoveData::Ship_Move))); unit->maxSpeed = ud->speed/30.0; if(ud->highTrajectoryType==1) unit->useHighTrajectory=true; if(ud->noAutoFire) unit->fireState=0; if(build){ unit->ChangeLos(1,1); unit->health=0.1f; } else { unit->ChangeLos((int)(ud->losRadius),(int)(ud->airLosRadius)); } if(type=="GroundUnit"){ new CMobileCAI(unit); } else if(type=="Transport"){ new CTransportCAI(unit); } else if(type=="Factory"){ new CFactoryCAI(unit); } else if(type=="Builder"){ new CBuilderCAI(unit); } else if(type=="Bomber"){ if (ud->hoverAttack) new CMobileCAI(unit); else new CAirCAI(unit); } else if(type=="Fighter"){ if (ud->hoverAttack) new CMobileCAI(unit); else new CAirCAI(unit); } else { new CCommandAI(unit); } if(ud->canmove && !ud->canfly && type!="Factory"){ CGroundMoveType* mt=new CGroundMoveType(unit); mt->maxSpeed=ud->speed/GAME_SPEED; mt->maxWantedSpeed=ud->speed/GAME_SPEED; mt->turnRate=ud->turnRate; mt->baseTurnRate=ud->turnRate; if (!mt->accRate) logOutput << "acceleration of " << ud->name.c_str() << " is zero!!\n"; mt->moveType=ud->moveType; mt->accRate=ud->maxAcc; mt->floatOnWater=ud->movedata->moveType==MoveData::Hover_Move || ud->movedata->moveType==MoveData::Ship_Move; if(!unit->beingBuilt) unit->mass=ud->mass; //otherwise set this when finished building instead unit->moveType=mt; //Ground-mobility unit->mobility = new CMobility(); unit->mobility->canFly = false; unit->mobility->subMarine = false; //Not always correct, as submarines are treated as ships. unit->mobility->maxAcceleration = ud->maxAcc; unit->mobility->maxBreaking = -3*ud->maxAcc; unit->mobility->maxSpeed = ud->speed / GAME_SPEED; unit->mobility->maxTurnRate = (short int) ud->turnRate; unit->mobility->moveData = ud->movedata; } else if(ud->canfly){ //Air-mobility unit->mobility = new CMobility(); unit->mobility->canFly = true; unit->mobility->subMarine = false; unit->mobility->maxAcceleration = ud->maxAcc; unit->mobility->maxBreaking = -3*ud->maxAcc; //Correct? unit->mobility->maxSpeed = ud->speed / GAME_SPEED; unit->mobility->maxTurnRate = (short int) ud->turnRate; unit->mobility->moveData = ud->movedata; if(!unit->beingBuilt) unit->mass=ud->mass; //otherwise set this when finished building instead if ((type == "Builder") || ud->hoverAttack || ud->transportCapacity) { CTAAirMoveType *mt = new CTAAirMoveType(unit); mt->turnRate = ud->turnRate; mt->maxSpeed = ud->speed / GAME_SPEED; mt->accRate = ud->maxAcc; mt->decRate = ud->maxDec; mt->wantedHeight = ud->wantedHeight+gs->randFloat()*5; mt->orgWantedHeight=mt->wantedHeight; mt->dontLand = ud->DontLand (); unit->moveType = mt; } else { CAirMoveType *mt = new CAirMoveType(unit); if(type=="Fighter") mt->isFighter=true; mt->wingAngle = ud->wingAngle; mt->invDrag = 1 - ud->drag; mt->frontToSpeed = ud->frontToSpeed; mt->speedToFront = ud->speedToFront; mt->myGravity = ud->myGravity; mt->maxBank = ud->maxBank; mt->maxPitch = ud->maxPitch; mt->turnRadius = ud->turnRadius; mt->wantedHeight = ud->wantedHeight*1.5f+(gs->randFloat()-0.3f)*15*(mt->isFighter?2:1);; mt->maxAcc = ud->maxAcc; mt->maxAileron = ud->maxAileron; mt->maxElevator = ud->maxElevator; mt->maxRudder = ud->maxRudder; unit->moveType = mt; } } else { unit->moveType=new CMoveType(unit); unit->upright=true; } unit->energyTickMake = ud->energyMake; if(ud->tidalGenerator>0) unit->energyTickMake += ud->tidalGenerator*readmap->tidalStrength; // if(!ud->weapons.empty()) // unit->mainDamageType=unit->weapons.front()->damageType; //unit->model=unitModelLoader->GetModel(ud->model.modelname,side); unit->model = modelParser->Load3DO((ud->model.modelpath).c_str(),ud->canfly?0.5f:1,side); //this is a hack to make aircrafts less likely to collide and get hit by nontracking weapons unit->SetRadius(unit->model->radius); if(ud->floater) unit->pos.y = max(-ud->waterline,ground->GetHeight2(unit->pos.x,unit->pos.z)); else unit->pos.y=ground->GetHeight2(unit->pos.x,unit->pos.z); //unit->pos.y=ground->GetHeight(unit->pos.x,unit->pos.z); unit->cob = new CCobInstance(GCobEngine.GetCobFile("scripts/" + name+".cob"), unit); unit->localmodel = modelParser->CreateLocalModel(unit->model, &unit->cob->pieces); for(unsigned int i=0; i< ud->weapons.size(); i++) unit->weapons.push_back(LoadWeapon(ud->weapons[i].def,unit,&ud->weapons[i])); // Calculate the max() of the available weapon reloadtimes int relMax = 0; for (vector<CWeapon*>::iterator i = unit->weapons.begin(); i != unit->weapons.end(); ++i) { if ((*i)->reloadTime > relMax) relMax = (*i)->reloadTime; if(dynamic_cast<CBeamLaser*>(*i)) relMax=150; } relMax *= 30; // convert ticks to milliseconds // TA does some special handling depending on weapon count if (unit->weapons.size() > 1) relMax = max(relMax, 3000); // Call initializing script functions unit->cob->Call(COBFN_Create); unit->cob->Call("SetMaxReloadTime", relMax); unit->heading = facing*16*1024; unit->frontdir=GetVectorFromHeading(unit->heading); unit->updir=UpVector; unit->rightdir=unit->frontdir.cross(unit->updir); unit->Init(); unit->yardMap = ud->yardmaps[facing]; if(!build) unit->FinishedBuilding(); END_TIME_PROFILE("Unit loader"); return unit; }
void CUnit::SlowUpdate() { --nextPosErrorUpdate; if(nextPosErrorUpdate==0) { float3 newPosError(gs->randVector()); newPosError.y*=0.2; if(posErrorVector.dot(newPosError)<0) newPosError=-newPosError; posErrorDelta=(newPosError-posErrorVector)*(1.0/256); nextPosErrorUpdate=16; } for(int a=0; a<gs->activeAllyTeams; ++a) { if(losStatus[a] & LOS_INTEAM) { } else if(loshandler->InLos(this,a)) { if(!(losStatus[a]&LOS_INLOS)) { int prevLosStatus = losStatus[a]; if(mobility || beingBuilt) { losStatus[a]|=(LOS_INLOS | LOS_INRADAR); } else { losStatus[a]|=(LOS_INLOS | LOS_INRADAR | LOS_PREVLOS | LOS_CONTRADAR); } if(!(prevLosStatus&LOS_INRADAR)) { globalAI->UnitEnteredRadar(this,a); } globalAI->UnitEnteredLos(this,a); } } else if(radarhandler->InRadar(this,a)) { if((losStatus[a] & LOS_INLOS)) { globalAI->UnitLeftLos(this,a); losStatus[a]&= ~LOS_INLOS; } else if(!(losStatus[a] & LOS_INRADAR)) { losStatus[a]|= LOS_INRADAR; globalAI->UnitEnteredRadar(this,a); } } else { if((losStatus[a]&LOS_INRADAR)) { if((losStatus[a]&LOS_INLOS)) { globalAI->UnitLeftLos(this,a); globalAI->UnitLeftRadar(this,a); } else { globalAI->UnitLeftRadar(this,a); } losStatus[a]&= ~(LOS_INLOS | LOS_INRADAR | LOS_CONTRADAR); } } } if(paralyzeDamage>0) { paralyzeDamage-=maxHealth*(16.f/30.f/40.f); if(paralyzeDamage<0) paralyzeDamage=0; if(paralyzeDamage<health) stunned=false; } if(stunned) { isCloaked=false; return; } if(selfDCountdown && !stunned) { selfDCountdown--; if(selfDCountdown<=1) { if(!beingBuilt) KillUnit(true,false,0); else KillUnit(false,true,0); //avoid unfinished buildings making an explosion selfDCountdown=0; return; } ENTER_MIXED; if(selfDCountdown&1 && team==gu->myTeam) info->AddLine("%s: Self destruct in %i s",unitDef->humanName.c_str(),selfDCountdown/2); ENTER_SYNCED; } if(beingBuilt) { if(lastNanoAdd<gs->frameNum-200) { health-=maxHealth/(buildTime*0.03); buildProgress-=1/(buildTime*0.03); AddMetal(metalCost/(buildTime*0.03)); if(health<0) KillUnit(false,true,0); } return; } //below is stuff that shouldnt be run while being built lastSlowUpdate=gs->frameNum; commandAI->SlowUpdate(); moveType->SlowUpdate(); metalMake = metalMakeI + metalMakeold; metalUse = metalUseI+ metalUseold; energyMake = energyMakeI + energyMakeold; energyUse = energyUseI + energyUseold; metalMakeold = metalMakeI; metalUseold = metalUseI; energyMakeold = energyMakeI; energyUseold = energyUseI; metalMakeI=metalUseI=energyMakeI=energyUseI=0; AddMetal(unitDef->metalMake*0.5f); if(activated) { if(UseEnergy(unitDef->energyUpkeep*0.5f)) { if(unitDef->isMetalMaker) { AddMetal(unitDef->makesMetal*0.5f*uh->metalMakerEfficiency); uh->metalMakerIncome+=unitDef->makesMetal; } else { AddMetal(unitDef->makesMetal*0.5f); } if(unitDef->extractsMetal>0) AddMetal(metalExtract * 0.5f); } UseMetal(unitDef->metalUpkeep*0.5f); if(unitDef->windGenerator>0) { if(wind.curStrength > unitDef->windGenerator) { AddEnergy(unitDef->windGenerator*0.5f); } else { AddEnergy(wind.curStrength*0.5f); } } } AddEnergy(energyTickMake*0.5f); if(health<maxHealth) { health += unitDef->autoHeal; if(restTime > unitDef->idleTime) { health += unitDef->idleAutoHeal; } if(health>maxHealth) health=maxHealth; } bonusShieldSaved+=0.05f; residualImpulse*=0.6; if(wantCloak) { if(helper->GetClosestEnemyUnitNoLosTest(pos,unitDef->decloakDistance,allyteam)) { curCloakTimeout=gs->frameNum+cloakTimeout; isCloaked=false; } if(isCloaked || gs->frameNum>=curCloakTimeout) { float cloakCost=unitDef->cloakCost; if(speed.SqLength()>0.2) cloakCost=unitDef->cloakCostMoving; if(UseEnergy(cloakCost * 0.5f)) { isCloaked=true; } else { isCloaked=false; } } else { isCloaked=false; } } else { isCloaked=false; } if(uh->waterDamage && (physicalState==CSolidObject::Floating || (physicalState==CSolidObject::OnGround && pos.y<=-3 && readmap->mipHeightmap[1][int((pos.z/(SQUARE_SIZE*2))*gs->hmapx+(pos.x/(SQUARE_SIZE*2)))]<-1))) { DoDamage(DamageArray()*uh->waterDamage,0,ZeroVector); } if(unitDef->canKamikaze) { if(fireState==2) { CUnit* u=helper->GetClosestEnemyUnitNoLosTest(pos,unitDef->kamikazeDist,allyteam); if(u && u->physicalState!=CSolidObject::Flying && u->speed.dot(pos - u->pos)<=0) //self destruct when unit start moving away from mine, should maximize damage KillUnit(true,false,0); } if(userTarget && userTarget->pos.distance(pos)<unitDef->kamikazeDist) KillUnit(true,false,0); if(userAttackGround && userAttackPos.distance(pos)<unitDef->kamikazeDist) KillUnit(true,false,0); } if(!weapons.empty()) { haveTarget=false; haveUserTarget=false; //aircraft does not want this if (moveType->useHeading) { frontdir=GetVectorFromHeading(heading); if(upright || !unitDef->canmove) { updir=UpVector; rightdir=frontdir.cross(updir); } else { updir=ground->GetNormal(pos.x,pos.z); rightdir=frontdir.cross(updir); rightdir.Normalize(); frontdir=updir.cross(rightdir); } } if(!dontFire) { for(vector<CWeapon*>::iterator wi=weapons.begin(); wi!=weapons.end(); ++wi) { CWeapon* w=*wi; if(userTarget && !w->haveUserTarget && (haveDGunRequest || !unitDef->canDGun || !w->weaponDef->manualfire)) w->AttackUnit(userTarget,true); else if(userAttackGround && !w->haveUserTarget && (haveDGunRequest || !unitDef->canDGun || !w->weaponDef->manualfire)) w->AttackGround(userAttackPos,true); w->SlowUpdate(); if(w->targetType==Target_None && fireState>0 && lastAttacker && lastAttack+200>gs->frameNum) w->AttackUnit(lastAttacker,false); } } } if(moveType->progressState == CMoveType::Active) { if(/*physicalState == OnGround*/seismicSignature && !(losStatus[gu->myAllyTeam] & LOS_INLOS) && radarhandler->InSeismicDistance(this, gu->myAllyTeam)) new CSimpleGroundFlash(pos + float3(radarhandler->radarErrorSize[gu->myAllyTeam]*(0.5f-gu->usRandFloat()),0,radarhandler->radarErrorSize[gu->myAllyTeam]*(0.5f-gu->usRandFloat())), ph->seismictex, 30, 15, 0, seismicSignature, 1, float3(0.8,0.0,0.0)); } CalculateTerrainType(); UpdateTerrainType(); }
void CUnit::Draw() { glPushMatrix(); float3 interPos=pos+speed*gu->timeOffset; if (physicalState == Flying && unitDef->canmove) { //aircraft or skidding ground unit CMatrix44f transMatrix(interPos,-rightdir,updir,frontdir); glMultMatrixf(&transMatrix[0]); } else if(upright || !unitDef->canmove) { glTranslatef3(interPos); if(heading!=0) glRotatef(heading*(180.0/32768.0),0,1,0); } else { float3 frontDir=GetVectorFromHeading(heading); //making local copies of vectors float3 upDir=ground->GetSmoothNormal(pos.x,pos.z); float3 rightDir=frontDir.cross(upDir); rightDir.Normalize(); frontDir=upDir.cross(rightDir); CMatrix44f transMatrix(interPos,-rightDir,upDir,frontDir); glMultMatrixf(&transMatrix[0]); } if(beingBuilt && unitDef->showNanoFrame) { if(shadowHandler->inShadowPass) { if(buildProgress>0.66) localmodel->Draw(); } else { float height=model->height; float start=model->miny; glEnable(GL_CLIP_PLANE0); glEnable(GL_CLIP_PLANE1); //float col=fabs(128.0-((gs->frameNum*4)&255))/255.0+0.5f; float3 fc;// fc frame color if(gu->teamNanospray) { unsigned char* tcol=gs->Team(team)->color; fc = float3(tcol[0]*(1./255.),tcol[1]*(1./255.),tcol[2]*(1./255.)); } else { fc = unitDef->nanoColor; } glColorf3(fc); unitDrawer->UnitDrawingTexturesOff(model); double plane[4]= {0,-1,0,start+height*buildProgress*3}; glClipPlane(GL_CLIP_PLANE0 ,plane); double plane2[4]= {0,1,0,-start-height*(buildProgress*10-9)}; glClipPlane(GL_CLIP_PLANE1 ,plane2); glPolygonMode(GL_FRONT_AND_BACK,GL_LINE); localmodel->Draw(); glPolygonMode(GL_FRONT_AND_BACK,GL_FILL); if(buildProgress>0.33) { glColorf3(fc*1.4f); double plane[4]= {0,-1,0,start+height*(buildProgress*3-1)}; glClipPlane(GL_CLIP_PLANE0 ,plane); double plane2[4]= {0,1,0,-start-height*(buildProgress*3-2)}; glClipPlane(GL_CLIP_PLANE1 ,plane2); localmodel->Draw(); } glDisable(GL_CLIP_PLANE1); unitDrawer->UnitDrawingTexturesOn(model); if(buildProgress>0.66) { double plane[4]= {0,-1,0,start+height*(buildProgress*3-2)}; glClipPlane(GL_CLIP_PLANE0 ,plane); if(shadowHandler->drawShadows && !water->drawReflection) { glPolygonOffset(1,1); glEnable(GL_POLYGON_OFFSET_FILL); } localmodel->Draw(); if(shadowHandler->drawShadows && !water->drawReflection) { glDisable(GL_POLYGON_OFFSET_FILL); } } glDisable(GL_CLIP_PLANE0); } } else { localmodel->Draw(); } if(gu->drawdebug) { glPushMatrix(); glTranslatef3(frontdir*relMidPos.z + updir*relMidPos.y + rightdir*relMidPos.x); GLUquadricObj* q=gluNewQuadric(); gluQuadricDrawStyle(q,GLU_LINE); gluSphere(q,radius,10,10); gluDeleteQuadric(q); glPopMatrix(); }/**/ glPopMatrix(); }
CUnit* CUnitLoader::LoadUnit(const UnitDef* ud, float3 pos, int team, bool build, int facing, const CUnit* builder) { GML_RECMUTEX_LOCK(sel); // LoadUnit - for anti deadlock purposes. GML_RECMUTEX_LOCK(quad); // LoadUnit - make sure other threads cannot access an incomplete unit SCOPED_TIMER("Unit loader"); CUnit* unit; std::string type = ud->type; // clamp to map if (pos.x < 0) pos.x = 0; if (pos.x >= gs->mapx * SQUARE_SIZE) pos.x = gs->mapx-1; if (pos.z < 0) pos.z = 0; if (pos.z >= gs->mapy * SQUARE_SIZE) pos.z = gs->mapy-1; if (!build) { pos.y = ground->GetHeight2(pos.x, pos.z); if (ud->floater && pos.y < 0.0f) { // adjust to waterline iif we are submerged pos.y = -ud->waterline; } } if (team < 0) { team = teamHandler->GaiaTeamID(); // FIXME use gs->gaiaTeamID ? (once it is always enabled) if (team < 0) throw content_error("Invalid team and no gaia team to put unit in"); } if (type == "GroundUnit") { unit = new CUnit; } else if (type == "Transport") { unit = new CTransportUnit; } else if (type == "Building") { unit = new CBuilding; } else if (type == "Factory") { unit = new CFactory; } else if (type == "Builder") { unit = new CBuilder; } else if (type == "Bomber" || type == "Fighter") { unit = new CUnit; } else if (type == "MetalExtractor") { unit = new CExtractorBuilding; } else { LogObject() << "Unknown unit type " << type.c_str() << "\n"; return NULL; } unit->UnitInit(ud, team, pos); unit->beingBuilt = build; unit->buildFacing = abs(facing) % 4; unit->xsize = ((unit->buildFacing & 1) == 0) ? ud->xsize : ud->zsize; unit->zsize = ((unit->buildFacing & 1) == 1) ? ud->xsize : ud->zsize; unit->power = ud->power; unit->maxHealth = ud->health; unit->health = ud->health; //unit->metalUpkeep = ud->metalUpkeep*16.0f/GAME_SPEED; //unit->energyUpkeep = ud->energyUpkeep*16.0f/GAME_SPEED; unit->controlRadius = (int)(ud->controlRadius / SQUARE_SIZE); unit->losHeight = ud->losHeight; unit->metalCost = ud->metalCost; unit->energyCost = ud->energyCost; unit->buildTime = ud->buildTime; unit->aihint = ud->aihint; unit->tooltip = ud->humanName + " - " + ud->tooltip; unit->armoredMultiple = std::max(0.0001f, ud->armoredMultiple); //armored multiple of 0 will crash spring unit->wreckName = ud->wreckName; unit->realLosRadius = (int) (ud->losRadius); unit->realAirLosRadius = (int) (ud->airLosRadius); unit->upright = ud->upright; unit->radarRadius = ud->radarRadius / (SQUARE_SIZE * 8); unit->sonarRadius = ud->sonarRadius / (SQUARE_SIZE * 8); unit->jammerRadius = ud->jammerRadius / (SQUARE_SIZE * 8); unit->sonarJamRadius = ud->sonarJamRadius / (SQUARE_SIZE * 8); unit->seismicRadius = ud->seismicRadius / (SQUARE_SIZE * 8); unit->seismicSignature = ud->seismicSignature; unit->hasRadarCapacity = unit->radarRadius || unit->sonarRadius || unit->jammerRadius || unit->sonarJamRadius || unit->seismicRadius; unit->stealth = ud->stealth; unit->sonarStealth = ud->sonarStealth; unit->category = ud->category; unit->armorType = ud->armorType; unit->floatOnWater = ud->floater || (ud->movedata && ((ud->movedata->moveType == MoveData::Hover_Move) || (ud->movedata->moveType == MoveData::Ship_Move))); unit->maxSpeed = ud->speed / GAME_SPEED; unit->decloakDistance = ud->decloakDistance; unit->flankingBonusMode = ud->flankingBonusMode; unit->flankingBonusDir = ud->flankingBonusDir; unit->flankingBonusMobility = ud->flankingBonusMobilityAdd * 1000; unit->flankingBonusMobilityAdd = ud->flankingBonusMobilityAdd; unit->flankingBonusAvgDamage = (ud->flankingBonusMax + ud->flankingBonusMin) * 0.5f; unit->flankingBonusDifDamage = (ud->flankingBonusMax - ud->flankingBonusMin) * 0.5f; if(ud->highTrajectoryType==1) unit->useHighTrajectory=true; if(ud->fireState >= 0) unit->fireState = ud->fireState; if(build){ unit->ChangeLos(1,1); unit->health=0.1f; } else { unit->ChangeLos((int)(ud->losRadius),(int)(ud->airLosRadius)); } if (type == "GroundUnit") { new CMobileCAI(unit); } else if (type == "Transport") { new CTransportCAI(unit); } else if (type == "Factory") { new CFactoryCAI(unit); } else if (type == "Builder") { new CBuilderCAI(unit); } else if (type == "Bomber") { if (ud->hoverAttack) { new CMobileCAI(unit); } else { new CAirCAI(unit); } } else if(type == "Fighter"){ if (ud->hoverAttack) { new CMobileCAI(unit); } else { new CAirCAI(unit); } } else { new CCommandAI(unit); } if (ud->canmove && !ud->canfly && (type != "Factory")) { CGroundMoveType* mt = new CGroundMoveType(unit); mt->maxSpeed = ud->speed / GAME_SPEED; mt->maxWantedSpeed = ud->speed / GAME_SPEED; mt->turnRate = ud->turnRate; mt->baseTurnRate = ud->turnRate; if (mt->accRate <= 0.0f) { LogObject() << "acceleration of unit-type " << ud->name.c_str() << " is zero or negative!\n"; mt->accRate = 0.01f; } mt->accRate = ud->maxAcc; mt->decRate = ud->maxDec; mt->floatOnWater = (ud->movedata->moveType == MoveData::Hover_Move || ud->movedata->moveType == MoveData::Ship_Move); if (!unit->beingBuilt) { // otherwise set this when finished building instead unit->mass = ud->mass; } unit->moveType = mt; // Ground-mobility unit->mobility = new MoveData(ud->movedata); } else if (ud->canfly) { // Air-mobility unit->mobility = new MoveData(ud->movedata); if (!unit->beingBuilt) { // otherwise set this when finished building instead unit->mass = ud->mass; } if ((type == "Builder") || ud->hoverAttack || ud->transportCapacity) { CTAAirMoveType* mt = new CTAAirMoveType(unit); mt->turnRate = ud->turnRate; mt->maxSpeed = ud->speed / GAME_SPEED; mt->accRate = ud->maxAcc; mt->decRate = ud->maxDec; mt->wantedHeight = ud->wantedHeight + gs->randFloat() * 5; mt->orgWantedHeight = mt->wantedHeight; mt->dontLand = ud->DontLand(); mt->collide = ud->collide; mt->altitudeRate = ud->verticalSpeed; mt->bankingAllowed = ud->bankingAllowed; unit->moveType = mt; } else { CAirMoveType *mt = new CAirMoveType(unit); if(type=="Fighter") mt->isFighter=true; mt->collide = ud->collide; mt->wingAngle = ud->wingAngle; mt->crashDrag = 1 - ud->crashDrag; mt->invDrag = 1 - ud->drag; mt->frontToSpeed = ud->frontToSpeed; mt->speedToFront = ud->speedToFront; mt->myGravity = ud->myGravity; mt->maxBank = ud->maxBank; mt->maxPitch = ud->maxPitch; mt->turnRadius = ud->turnRadius; mt->wantedHeight = (ud->wantedHeight * 1.5f) + ((gs->randFloat() - 0.3f) * 15 * (mt->isFighter ? 2 : 1)); mt->maxAcc = ud->maxAcc; mt->maxAileron = ud->maxAileron; mt->maxElevator = ud->maxElevator; mt->maxRudder = ud->maxRudder; unit->moveType = mt; } } else { unit->moveType = new CMoveType(unit); unit->upright = true; } unit->energyTickMake = ud->energyMake; if (ud->tidalGenerator > 0) unit->energyTickMake += ud->tidalGenerator * mapInfo->map.tidalStrength; unit->model = ud->LoadModel(); unit->SetRadius(unit->model->radius); modelParser->CreateLocalModel(unit); // copy the UnitDef volume instance // // aircraft still get half-size spheres for coldet purposes // iif no custom volume is defined (unit->model->radius and // unit->radius themselves are no longer altered) unit->collisionVolume = new CollisionVolume(ud->collisionVolume, unit->model->radius * ((ud->canfly)? 0.5f: 1.0f)); if (unit->model->radius <= 60.0f) { // the interval-based method fails too easily for units // with small default volumes, force use of raytracing unit->collisionVolume->SetTestType(COLVOL_TEST_CONT); } if (ud->floater) { // restrict our depth to our waterline unit->pos.y = std::max(-ud->waterline, ground->GetHeight2(unit->pos.x, unit->pos.z)); } else { unit->pos.y = ground->GetHeight2(unit->pos.x, unit->pos.z); } unit->script = CUnitScriptFactory::CreateScript(ud->scriptPath, unit); unit->weapons.reserve(ud->weapons.size()); for (unsigned int i = 0; i < ud->weapons.size(); i++) { unit->weapons.push_back(LoadWeapon(ud->weapons[i].def, unit, &ud->weapons[i])); } // Call initializing script functions unit->script->Create(); unit->heading = GetHeadingFromFacing(facing); unit->frontdir = GetVectorFromHeading(unit->heading); unit->updir = UpVector; unit->rightdir = unit->frontdir.cross(unit->updir); unit->yardMap = ud->yardmaps[facing]; unit->Init(builder); if (!build) { unit->FinishedBuilding(); } return unit; }
CUnit* CUnitLoader::LoadUnit(const string& name, float3 pos, int team, bool build, int facing, const CUnit* builder) { // GML_RECMUTEX_LOCK(unit); // LoadUnit. Unitinit puts unit in the quadfield and activeUnits - GML_RECMUTEX_LOCK(sel); // LoadUnit. For anti deadlock purposes. GML_RECMUTEX_LOCK(quad); // LoadUnit. - make sure other threads cannot access an incomplete unit CUnit* unit; SCOPED_TIMER("Unit loader"); const UnitDef* ud = unitDefHandler->GetUnitByName(name); if (!ud) { throw content_error("Couldn't find unittype " + name); } string type = ud->type; if (!build) { pos.y = ground->GetHeight2(pos.x, pos.z); if (ud->floater && pos.y < 0.0f) { // adjust to waterline iif we are submerged pos.y = -ud->waterline; } } if (team < 0) { team = MAX_TEAMS - 1; // FIXME use gs->gaiaTeamID ? (once it is always enabled) } if (type == "GroundUnit") { unit = SAFE_NEW CUnit; } else if (type == "Transport") { unit = SAFE_NEW CTransportUnit; } else if (type == "Building") { unit = SAFE_NEW CBuilding; } else if (type == "Factory") { unit = SAFE_NEW CFactory; } else if (type == "Builder") { unit = SAFE_NEW CBuilder; } else if (type == "Bomber" || type == "Fighter") { unit = SAFE_NEW CUnit; } else if (type == "MetalExtractor") { unit = SAFE_NEW CExtractorBuilding; } else { logOutput << "Unknown unit type " << type.c_str() << "\n"; return NULL; } unit->UnitInit(ud, team, pos); unit->beingBuilt = build; unit->xsize = ((facing & 1) == 0) ? ud->xsize : ud->zsize; unit->zsize = ((facing & 1) == 1) ? ud->xsize : ud->zsize; unit->buildFacing = facing; unit->power = ud->power; unit->maxHealth = ud->health; unit->health = ud->health; //unit->metalUpkeep = ud->metalUpkeep*16.0f/GAME_SPEED; //unit->energyUpkeep = ud->energyUpkeep*16.0f/GAME_SPEED; unit->controlRadius = (int)(ud->controlRadius / SQUARE_SIZE); unit->losHeight = ud->losHeight; unit->metalCost = ud->metalCost; unit->energyCost = ud->energyCost; unit->buildTime = ud->buildTime; unit->aihint = ud->aihint; unit->tooltip = ud->humanName + " - " + ud->tooltip; unit->armoredMultiple = std::max(0.0001f, ud->armoredMultiple); //armored multiple of 0 will crash spring unit->wreckName = ud->wreckName; unit->realLosRadius = (int) (ud->losRadius); unit->realAirLosRadius = (int) (ud->airLosRadius); unit->upright = ud->upright; unit->radarRadius = ud->radarRadius / (SQUARE_SIZE * 8); unit->sonarRadius = ud->sonarRadius / (SQUARE_SIZE * 8); unit->jammerRadius = ud->jammerRadius / (SQUARE_SIZE * 8); unit->sonarJamRadius = ud->sonarJamRadius / (SQUARE_SIZE * 8); unit->seismicRadius = ud->seismicRadius / (SQUARE_SIZE * 8); unit->seismicSignature = ud->seismicSignature; unit->hasRadarCapacity = unit->radarRadius || unit->sonarRadius || unit->jammerRadius || unit->sonarJamRadius || unit->seismicRadius; unit->stealth = ud->stealth; unit->sonarStealth = ud->sonarStealth; unit->category = ud->category; unit->armorType = ud->armorType; unit->floatOnWater = ud->floater || (ud->movedata && ((ud->movedata->moveType == MoveData::Hover_Move) || (ud->movedata->moveType == MoveData::Ship_Move))); unit->maxSpeed = ud->speed / 30.0f; unit->decloakDistance = ud->decloakDistance; unit->flankingBonusMode = ud->flankingBonusMode; unit->flankingBonusDir = ud->flankingBonusDir; unit->flankingBonusMobility = ud->flankingBonusMobilityAdd * 1000; unit->flankingBonusMobilityAdd = ud->flankingBonusMobilityAdd; unit->flankingBonusAvgDamage = (ud->flankingBonusMax + ud->flankingBonusMin) * 0.5f; unit->flankingBonusDifDamage = (ud->flankingBonusMax - ud->flankingBonusMin) * 0.5f; if(ud->highTrajectoryType==1) unit->useHighTrajectory=true; if(ud->fireState >= 0) unit->fireState = ud->fireState; if(build){ unit->ChangeLos(1,1); unit->health=0.1f; } else { unit->ChangeLos((int)(ud->losRadius),(int)(ud->airLosRadius)); } if (type == "GroundUnit") { SAFE_NEW CMobileCAI(unit); } else if (type == "Transport") { SAFE_NEW CTransportCAI(unit); } else if (type == "Factory") { SAFE_NEW CFactoryCAI(unit); } else if (type == "Builder") { SAFE_NEW CBuilderCAI(unit); } else if (type == "Bomber") { if (ud->hoverAttack) { SAFE_NEW CMobileCAI(unit); } else { SAFE_NEW CAirCAI(unit); } } else if(type == "Fighter"){ if (ud->hoverAttack) { SAFE_NEW CMobileCAI(unit); } else { SAFE_NEW CAirCAI(unit); } } else { SAFE_NEW CCommandAI(unit); } if (ud->canmove && !ud->canfly && (type != "Factory")) { CGroundMoveType* mt = SAFE_NEW CGroundMoveType(unit); mt->maxSpeed = ud->speed / GAME_SPEED; mt->maxWantedSpeed = ud->speed / GAME_SPEED; mt->turnRate = ud->turnRate; mt->baseTurnRate = ud->turnRate; if (!mt->accRate) { logOutput << "acceleration of " << ud->name.c_str() << " is zero!!\n"; } mt->moveType = ud->moveType; mt->accRate = ud->maxAcc; mt->decRate = ud->maxDec; mt->floatOnWater = (ud->movedata->moveType == MoveData::Hover_Move || ud->movedata->moveType == MoveData::Ship_Move); if (!unit->beingBuilt) { // otherwise set this when finished building instead unit->mass = ud->mass; } unit->moveType = mt; // Ground-mobility unit->mobility = SAFE_NEW MoveData(ud->movedata, GAME_SPEED); } else if (ud->canfly) { // Air-mobility unit->mobility = SAFE_NEW MoveData(ud->movedata, GAME_SPEED); if (!unit->beingBuilt) { // otherwise set this when finished building instead unit->mass = ud->mass; } if ((type == "Builder") || ud->hoverAttack || ud->transportCapacity) { CTAAirMoveType* mt = SAFE_NEW CTAAirMoveType(unit); mt->turnRate = ud->turnRate; mt->maxSpeed = ud->speed / GAME_SPEED; mt->accRate = ud->maxAcc; mt->decRate = ud->maxDec; mt->wantedHeight = ud->wantedHeight + gs->randFloat() * 5; mt->orgWantedHeight = mt->wantedHeight; mt->dontLand = ud->DontLand(); mt->collide = ud->collide; mt->altitudeRate = ud->verticalSpeed; mt->bankingAllowed = ud->bankingAllowed; unit->moveType = mt; } else { CAirMoveType *mt = SAFE_NEW CAirMoveType(unit); if(type=="Fighter") mt->isFighter=true; mt->collide = ud->collide; mt->wingAngle = ud->wingAngle; mt->crashDrag = 1 - ud->crashDrag; mt->invDrag = 1 - ud->drag; mt->frontToSpeed = ud->frontToSpeed; mt->speedToFront = ud->speedToFront; mt->myGravity = ud->myGravity; mt->maxBank = ud->maxBank; mt->maxPitch = ud->maxPitch; mt->turnRadius = ud->turnRadius; mt->wantedHeight = (ud->wantedHeight * 1.5f) + ((gs->randFloat() - 0.3f) * 15 * (mt->isFighter ? 2 : 1)); mt->maxAcc = ud->maxAcc; mt->maxAileron = ud->maxAileron; mt->maxElevator = ud->maxElevator; mt->maxRudder = ud->maxRudder; unit->moveType = mt; } } else { unit->moveType = SAFE_NEW CMoveType(unit); unit->upright = true; } unit->energyTickMake = ud->energyMake; if (ud->tidalGenerator > 0) unit->energyTickMake += ud->tidalGenerator * mapInfo->map.tidalStrength; unit->model = LoadModel(ud); unit->SetRadius(unit->model->radius); // copy the UnitDef volume archetype data unit->collisionVolume = SAFE_NEW CollisionVolume(ud->collisionVolume); // if no "collisionVolumeScales" tag was defined in UnitDef, // the default scale for this volume will be a ZeroVector if (unit->collisionVolume->GetScale(COLVOL_AXIS_X) <= 1.0f && unit->collisionVolume->GetScale(COLVOL_AXIS_Y) <= 1.0f && unit->collisionVolume->GetScale(COLVOL_AXIS_Z) <= 1.0f) { // aircraft still get half-size spheres for coldet purposes // if no custom volume is defined (unit->model->radius and // unit->radius themselves are no longer altered) const float scaleFactor = (ud->canfly)? 0.5f: 1.0f; unit->collisionVolume->SetDefaultScale(unit->model->radius * scaleFactor); if (unit->collisionVolume->GetBoundingRadius() <= 30.0f) { // the interval-based method fails too easily for units // with small default volumes, force use of raytracing unit->collisionVolume->SetTestType(COLVOL_TEST_CONT); } } if (ud->floater) { // restrict our depth to our waterline unit->pos.y = std::max(-ud->waterline, ground->GetHeight2(unit->pos.x, unit->pos.z)); } else { unit->pos.y = ground->GetHeight2(unit->pos.x, unit->pos.z); } modelParser->CreateLocalModel(unit); unit->cob = SAFE_NEW CCobInstance(GCobEngine.GetCobFile("scripts/" + ud->cobFilename), unit); unit->weapons.reserve(ud->weapons.size()); for (unsigned int i = 0; i < ud->weapons.size(); i++) { unit->weapons.push_back(LoadWeapon(ud->weapons[i].def, unit, &ud->weapons[i])); } // Calculate the max() of the available weapon reloadtimes int relMax = 0; for (vector<CWeapon*>::iterator i = unit->weapons.begin(); i != unit->weapons.end(); ++i) { if ((*i)->reloadTime > relMax) relMax = (*i)->reloadTime; if (dynamic_cast<CBeamLaser*>(*i)) relMax = 150; } // convert ticks to milliseconds relMax *= 30; // TA does some special handling depending on weapon count if (unit->weapons.size() > 1) { relMax = std::max(relMax, 3000); } // Call initializing script functions unit->cob->Call(COBFN_Create); unit->cob->Call("SetMaxReloadTime", relMax); unit->heading = GetHeadingFromFacing(facing); unit->frontdir = GetVectorFromHeading(unit->heading); unit->updir = UpVector; unit->rightdir = unit->frontdir.cross(unit->updir); unit->yardMap = ud->yardmaps[facing]; unit->Init(builder); if (!build) { unit->FinishedBuilding(); } return unit; }