void CTransportUnit::KillUnit(bool selfDestruct,bool reclaimed, CUnit *attacker) { list<TransportedUnit>::iterator ti; for (ti = transported.begin(); ti != transported.end(); ++ti) { ti->unit->transporter = 0; ti->unit->DeleteDeathDependence(this); if (!unitDef->releaseHeld) { if (!selfDestruct) { ti->unit->DoDamage(DamageArray()*1000000,0,ZeroVector); //dont want it to leave a corpse } ti->unit->KillUnit(selfDestruct,reclaimed,attacker); } else { ti->unit->stunned = (ti->unit->paralyzeDamage > ti->unit->health); if (CGroundMoveType* mt = dynamic_cast<CGroundMoveType*>(ti->unit->moveType)) { mt->StartSkidding(); mt->StartFlying(); } ti->unit->speed = speed; luaCallIns.UnitUnloaded(ti->unit, this); } } CUnit::KillUnit(selfDestruct,reclaimed,attacker); }
void CPieceProjectile::Collision() { if (speed.SqLength() > Square(gs->randFloat() * 5 + 1) && pos.y > radius + 2) { float3 norm = ground->GetNormal(pos.x, pos.z); float ns = speed.dot(norm); speed -= norm * ns * 1.6f; pos += norm * 0.1f; } else { if (flags & PF_Explode) { helper->Explosion(pos, DamageArray(50), 5, 0, 10, owner(), false, 1.0f, false, false, 0, 0, ZeroVector, -1); } if (flags & PF_Smoke) { if (flags & PF_NoCEGTrail) { float3 dir = speed; dir.Normalize(); CSmokeTrailProjectile* tp = new CSmokeTrailProjectile(pos, oldSmoke, dir, oldSmokeDir, owner(), false, true, 7, Smoke_Time, 0.5f, drawTrail, 0, projectileDrawer->smoketrailtex); tp->creationTime += (8 - ((age) & 7)); } } CProjectile::Collision(); oldSmoke = pos; } }
void CFireProjectile::Update(void) { ttl--; if(ttl>0){ if(ph->particleSaturation<0.8 || (ph->particleSaturation<1 && (gs->frameNum & 1))){ //this area must be unsynced SubParticle sub; sub.age=0; sub.maxSize=(0.7+gu->usRandFloat()*0.3)*particleSize; sub.posDif=gu->usRandVector()*emitRadius; sub.pos=emitPos; sub.pos.y+=sub.posDif.y; sub.posDif.y=0; sub.rotSpeed=(gu->usRandFloat()-0.5)*4; sub.smokeType=gu->usRandInt()%6; subParticles.push_front(sub); sub.maxSize=(0.7+gu->usRandFloat()*0.3)*particleSize; sub.posDif=gu->usRandVector()*emitRadius; sub.pos=emitPos; sub.pos.y+=sub.posDif.y-radius*0.3; sub.posDif.y=0; sub.rotSpeed=(gu->usRandFloat()-0.5)*4; subParticles2.push_front(sub); } if(!(ttl&31)){ //this area must be synced std::vector<CFeature*> f=qf->GetFeaturesExact(emitPos+wind.curWind*0.7,emitRadius*2); for(std::vector<CFeature*>::iterator fi=f.begin();fi!=f.end();++fi){ if(gs->randFloat()>0.8) (*fi)->StartFire(); } std::vector<CUnit*> units=qf->GetUnitsExact(emitPos+wind.curWind*0.7,emitRadius*2); for(std::vector<CUnit*>::iterator ui=units.begin();ui!=units.end();++ui){ (*ui)->DoDamage(DamageArray()*30,0,ZeroVector); } } } for(std::list<SubParticle>::iterator pi=subParticles.begin();pi!=subParticles.end();++pi){ pi->age+=ageSpeed; if(pi->age>1){ subParticles.pop_back(); break; } pi->pos+=speed + wind.curWind*pi->age*0.05 + pi->posDif*0.1; pi->posDif*=0.9; } for(std::list<SubParticle>::iterator pi=subParticles2.begin();pi!=subParticles2.end();++pi){ pi->age+=ageSpeed*1.5; if(pi->age>1){ subParticles2.pop_back(); break; } pi->pos+=speed*0.7+pi->posDif*0.1; pi->posDif*=0.9; } if(subParticles.empty() && ttl<=0) deleteMe=true; }
void CTransportUnit::KillUnit(bool selfDestruct,bool reclaimed) { for(list<TransportedUnit>::iterator ti=transported.begin();ti!=transported.end();++ti){ ti->unit->inTransport=false; if(!selfDestruct) ti->unit->DoDamage(DamageArray()*1000000,0,ZeroVector); //dont want it to leave a corpse ti->unit->KillUnit(selfDestruct,reclaimed); } CUnit::KillUnit(selfDestruct,reclaimed); }
void CPieceProjectile::Collision(CUnit* unit) { if(unit==owner) return; if (flags & PP_Explode) { helper->Explosion(pos,DamageArray()*50,5,0,10,owner,false,1.0f, false, 0, unit, ZeroVector, -1); } if(flags & PP_Smoke){ float3 dir=speed; dir.Normalize(); CSmokeTrailProjectile* tp=SAFE_NEW CSmokeTrailProjectile(pos,oldSmoke,dir,oldSmokeDir,owner,false,true,7,Smoke_Time,0.5f,drawTrail); tp->creationTime+=8-((age)&7); } CProjectile::Collision(unit); oldSmoke=pos; }
void CTransportUnit::KillUnit(bool selfDestruct, bool reclaimed, CUnit* attacker, bool) { std::list<TransportedUnit>::iterator ti; for (ti = transported.begin(); ti != transported.end(); ++ti) { CUnit* u = ti->unit; u->transporter = 0; u->DeleteDeathDependence(this); // prevent a position teleport on the next movetype update if // the transport died in a place that the unit being carried // could not get to on its own if (!u->pos.IsInBounds()) { u->KillUnit(false, false, 0x0, false); continue; } else { const float gh = ground->GetHeight2(u->pos.x, u->pos.z); if (gh < -u->unitDef->maxWaterDepth || gh > -u->unitDef->minWaterDepth) { // note: should also check movedef restraints? u->KillUnit(false, false, 0x0, false); continue; } } if (!unitDef->releaseHeld) { if (!selfDestruct) { // we don't want it to leave a corpse u->DoDamage(DamageArray() * 1000000, 0, ZeroVector); } u->KillUnit(selfDestruct, reclaimed, attacker); } else { u->stunned = (u->paralyzeDamage > u->health); if (CGroundMoveType* mt = dynamic_cast<CGroundMoveType*>(u->moveType)) { mt->StartFlying(); } u->speed = speed; eventHandler.UnitUnloaded(u, this); } } CUnit::KillUnit(selfDestruct, reclaimed, attacker); }
void CPieceProjectile::Collision() { if (speed.SqLength() > Square(gs->randFloat() * 5 + 1) && pos.y > radius + 2) { float3 norm = ground->GetNormal(pos.x, pos.z); float ns = speed.dot(norm); speed -= norm * ns * 1.6f; pos += norm * 0.1f; } else { if (flags & PF_Explode) { CGameHelper::ExplosionParams params = { pos, ZeroVector, DamageArray(50), NULL, // weaponDef owner(), NULL, // hitUnit NULL, // hitFeature 5.0f, // areaOfEffect 0.0f, // edgeEffectiveness 10.0f, // explosionSpeed 1.0f, // gfxMod false, // impactOnly false, // ignoreOwner true // damageGround }; helper->Explosion(params); } if (flags & PF_Smoke) { if (flags & PF_NoCEGTrail) { float3 dir = speed; dir.Normalize(); CSmokeTrailProjectile* tp = new CSmokeTrailProjectile(pos, oldSmoke, dir, oldSmokeDir, owner(), false, true, 7, Smoke_Time, 0.5f, drawTrail, 0, projectileDrawer->smoketrailtex); tp->creationTime += (8 - ((age) & 7)); } } CProjectile::Collision(); oldSmoke = pos; } }
void CPieceProjectile::Collision() { if(speed.Length()>gs->randFloat()*5+1 && pos.y>radius+2){ float3 norm=ground->GetNormal(pos.x,pos.z); float ns=speed.dot(norm); speed-=norm*ns*1.6f; pos+=norm*0.1f; } else { if (flags & PP_Explode) { helper->Explosion(pos,DamageArray()*50,5,0,10,owner,false,1.0f,false,0,0,ZeroVector, -1); } if(flags & PP_Smoke){ float3 dir=speed; dir.Normalize(); CSmokeTrailProjectile* tp=SAFE_NEW CSmokeTrailProjectile(pos,oldSmoke,dir,oldSmokeDir,owner,false,true,7,Smoke_Time,0.5f,drawTrail); tp->creationTime+=8-((age)&7); } CProjectile::Collision(); oldSmoke=pos; } }
void CPieceProjectile::Collision(CUnit* unit) { if (unit == owner()) { return; } if (flags & PF_Explode) { CGameHelper::ExplosionParams params = { pos, ZeroVector, DamageArray(50), NULL, // weaponDef owner(), unit, // hitUnit NULL, // hitFeature 5.0f, // areaOfEffect 0.0f, // edgeEffectiveness 10.0f, // explosionSpeed 1.0f, // gfxMod false, // impactOnly false, // ignoreOwner true // damageGround }; helper->Explosion(params); } if (flags & PF_Smoke) { if (flags & PF_NoCEGTrail) { float3 dir = speed; dir.Normalize(); CSmokeTrailProjectile* tp = new CSmokeTrailProjectile(pos, oldSmoke, dir, oldSmokeDir, owner(), false, true, 7, Smoke_Time, 0.5f, drawTrail, 0, projectileDrawer->smoketrailtex); tp->creationTime += (8 - ((age) & 7)); } } CProjectile::Collision(unit); oldSmoke = pos; }
void CPieceProjectile::Collision(CUnit* unit) { if (unit == owner()) return; if (flags & PF_Explode) { helper->Explosion(pos, DamageArray() * 50, 5, 0, 10, owner(), false, 1.0f, false, false, 0, unit, ZeroVector, -1); } if (flags & PF_Smoke) { if (flags & PF_NoCEGTrail) { float3 dir = speed; dir.Normalize(); CSmokeTrailProjectile* tp = new CSmokeTrailProjectile(pos, oldSmoke, dir, oldSmokeDir, owner(), false, true, 7, Smoke_Time, 0.5f, drawTrail, 0, &ph->smoketrailtex); tp->creationTime += (8 - ((age) & 7)); } } CProjectile::Collision(unit); oldSmoke = pos; }
void CFireProjectile::Update() { ttl--; if (ttl > 0) { if (projectileHandler->particleSaturation < 0.8f || (projectileHandler->particleSaturation < 1 && (gs->frameNum & 1))) { //! unsynced code SubParticle sub; sub.age = 0; sub.maxSize = (0.7f + gu->RandFloat()*0.3f) * particleSize; sub.posDif = gu->RandVector() * emitRadius; sub.pos = emitPos; sub.pos.y += sub.posDif.y; sub.posDif.y = 0; sub.rotSpeed = (gu->RandFloat() - 0.5f) * 4; sub.smokeType = gu->RandInt() % projectileDrawer->smoketex.size(); subParticles.push_front(sub); sub.maxSize = (0.7f + gu->RandFloat()*0.3f) * particleSize; sub.posDif = gu->RandVector() * emitRadius; sub.pos = emitPos; sub.pos.y += sub.posDif.y - radius*0.3f; sub.posDif.y = 0; sub.rotSpeed=(gu->RandFloat() - 0.5f) * 4; subParticles2.push_front(sub); } if (!(ttl & 31)) { //! synced code const std::vector<CFeature*>& features = quadField->GetFeaturesExact(emitPos + wind.GetCurrentWind() * 0.7f, emitRadius * 2); const std::vector<CUnit*>& units = quadField->GetUnitsExact(emitPos + wind.GetCurrentWind() * 0.7f, emitRadius * 2); for (std::vector<CFeature*>::const_iterator fi = features.begin(); fi != features.end(); ++fi) { if (gs->randFloat() > 0.8f) { (*fi)->StartFire(); } } for (std::vector<CUnit*>::const_iterator ui = units.begin(); ui != units.end(); ++ui) { (*ui)->DoDamage(DamageArray(30), ZeroVector, NULL, -CSolidObject::DAMAGE_EXTSOURCE_FIRE, -1); } } } for(part_list_type::iterator pi=subParticles.begin();pi!=subParticles.end();++pi){ pi->age+=ageSpeed; if(pi->age>1){ subParticles.pop_back(); break; } pi->pos+=speed + wind.GetCurrentWind()*pi->age*0.05f + pi->posDif*0.1f; pi->posDif*=0.9f; } for(part_list_type::iterator pi=subParticles2.begin();pi!=subParticles2.end();++pi){ pi->age+=ageSpeed*1.5f; if(pi->age>1){ subParticles2.pop_back(); break; } pi->pos+=speed*0.7f+pi->posDif*0.1f; pi->posDif*=0.9f; } if (subParticles.empty() && (ttl <= 0)) { deleteMe = true; } }
void CAirMoveType::Update(void) { float3 &pos=owner->pos; //This is only set to false after the plane has finished constructing if (useHeading){ useHeading = false; SetState(AIRCRAFT_TAKEOFF); } if(owner->stunned){ UpdateAirPhysics(0,lastAileronPos,lastElevatorPos,0,ZeroVector); goto EndNormalControl; } #ifdef DIRECT_CONTROL_ALLOWED if(owner->directControl && !(aircraftState==AIRCRAFT_CRASHING)){ SetState(AIRCRAFT_FLYING); DirectControlStruct* dc=owner->directControl; inefficientAttackTime=0; if(dc->forward || dc->back || dc->left || dc->right){ float aileron=0; float elevator=0; if(dc->forward) elevator-=1; if(dc->back) elevator+=1; if(dc->right) aileron+=1; if(dc->left) aileron-=1; UpdateAirPhysics(0,aileron,elevator,1,owner->frontdir); maneuver=0; goto EndNormalControl; //ok so goto is bad i know } } #endif if(reservedPad){ CUnit* unit=reservedPad->unit; float3 relPos=unit->localmodel->GetPiecePos(reservedPad->piece); float3 pos=unit->pos + unit->frontdir*relPos.z + unit->updir*relPos.y + unit->rightdir*relPos.x; if(padStatus==0){ if(aircraftState!=AIRCRAFT_FLYING && aircraftState!=AIRCRAFT_TAKEOFF) SetState(AIRCRAFT_FLYING); goalPos=pos; if(pos.distance(owner->pos)<400){ padStatus=1; } // geometricObjects->AddLine(owner->pos,pos,1,0,1); } else if(padStatus==1){ if(aircraftState!=AIRCRAFT_LANDING) SetState(AIRCRAFT_LANDING); goalPos=pos; reservedLandingPos=pos; if(owner->pos.distance(pos)<3 || aircraftState==AIRCRAFT_LANDED){ padStatus=2; } // geometricObjects->AddLine(owner->pos,pos,10,0,1); } else { if(aircraftState!=AIRCRAFT_LANDED) SetState(AIRCRAFT_LANDED); owner->pos=pos; owner->AddBuildPower(20,unit); if(owner->health>=owner->maxHealth-1){ airBaseHandler->LeaveLandingPad(reservedPad); reservedPad=0; padStatus=0; goalPos=oldGoalPos; SetState(AIRCRAFT_TAKEOFF); } } } switch(aircraftState){ case AIRCRAFT_FLYING: #ifdef DEBUG_AIRCRAFT if(selectedUnits.selectedUnits.find(this)!=selectedUnits.selectedUnits.end()){ info->AddLine("Flying %i %i %.1f %i",moveState,fireState,inefficientAttackTime,(int)isFighter); } #endif owner->restTime=0; if(owner->userTarget || owner->userAttackGround){ inefficientAttackTime=min(inefficientAttackTime,(float)gs->frameNum-owner->lastFireWeapon); if(owner->userTarget){ goalPos=owner->userTarget->pos; } else { goalPos=owner->userAttackPos; } if(maneuver){ UpdateManeuver(); inefficientAttackTime=0; } else if(isFighter && goalPos.distance(pos)<owner->maxRange*4){ inefficientAttackTime++; UpdateFighterAttack(); }else{ inefficientAttackTime=0; UpdateAttack(); } }else{ inefficientAttackTime=0; UpdateFlying(wantedHeight,1); } break; case AIRCRAFT_LANDED: inefficientAttackTime=0; UpdateLanded(); break; case AIRCRAFT_LANDING: inefficientAttackTime=0; UpdateLanding(); break; case AIRCRAFT_CRASHING: owner->crashing=true; UpdateAirPhysics(crashRudder,crashAileron,crashElevator,0,owner->frontdir); new CSmokeProjectile(owner->midPos,gs->randVector()*0.08,100+gs->randFloat()*50,5,0.2,owner,0.4); if(!(gs->frameNum&3) && max(0.f,ground->GetApproximateHeight(pos.x,pos.z))+5+owner->radius>pos.y) owner->KillUnit(true,false,0); break; case AIRCRAFT_TAKEOFF: UpdateTakeOff(wantedHeight); default: break; } EndNormalControl: if(pos!=oldpos){ oldpos=pos; if(aircraftState==AIRCRAFT_FLYING || aircraftState==AIRCRAFT_CRASHING){ vector<CUnit*> nearUnits=qf->GetUnitsExact(pos,owner->radius+6); vector<CUnit*>::iterator ui; for(ui=nearUnits.begin();ui!=nearUnits.end();++ui){ float sqDist=(pos-(*ui)->pos).SqLength(); float totRad=owner->radius+(*ui)->radius; if(sqDist<totRad*totRad && sqDist!=0){ float dist=sqrt(sqDist); float3 dif=pos-(*ui)->pos; dif/=dist; if((*ui)->immobile){ pos-=dif*(dist-totRad); owner->midPos=pos+owner->frontdir*owner->relMidPos.z + owner->updir*owner->relMidPos.y + owner->rightdir*owner->relMidPos.x; owner->speed*=0.99f; float damage=(((*ui)->speed-owner->speed)*0.1).SqLength(); owner->DoDamage(DamageArray()*damage,0,ZeroVector); (*ui)->DoDamage(DamageArray()*damage,0,ZeroVector); } else { float part=owner->mass/(owner->mass+(*ui)->mass); pos-=dif*(dist-totRad)*(1-part); owner->midPos=pos+owner->frontdir*owner->relMidPos.z + owner->updir*owner->relMidPos.y + owner->rightdir*owner->relMidPos.x; CUnit* u=(CUnit*)(*ui); u->pos+=dif*(dist-totRad)*(part); u->midPos=u->pos+u->frontdir*u->relMidPos.z + u->updir*u->relMidPos.y + u->rightdir*u->relMidPos.x; float damage=(((*ui)->speed-owner->speed)*0.1).SqLength(); owner->DoDamage(DamageArray()*damage,0,ZeroVector); (*ui)->DoDamage(DamageArray()*damage,0,ZeroVector); owner->speed*=0.99f; } } } } if(pos.x<0){ pos.x+=1.5; owner->midPos.x+=1.5; }else if(pos.x>float3::maxxpos){ pos.x-=1.5; owner->midPos.x-=1.5; } if(pos.z<0){ pos.z+=1.5; owner->midPos.z+=1.5; }else if(pos.z>float3::maxzpos){ pos.z-=1.5; owner->midPos.z-=1.5; } } #ifdef DEBUG_AIRCRAFT if(lastColWarningType==1){ int g=geometricObjects->AddLine(owner->pos,lastColWarning->pos,10,1,1); geometricObjects->SetColor(g,0.2,1,0.2,0.6); } else if(lastColWarningType==2){ int g=geometricObjects->AddLine(owner->pos,lastColWarning->pos,10,1,1); if(owner->frontdir.dot(lastColWarning->midPos+lastColWarning->speed*20 - owner->midPos - owner->speed*20)<0) geometricObjects->SetColor(g,1,0.2,0.2,0.6); else geometricObjects->SetColor(g,1,1,0.2,0.6); } #endif }
void CAirMoveType::UpdateAirPhysics(float rudder, float aileron, float elevator,float engine,const float3& engineVector) { float3 &pos = owner->pos; float3 &rightdir = owner->rightdir; float3 &frontdir = owner->frontdir; float3 &updir = owner->updir; float3 &speed = owner->speed; lastRudderPos=rudder; lastAileronPos=aileron; lastElevatorPos=elevator; float speedf=speed.Length(); float3 speeddir=frontdir; if(speedf!=0) speeddir=speed/speedf; float gHeight=ground->GetHeight(pos.x,pos.z); #ifdef DIRECT_CONTROL_ALLOWED if(owner->directControl) if((pos.y-gHeight)>wantedHeight*1.2) engine=max(0.,min((double)engine,1-(pos.y-gHeight-wantedHeight*1.2)/wantedHeight)); #endif speed+=engineVector*maxAcc*engine; speed.y+=gs->gravity*myGravity; speed*=invDrag; float3 wingDir=updir*(1-wingAngle)-frontdir*wingAngle; float wingForce=wingDir.dot(speed)*wingDrag; speed-=wingDir*wingForce; frontdir+=rightdir*rudder*maxRudder*speedf; updir+=rightdir*aileron*maxAileron*speedf; frontdir+=updir*elevator*maxElevator*speedf; frontdir+=(speeddir-frontdir)*frontToSpeed; speed+=(frontdir*speedf-speed)*speedToFront; pos+=speed; if(gHeight>owner->pos.y-owner->model->radius*0.2 && !owner->crashing){ float3 gNormal=ground->GetNormal(pos.x,pos.z); float impactSpeed=-speed.dot(gNormal); if(impactSpeed>0){ if(owner->stunned){ float damage=0; if(impactSpeed>0.5) damage+=impactSpeed*impactSpeed*1000; if(updir.dot(gNormal)<0.95) damage+=(1-(updir.dot(gNormal)))*1000; if(damage>0) owner->DoDamage(DamageArray()*(damage*0.4),0,ZeroVector); //only do damage while stunned for now } pos.y=gHeight+owner->model->radius*0.2+0.01; speed+=gNormal*(impactSpeed*1.5); speed*=0.95; updir=gNormal-frontdir*0.1; frontdir=updir.cross(frontdir.cross(updir)); } } frontdir.Normalize(); rightdir=frontdir.cross(updir); rightdir.Normalize(); updir=rightdir.cross(frontdir); owner->midPos=pos+frontdir*owner->relMidPos.z + updir*owner->relMidPos.y + rightdir*owner->relMidPos.x; #ifdef DEBUG_AIRCRAFT if(selectedUnits.selectedUnits.find(this)!=selectedUnits.selectedUnits.end()){ info->AddLine("UpdataAP %.1f %.1f %.1f %.1f",speedf,pos.x,pos.y,pos.z); // info->AddLine("Rudders %.1f %.1f %.1f %.1f",rudder,aileron,elevator,engine); } #endif }
void CAirMoveType::UpdateAirPhysics(float rudder, float aileron, float elevator, float engine, const float3& engineVector) { float3& pos = owner->pos; SyncedFloat3& rightdir = owner->rightdir; SyncedFloat3& frontdir = owner->frontdir; SyncedFloat3& updir = owner->updir; float3& speed = owner->speed; bool nextPosInBounds = true; lastRudderPos = rudder; lastAileronPos = aileron; lastElevatorPos = elevator; float speedf = speed.Length(); float3 speeddir = frontdir; if (speedf != 0.0f) speeddir = speed / speedf; float gHeight = ground->GetHeight(pos.x, pos.z); if (owner->directControl) { if ((pos.y - gHeight) > wantedHeight * 1.2f) { engine = std::max(0.0f, std::min(engine, 1 - (pos.y - gHeight - wantedHeight * 1.2f) / wantedHeight)); } // check next position given current (unadjusted) pos and speed nextPosInBounds = (pos + speed).CheckInBounds(); } speed += engineVector * maxAcc * engine; speed.y += mapInfo->map.gravity * myGravity; if (aircraftState == AIRCRAFT_CRASHING) { speed *= crashDrag; } else { speed *= invDrag; } float3 wingDir = updir * (1 - wingAngle) - frontdir * wingAngle; float wingForce = wingDir.dot(speed) * wingDrag; speed -= wingDir * wingForce; frontdir += rightdir * rudder * maxRudder * speedf; updir += rightdir * aileron * maxAileron * speedf; frontdir += updir * elevator * maxElevator * speedf; frontdir += (speeddir - frontdir) * frontToSpeed; speed += (frontdir * speedf - speed) * speedToFront; if (nextPosInBounds) { pos += speed; } // ground collision if (gHeight > owner->pos.y - owner->model->radius * 0.2f && !owner->crashing) { float3 gNormal = ground->GetNormal(pos.x, pos.z); float impactSpeed = -speed.dot(gNormal); if (impactSpeed > 0) { if (owner->stunned) { float damage = 0; if (impactSpeed > 0.5f) damage += impactSpeed * impactSpeed * 1000; if (updir.dot(gNormal) < 0.95f) damage += (1 - (updir.dot(gNormal))) * 1000; if (damage > 0) { // only do damage while stunned for now owner->DoDamage(DamageArray() * (damage * 0.4f), 0, ZeroVector); } } pos.y = gHeight + owner->model->radius * 0.2f + 0.01f; speed += gNormal * (impactSpeed * 1.5f); // fix for mantis #1355 // aircraft could get stuck in the ground and never recover; // this only happened when the speed wasn't high enough. // do not reduce speed if it's too low, add a vertical component // to help get off the ground instead. if (speed.SqLength() > 0.3f*0.3f * owner->unitDef->speed*owner->unitDef->speed) speed *= 0.95f; else speed.y += impactSpeed; updir = gNormal - frontdir * 0.1f; frontdir = updir.cross(frontdir.cross(updir)); } } frontdir.Normalize(); rightdir = frontdir.cross(updir); rightdir.Normalize(); updir = rightdir.cross(frontdir); owner->UpdateMidPos(); #ifdef DEBUG_AIRCRAFT GML_RECMUTEX_LOCK(sel); // UpdateAirPhysics if (selectedUnits.selectedUnits.find(this) != selectedUnits.selectedUnits.end()) { logOutput.Print("UpdataAP %.1f %.1f %.1f %.1f", speedf, pos.x, pos.y, pos.z); // logOutput.Print("Rudders %.1f %.1f %.1f %.1f", rudder, aileron, elevator, engine); } #endif }
void CAirMoveType::Update(void) { float3& pos = owner->pos; // note: this is only set to false after // the plane has finished constructing if (useHeading) { useHeading = false; SetState(AIRCRAFT_TAKEOFF); } if (owner->stunned) { UpdateAirPhysics(0, lastAileronPos, lastElevatorPos, 0, ZeroVector); goto EndNormalControl; } if (owner->directControl && !(aircraftState == AIRCRAFT_CRASHING)) { SetState(AIRCRAFT_FLYING); DirectControlStruct* dc = owner->directControl; inefficientAttackTime = 0; if (dc->forward || dc->back || dc->left || dc->right) { float aileron = 0; float elevator = 0; if (dc->forward) elevator -= 1; if (dc->back) elevator += 1; if (dc->right) aileron += 1; if (dc->left) aileron -= 1; UpdateAirPhysics(0, aileron, elevator, 1, owner->frontdir); maneuver = 0; goto EndNormalControl; // bad } } if (reservedPad) { CUnit* unit = reservedPad->GetUnit(); float3 relPos = unit->script->GetPiecePos(reservedPad->GetPiece()); float3 pos = unit->pos + (unit->frontdir * relPos.z) + (unit->updir * relPos.y) + (unit->rightdir * relPos.x); if (padStatus == 0) { if (aircraftState != AIRCRAFT_FLYING && aircraftState != AIRCRAFT_TAKEOFF) { SetState(AIRCRAFT_FLYING); } goalPos = pos; if (pos.SqDistance2D(owner->pos) < (400*400)) { padStatus = 1; } } else if (padStatus == 1) { if (aircraftState != AIRCRAFT_LANDING) { SetState(AIRCRAFT_LANDING); } goalPos = pos; reservedLandingPos = pos; if (owner->pos.SqDistance(pos) < 9 || aircraftState == AIRCRAFT_LANDED) { padStatus = 2; } } else { if (aircraftState != AIRCRAFT_LANDED) { SetState(AIRCRAFT_LANDED); } owner->pos = pos; owner->AddBuildPower(unit->unitDef->buildSpeed / GAME_SPEED, unit); owner->currentFuel = std::min(owner->unitDef->maxFuel, owner->currentFuel + (owner->unitDef->maxFuel / (GAME_SPEED * owner->unitDef->refuelTime))); if (owner->health >= owner->maxHealth - 1 && owner->currentFuel >= owner->unitDef->maxFuel) { // repaired and filled up, leave the pad airBaseHandler->LeaveLandingPad(reservedPad); reservedPad = 0; padStatus = 0; goalPos = oldGoalPos; SetState(AIRCRAFT_TAKEOFF); } } } else if ((owner->unitDef->maxFuel > 0.0f && owner->currentFuel <= 0.0f) && padStatus == 0 && maxWantedSpeed > 0.0f) { // keep us in the air to reach our landing goalPos // (which is hopefully in the vicinity of a pad) SetState(AIRCRAFT_FLYING); } switch (aircraftState) { case AIRCRAFT_FLYING: { #ifdef DEBUG_AIRCRAFT GML_RECMUTEX_LOCK(sel); // Update if (selectedUnits.selectedUnits.find(this) != selectedUnits.selectedUnits.end()) { logOutput.Print("Flying %i %i %.1f %i", moveState, fireState, inefficientAttackTime, (int) isFighter); } #endif owner->restTime = 0; // somewhat hackish, but planes that have attack orders // while no pad is available would otherwise continue // attacking even if out of fuel bool continueAttack = (!reservedPad && ((owner->currentFuel > 0.0f) || owner->unitDef->maxFuel <= 0.0f)); if (continueAttack && ((owner->userTarget && !owner->userTarget->isDead) || owner->userAttackGround)) { inefficientAttackTime = std::min(inefficientAttackTime, float(gs->frameNum) - owner->lastFireWeapon); if (owner->userTarget) { goalPos = owner->userTarget->pos; } else { goalPos = owner->userAttackPos; } if (maneuver) { UpdateManeuver(); inefficientAttackTime = 0; } else if (isFighter && goalPos.SqDistance(pos) < Square(owner->maxRange * 4)) { inefficientAttackTime++; UpdateFighterAttack(); } else { inefficientAttackTime = 0; UpdateAttack(); } } else { inefficientAttackTime = 0; UpdateFlying(wantedHeight, 1); } } break; case AIRCRAFT_LANDED: inefficientAttackTime = 0; UpdateLanded(); break; case AIRCRAFT_LANDING: inefficientAttackTime = 0; UpdateLanding(); break; case AIRCRAFT_CRASHING: owner->crashing = true; UpdateAirPhysics(crashRudder, crashAileron, crashElevator, 0, owner->frontdir); new CSmokeProjectile(owner->midPos, gs->randVector() * 0.08f, 100 + gs->randFloat() * 50, 5, 0.2f, owner, 0.4f); if (!(gs->frameNum & 3) && std::max(0.f, ground->GetApproximateHeight(pos.x, pos.z)) + 5 + owner->radius > pos.y) owner->KillUnit(true, false, 0); break; case AIRCRAFT_TAKEOFF: UpdateTakeOff(wantedHeight); break; default: break; } EndNormalControl: // handle collisions if (pos != oldpos) { oldpos = pos; bool hitBuilding = false; if (collide && (aircraftState == AIRCRAFT_FLYING || aircraftState == AIRCRAFT_CRASHING)) { vector<CUnit*> nearUnits = qf->GetUnitsExact(pos, owner->radius + 6); vector<CUnit*>::iterator ui; for (ui = nearUnits.begin(); ui != nearUnits.end(); ++ui) { float sqDist = (pos - (*ui)->pos).SqLength(); float totRad = owner->radius + (*ui)->radius; if (sqDist < totRad * totRad && sqDist != 0) { float dist = sqrt(sqDist); float3 dif = pos - (*ui)->pos; if (dist > 0.0f) { dif /= dist; } if ((*ui)->immobile) { pos -= dif * (dist - totRad); owner->UpdateMidPos(); owner->speed *= 0.99f; float damage = (((*ui)->speed - owner->speed) * 0.1f).SqLength(); owner->DoDamage(DamageArray() * damage, 0, ZeroVector); (*ui)->DoDamage(DamageArray() * damage, 0, ZeroVector); hitBuilding = true; } else { float part = owner->mass / (owner->mass + (*ui)->mass); pos -= dif * (dist - totRad) * (1 - part); owner->UpdateMidPos(); CUnit* u = (CUnit*)(*ui); u->pos += dif * (dist - totRad) * (part); u->UpdateMidPos(); float damage = (((*ui)->speed - owner->speed) * 0.1f).SqLength(); owner->DoDamage(DamageArray() * damage, 0, ZeroVector); (*ui)->DoDamage(DamageArray() * damage, 0, ZeroVector); owner->speed *= 0.99f; } } } if (hitBuilding && owner->crashing) { // if our collision sphere overlapped with that // of a building and we're crashing, die right // now rather than waiting until we're close // enough to the ground (which may never happen // if eg. we're going down over a crowded field // of windmills due to col-det) owner->KillUnit(true, false, 0); return; } } if (pos.x < 0) { pos.x += 1.5f; owner->midPos.x += 1.5f; } else if (pos.x > float3::maxxpos) { pos.x -= 1.5f; owner->midPos.x -= 1.5f; } if (pos.z < 0) { pos.z += 1.5f; owner->midPos.z += 1.5f; } else if (pos.z > float3::maxzpos) { pos.z -= 1.5f; owner->midPos.z -= 1.5f; } } #ifdef DEBUG_AIRCRAFT if (lastColWarningType == 1) { int g = geometricObjects->AddLine(owner->pos, lastColWarning->pos, 10, 1, 1); geometricObjects->SetColor(g, 0.2f, 1, 0.2f, 0.6f); } else if (lastColWarningType == 2) { int g = geometricObjects->AddLine(owner->pos, lastColWarning->pos, 10, 1, 1); if (owner->frontdir.dot(lastColWarning->midPos + lastColWarning->speed * 20 - owner->midPos - owner->speed * 20) < 0) geometricObjects->SetColor(g, 1, 0.2f, 0.2f, 0.6f); else geometricObjects->SetColor(g, 1, 1, 0.2f, 0.6f); } #endif }
void CTransportUnit::KillUnit(bool selfDestruct, bool reclaimed, CUnit* attacker, bool) { std::list<TransportedUnit>::iterator ti; for (ti = transported.begin(); ti != transported.end(); ++ti) { CUnit* u = ti->unit; const float gh = ground->GetHeight2(u->pos.x, u->pos.z); u->transporter = 0; u->DeleteDeathDependence(this); // prevent a position teleport on the next movetype update if // the transport died in a place that the unit being carried // could not get to on its own if (!u->pos.IsInBounds()) { u->KillUnit(false, false, NULL, false); continue; } else { // immobile units can still be transported // via script trickery, guard against this if (u->unitDef->movedata != NULL && gh < -u->unitDef->movedata->depth) { // always treat depth as maxWaterDepth (fails if // the transportee is a ship, but so does using // UnitDef::{min, max}WaterDepth) u->KillUnit(false, false, NULL, false); continue; } } if (!unitDef->releaseHeld) { if (!selfDestruct) { // we don't want it to leave a corpse u->DoDamage(DamageArray() * 1000000, 0, ZeroVector); } u->KillUnit(selfDestruct, reclaimed, attacker); } else { // place unit near the place of death of the transport // if it's a ground transport and uses a piece-in-ground method // to hide units if (u->pos.y < gh) { const float k = (u->radius + radius)*std::max(unitDef->unloadSpread, 1.f); // try to unload in a presently unoccupied spot // unload on a wreck if suitable position not found for (int i = 0; i<10; ++i) { float3 pos = u->pos; pos.x += gs->randFloat()*2*k - k; pos.z += gs->randFloat()*2*k - k; pos.y = ground->GetHeight2(u->pos.x, u->pos.z); if (qf->GetUnitsExact(pos, u->radius + 2).empty()) { u->pos = pos; break; } } u->UpdateMidPos(); } else if (CGroundMoveType* mt = dynamic_cast<CGroundMoveType*>(u->moveType)) { mt->StartFlying(); } u->stunned = (u->paralyzeDamage > u->health); u->moveType->LeaveTransport(); u->speed = speed*(0.5f + 0.5f*gs->randFloat()); eventHandler.UnitUnloaded(u, this); } } CUnit::KillUnit(selfDestruct, reclaimed, attacker); }
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 CTransportUnit::KillUnit(bool selfDestruct, bool reclaimed, CUnit* attacker, bool) { if (!isDead) { // guard against recursive invocation via // transportee->KillUnit // helper->Explosion // helper->DoExplosionDamage // unit->DoDamage // unit->KillUnit // in the case that unit == this isDead = true; // ::KillUnit might be called multiple times while !deathScriptFinished, // but it makes no sense to kill/detach our transportees more than once std::list<TransportedUnit>::iterator ti; for (ti = transportedUnits.begin(); ti != transportedUnits.end(); ++ti) { CUnit* transportee = ti->unit; assert(transportee != this); if (transportee->isDead) continue; const float gh = ground->GetHeightReal(transportee->pos.x, transportee->pos.z); transportee->transporter = NULL; transportee->DeleteDeathDependence(this, DEPENDENCE_TRANSPORTER); // prevent a position teleport on the next movetype update if // the transport died in a place that the unit being carried // could not get to on its own if (!transportee->pos.IsInBounds()) { transportee->KillUnit(false, false, NULL, false); continue; } else { // immobile units can still be transported // via script trickery, guard against this if (!transportee->unitDef->IsAllowedTerrainHeight(gh)) { transportee->KillUnit(false, false, NULL, false); continue; } } if (!unitDef->releaseHeld) { if (!selfDestruct) { // we don't want it to leave a corpse transportee->DoDamage(DamageArray(1e6f), ZeroVector, NULL, -DAMAGE_EXTSOURCE_KILLED); } transportee->KillUnit(selfDestruct, reclaimed, attacker); } else { // place unit near the place of death of the transport // if it's a ground transport and uses a piece-in-ground method // to hide units if (transportee->pos.y < gh) { const float k = (transportee->radius + radius) * std::max(unitDef->unloadSpread, 1.0f); // try to unload in a presently unoccupied spot // unload on a wreck if suitable position not found for (int i = 0; i < 10; ++i) { float3 pos = transportee->pos; pos.x += (gs->randFloat() * 2 * k - k); pos.z += (gs->randFloat() * 2 * k - k); pos.y = ground->GetHeightReal(transportee->pos.x, transportee->pos.z); if (qf->GetUnitsExact(pos, transportee->radius + 2).empty()) { transportee->Move3D(pos, false); break; } } } else if (CGroundMoveType* mt = dynamic_cast<CGroundMoveType*>(transportee->moveType)) { mt->StartFlying(); } transportee->moveType->SlowUpdate(); transportee->moveType->LeaveTransport(); // issue a move order so that unit won't try to return to pick-up pos in IdleCheck() if (unitDef->canfly && transportee->unitDef->canmove) { Command c(CMD_MOVE); c.params.push_back(transportee->pos.x); c.params.push_back(ground->GetHeightAboveWater(transportee->pos.x, transportee->pos.z)); c.params.push_back(transportee->pos.z); transportee->commandAI->GiveCommand(c); } transportee->stunned = (transportee->paralyzeDamage > (modInfo.paralyzeOnMaxHealth? transportee->maxHealth: transportee->health)); transportee->speed = speed * (0.5f + 0.5f * gs->randFloat()); if (CBuilding* building = dynamic_cast<CBuilding*>(transportee)) { // this building may end up in a strange position, so kill it building->KillUnit(selfDestruct, reclaimed, attacker); } eventHandler.UnitUnloaded(transportee, this); } } transportedUnits.clear(); // make sure CUnit::KillUnit does not return early isDead = false; } CUnit::KillUnit(selfDestruct, reclaimed, attacker); }