bool CWeapon::TryTargetRotate(CUnit* unit, bool userTarget){ float3 tempTargetPos(helper->GetUnitErrorPos(unit,owner->allyteam)); tempTargetPos+=errorVector*(weaponDef->targetMoveError*30*unit->speed.Length()*(1.0f-owner->limExperience)); float appHeight=ground->GetApproximateHeight(tempTargetPos.x,tempTargetPos.z)+2; if(tempTargetPos.y < appHeight){ tempTargetPos.y=appHeight; } short weaponHeadding = GetHeadingFromVector(mainDir.x, mainDir.z); short enemyHeadding = GetHeadingFromVector( tempTargetPos.x - weaponPos.x, tempTargetPos.z - weaponPos.z); return TryTargetHeading(enemyHeadding - weaponHeadding, tempTargetPos,userTarget, unit); }
void CFarTextureHandler::DrawFarTexture(const CSolidObject* obj, CVertexArray* va) { const int farTextureNum = cache[obj->team][obj->model->id]; // not found in the atlas if (farTextureNum <= 0) return; const float3 interPos = obj->drawPos + UpVector * obj->model->height * 0.5f; // indicates the orientation to draw static const int USHRT_MAX_ = (1 << 16); const int orient_step = USHRT_MAX_ / numOrientations; int orient = GetHeadingFromVector(-camera->forward.x, -camera->forward.z) - obj->heading; orient += USHRT_MAX_; // make it positive only orient += (orient_step >> 1); // we want that frontdir is from -orient_step/2 upto orient_step/2 orient %= USHRT_MAX_; // we have an angle so it's periodical orient /= orient_step; // get the final direction index const float iconSizeX = float(this->iconSizeX) / texSizeX; const float iconSizeY = float(this->iconSizeY) / texSizeY; const float2 texcoords = GetTextureCoords(farTextureNum - 1, orient); const float3 curad = camera->up * obj->model->radius; const float3 crrad = camera->right * obj->model->radius; va->AddVertexQT(interPos - curad + crrad, texcoords.x, texcoords.y ); va->AddVertexQT(interPos + curad + crrad, texcoords.x, texcoords.y + iconSizeY); va->AddVertexQT(interPos + curad - crrad, texcoords.x + iconSizeX, texcoords.y + iconSizeY); va->AddVertexQT(interPos - curad - crrad, texcoords.x + iconSizeX, texcoords.y ); }
void HUDDrawer::DrawCameraDirectionArrow(const CUnit* unit) { glDisable(GL_TEXTURE_2D); if (unit->moveType->useHeading) { glPushMatrix(); glTranslatef(-0.8f, -0.4f, 0.0f); glScalef(0.33f, 0.33f * globalRendering->aspectRatio, 0.33f); glRotatef( GetHeadingFromVector(camera->forward.x, camera->forward.z) * 180.0f / 32768 + 180, 0.0f, 0.0f, 1.0f ); glScalef(0.4f, 0.4f, 0.3f); glColor4f(0.4f, 0.4f, 1.0f, 0.6f); glBegin(GL_TRIANGLE_FAN); glVertex2f(-0.2f, -0.3f); glVertex2f(-0.2f, 0.3f); glVertex2f( 0.0f, 0.5f); glVertex2f( 0.2f, 0.3f); glVertex2f( 0.2f, -0.3f); glVertex2f(-0.2f, -0.3f); glEnd(); glPopMatrix(); } }
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); } }
bool CWeapon::TryTargetRotate(float3 pos, bool userTarget) { if (!userTarget && weaponDef->noAutoTarget) { return false; } if (weaponDef->interceptor || !weaponDef->canAttackGround || (weaponDef->onlyTargetCategory != 0xffffffff)) { return false; } if (!weaponDef->waterweapon && pos.y < 1) { pos.y = 1; } short weaponHeading = GetHeadingFromVector(mainDir.x, mainDir.z); short enemyHeading = GetHeadingFromVector( pos.x - weaponPos.x, pos.z - weaponPos.z); return TryTargetHeading(enemyHeading - weaponHeading, pos, userTarget, 0); }
void CUnit::PushWind(float x, float z, float strength) { if(strength > unitDef->windGenerator) { cob->Call(COBFN_SetSpeed, (int)(unitDef->windGenerator*3000.0f)); } else { cob->Call(COBFN_SetSpeed, (int)(strength*3000.0f)); } cob->Call(COBFN_SetDirection, (int)GetHeadingFromVector(-x, -z)); }
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 CAirMoveType::SetState(AAirMoveType::AircraftState state) { // this state is only used by CTAAirMoveType assert(state != AIRCRAFT_HOVERING); if (aircraftState == AIRCRAFT_CRASHING || state == aircraftState) return; /* if (state == AIRCRAFT_LANDING) owner->cob->Call(COBFN_Deactivate); else if (state == aircraft_flying) // cob->Call(COBFN_Activate); */ if (state == AIRCRAFT_FLYING) { owner->Activate(); owner->script->StartMoving(); } if (state == AIRCRAFT_LANDED) { owner->heading = GetHeadingFromVector(owner->frontdir.x, owner->frontdir.z); owner->physicalState = CSolidObject::OnGround; owner->useAirLos = false; } else { owner->physicalState = CSolidObject::Flying; owner->useAirLos = true; if (state != AIRCRAFT_LANDING) { reservedLandingPos.x = -1; owner->UnBlock(); } } if (aircraftState == AIRCRAFT_LANDED && reservedLandingPos.x > 0) { reservedLandingPos.x = -1; } subState = 0; // make sure we only go into takeoff if actually landed if (state != AIRCRAFT_TAKEOFF || aircraftState == AIRCRAFT_LANDED) { aircraftState = state; } else { aircraftState = AIRCRAFT_TAKEOFF; } }
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 CAirMoveType::SetState(CAirMoveType::AircraftState state) { if(aircraftState==AIRCRAFT_CRASHING || state==aircraftState) return; /* if (state == AIRCRAFT_LANDING) owner->animator->AnimAction(ANIMFN_Deactivate); else if (state == aircraft_flying) //animator->AnimAction(ANIMFN_Activate); */ if (state == AIRCRAFT_FLYING) { owner->Activate(); owner->animator->AnimAction(ANIMFN_StartMoving); } if(state==AIRCRAFT_LANDED){ owner->heading=GetHeadingFromVector(owner->frontdir.x, owner->frontdir.z); owner->physicalState = CSolidObject::OnGround; owner->useAirLos=false; }else{ owner->physicalState = CSolidObject::Flying; owner->useAirLos=true; if(state!=AIRCRAFT_LANDING){ reservedLandingPos.x=-1; owner->UnBlock(); } } if(aircraftState==AIRCRAFT_LANDED && reservedLandingPos.x>0){ reservedLandingPos.x=-1; } subState=0; if(state!=AIRCRAFT_TAKEOFF || aircraftState==AIRCRAFT_LANDED) //make sure we only go into takeoff if actually landed aircraftState=state; else aircraftState=AIRCRAFT_TAKEOFF; }
void CFactory::UpdateBuild(CUnit* buildee) { if (stunned) return; // factory not under construction and // nanolathing unit: continue building lastBuildUpdateFrame = gs->frameNum; // buildPiece is the rotating platform const int buildPiece = GetBuildPiece(); const float3& buildPos = CalcBuildPos(buildPiece); const CMatrix44f& mat = script->GetPieceMatrix(buildPiece); const int h = GetHeadingFromVector(mat[2], mat[10]); //! x.z, z.z float3 buildeePos = buildPos; // rotate unit nanoframe with platform buildee->heading = (-h + GetHeadingFromFacing(buildFacing)) & (SPRING_CIRCLE_DIVS - 1); if (buildee->unitDef->floatOnWater && (buildeePos.y <= 0.0f)) buildeePos.y = -buildee->unitDef->waterline; buildee->Move3D(buildeePos, false); buildee->UpdateDirVectors(false); buildee->UpdateMidAndAimPos(); const CCommandQueue& queue = commandAI->commandQue; if (!queue.empty() && (queue.front().GetID() == CMD_WAIT)) { buildee->AddBuildPower(0, this); } else { if (buildee->AddBuildPower(buildSpeed, this)) { CreateNanoParticle(); } } }
bool CHoverAirMoveType::Update() { const float3 lastPos = owner->pos; const float4 lastSpd = owner->speed; AAirMoveType::Update(); if ((owner->IsStunned() && !owner->IsCrashing()) || owner->beingBuilt) { wantedSpeed = ZeroVector; UpdateAirPhysics(); return (HandleCollisions(collide && !owner->beingBuilt && (padStatus == PAD_STATUS_FLYING) && (aircraftState != AIRCRAFT_TAKEOFF))); } // allow us to stop if wanted (changes aircraft state) if (wantToStop) ExecuteStop(); if (aircraftState != AIRCRAFT_CRASHING) { if (owner->UnderFirstPersonControl()) { SetState(AIRCRAFT_FLYING); const FPSUnitController& con = owner->fpsControlPlayer->fpsController; const float3 forward = con.viewDir; const float3 right = forward.cross(UpVector); const float3 nextPos = lastPos + owner->speed; float3 flatForward = forward; flatForward.Normalize2D(); wantedSpeed = ZeroVector; if (con.forward) wantedSpeed += flatForward; if (con.back ) wantedSpeed -= flatForward; if (con.right ) wantedSpeed += right; if (con.left ) wantedSpeed -= right; wantedSpeed.Normalize(); wantedSpeed *= maxSpeed; if (!nextPos.IsInBounds()) { owner->SetVelocityAndSpeed(ZeroVector); } UpdateAirPhysics(); wantedHeading = GetHeadingFromVector(flatForward.x, flatForward.z); } if (reservedPad != NULL) { MoveToRepairPad(); if (padStatus >= PAD_STATUS_LANDING) { flyState = FLY_LANDING; } } } switch (aircraftState) { case AIRCRAFT_LANDED: UpdateLanded(); break; case AIRCRAFT_TAKEOFF: UpdateTakeoff(); break; case AIRCRAFT_FLYING: UpdateFlying(); break; case AIRCRAFT_LANDING: UpdateLanding(); break; case AIRCRAFT_HOVERING: UpdateHovering(); break; case AIRCRAFT_CRASHING: { UpdateAirPhysics(); if ((CGround::GetHeightAboveWater(owner->pos.x, owner->pos.z) + 5.0f + owner->radius) > owner->pos.y) { owner->ClearPhysicalStateBit(CSolidObject::PSTATE_BIT_CRASHING); owner->KillUnit(NULL, true, false); } else { #define SPIN_DIR(o) ((o->id & 1) * 2 - 1) wantedHeading = GetHeadingFromVector(owner->rightdir.x * SPIN_DIR(owner), owner->rightdir.z * SPIN_DIR(owner)); wantedHeight = 0.0f; #undef SPIN_DIR } new CSmokeProjectile(owner, owner->midPos, gs->randVector() * 0.08f, 100 + gs->randFloat() * 50, 5, 0.2f, 0.4f); } break; } if (lastSpd == ZeroVector && owner->speed != ZeroVector) { owner->script->StartMoving(false); } if (lastSpd != ZeroVector && owner->speed == ZeroVector) { owner->script->StopMoving(); } // Banking requires deltaSpeed.y = 0 deltaSpeed = owner->speed - lastSpd; deltaSpeed.y = 0.0f; // Turn and bank and move; update dirs UpdateHeading(); UpdateBanking(aircraftState == AIRCRAFT_HOVERING); return (HandleCollisions(collide && !owner->beingBuilt && (padStatus == PAD_STATUS_FLYING) && (aircraftState != AIRCRAFT_TAKEOFF))); }
void CHoverAirMoveType::UpdateFlying() { const float3& pos = owner->pos; // const float4& spd = owner->speed; // Direction to where we would like to be float3 goalVec = goalPos - pos; owner->restTime = 0; // don't change direction for waypoints we just flew over and missed slightly if (flyState != FLY_LANDING && owner->commandAI->HasMoreMoveCommands()) { float3 goalDir = goalVec; if ((goalDir != ZeroVector) && (goalVec.dot(goalDir.UnsafeANormalize()) < 1.0f)) { goalVec = owner->frontdir; } } const float goalDistSq2D = goalVec.SqLength2D(); const float gHeight = UseSmoothMesh()? std::max(smoothGround->GetHeight(pos.x, pos.z), CGround::GetApproximateHeight(pos.x, pos.z)): CGround::GetHeightAboveWater(pos.x, pos.z); const bool closeToGoal = (flyState == FLY_ATTACKING)? (goalDistSq2D < ( 400.0f)): (goalDistSq2D < (maxDrift * maxDrift)) && (math::fabs(gHeight + wantedHeight - pos.y) < maxDrift); if (closeToGoal) { switch (flyState) { case FLY_CRUISING: { // NOTE: should CMD_LOAD_ONTO be here? const bool isTransporter = (dynamic_cast<CTransportUnit*>(owner) != NULL); const bool hasLoadCmds = isTransporter && !owner->commandAI->commandQue.empty() && (owner->commandAI->commandQue.front().GetID() == CMD_LOAD_ONTO || owner->commandAI->commandQue.front().GetID() == CMD_LOAD_UNITS); // [?] transport aircraft need some time to detect that they can pickup const bool canLoad = isTransporter && (++waitCounter < ((GAME_SPEED << 1) - 5)); const bool isBusy = IsUnitBusy(owner); if (!CanLand(isBusy) || (canLoad && hasLoadCmds)) { wantedSpeed = ZeroVector; if (isTransporter) { if (waitCounter > (GAME_SPEED << 1)) { wantedHeight = orgWantedHeight; } SetState(AIRCRAFT_HOVERING); return; } else { if (!isBusy) { wantToStop = true; // NOTE: // this is not useful, next frame UpdateFlying() // will change it to _LANDING because wantToStop // is now true SetState(AIRCRAFT_HOVERING); return; } } } else { wantedHeight = orgWantedHeight; SetState(AIRCRAFT_LANDING); return; } } break; case FLY_CIRCLING: { if ((++waitCounter) > ((GAME_SPEED * 3) + 10)) { if (airStrafe) { float3 relPos = pos - circlingPos; if (relPos.x < 0.0001f && relPos.x > -0.0001f) { relPos.x = 0.0001f; } static CMatrix44f rot(0.0f, fastmath::PI / 4.0f, 0.0f); // make sure the point is on the circle, go there in a straight line goalPos = circlingPos + (rot.Mul(relPos.Normalize2D()) * goalDistance); } waitCounter = 0; } } break; case FLY_ATTACKING: { if (airStrafe) { float3 relPos = pos - circlingPos; if (relPos.x < 0.0001f && relPos.x > -0.0001f) { relPos.x = 0.0001f; } CMatrix44f rot; if (gs->randFloat() > 0.5f) { rot.RotateY(0.6f + gs->randFloat() * 0.6f); } else { rot.RotateY(-(0.6f + gs->randFloat() * 0.6f)); } // Go there in a straight line goalPos = circlingPos + (rot.Mul(relPos.Normalize2D()) * goalDistance); } } break; case FLY_LANDING: { } break; } } // not "close" to goal yet, so keep going // use 2D math since goal is on the ground // but we are not goalVec.y = 0.0f; // if we are close to our goal, we should // adjust speed st. we never overshoot it // (by respecting current brake-distance) const float curSpeed = owner->speed.Length2D(); const float brakeDist = (0.5f * curSpeed * curSpeed) / decRate; const float goalDist = goalVec.Length() + 0.1f; const float goalSpeed = (maxSpeed ) * (goalDist > brakeDist) + (curSpeed - decRate) * (goalDist <= brakeDist); if (goalDist > goalSpeed) { // update our velocity and heading so long as goal is still // further away than the distance we can cover in one frame // we must use a variable-size "dead zone" to avoid freezing // in mid-air or oscillation behavior at very close distances // NOTE: // wantedSpeed is a vector, so even aircraft with turnRate=0 // are coincidentally able to reach any goal by side-strafing wantedHeading = GetHeadingFromVector(goalVec.x, goalVec.z); wantedSpeed = (goalVec / goalDist) * goalSpeed; } else { // switch to hovering (if !CanLand())) if (flyState != FLY_ATTACKING) { ExecuteStop(); } } // redundant, done in Update() // UpdateHeading(); UpdateAirPhysics(); // Point toward goal or forward - unless we just passed it to get to another goal if ((flyState == FLY_ATTACKING) || (flyState == FLY_CIRCLING)) { goalVec = circlingPos - pos; } else { const bool b0 = (flyState != FLY_LANDING && (owner->commandAI->HasMoreMoveCommands())); const bool b1 = (goalDist < brakeDist && goalDist > 1.0f); if (b0 && b1) { goalVec = owner->frontdir; } else { goalVec = goalPos - pos; } } if (goalVec.SqLength2D() > 1.0f) { // update heading again in case goalVec changed wantedHeading = GetHeadingFromVector(goalVec.x, goalVec.z); } }
void CTAAirMoveType::UpdateFlying() { float3 &pos = owner->pos; float3 &speed = owner->speed; //Direction to where we would like to be float3 dir = goalPos - pos; bool arrived = false; owner->restTime=0; //are we there yet? // logOutput.Print("max drift %f %i %f %f",maxDrift,waitCounter,dir.Length2D(),fabs(ground->GetHeight(pos.x,pos.z)-pos.y+wantedHeight)); bool closeToGoal=dir.SqLength2D() < maxDrift*maxDrift && fabs(ground->GetHeight(pos.x,pos.z)-pos.y+wantedHeight)<maxDrift; if(flyState==FLY_ATTACKING) closeToGoal=dir.SqLength2D() < 400; if (closeToGoal) { //pretty close switch (flyState) { case FLY_CRUISING: if(dontLand || (++waitCounter<55 && dynamic_cast<CTransportUnit*>(owner))){ //transport aircrafts need some time to detect that they can pickup if (dynamic_cast<CTransportUnit*>(owner)) { wantedSpeed=ZeroVector; if(waitCounter>60){ wantedHeight=orgWantedHeight; } } else SetState(AIRCRAFT_HOVERING); } else { wantedHeight=orgWantedHeight; SetState(AIRCRAFT_LANDING); } //logOutput.Print("In position, landing"); return; case FLY_CIRCLING: waitCounter++; if (waitCounter > 100) { //logOutput.Print("moving circlepos"); float3 relPos = pos - circlingPos; if(relPos.x<0.0001f && relPos.x>-0.0001f) relPos.x=0.0001f; relPos.y = 0; relPos.Normalize(); CMatrix44f rot; rot.RotateY(1.0f); float3 newPos = rot.Mul(relPos); //Make sure the point is on the circle newPos = newPos.Normalize() * goalDistance; //Go there in a straight line goalPos = circlingPos + newPos; waitCounter = 0; } break; case FLY_ATTACKING:{ //logOutput.Print("wait is %d", waitCounter); float3 relPos = pos - circlingPos; if(relPos.x<0.0001f && relPos.x>-0.0001f) relPos.x=0.0001f; relPos.y = 0; relPos.Normalize(); CMatrix44f rot; if (gs->randFloat()>0.5f) rot.RotateY(0.6f+gs->randFloat()*0.6f); else rot.RotateY(-(0.6f+gs->randFloat()*0.6f)); float3 newPos = rot.Mul(relPos); newPos = newPos.Normalize() * goalDistance; //Go there in a straight line goalPos = circlingPos + newPos; // logOutput.Print("Changed circle pos"); break;} case FLY_LANDING:{ /* //First check if we can land around here somewhere if (!CanLandAt(pos)) { //Check the surrounding area for a suitable position float3 newPos; bool found = FindLandingSpot(pos, newPos); if (found) { SetState(AIRCRAFT_FLYING); logOutput.Print("Found a landingspot when cruising around"); goalPos = newPos; return; } } else { SetState(AIRCRAFT_LANDING); return; } float3 relPos = pos - circlingPos; relPos.y = 0; CMatrix44f rot; rot.RotateY(2.0f); float3 newPos = rot.Mul(relPos); //Make sure the point is on the circle newPos = newPos.Normalize() * goalDistance; //Go there in a straight line goalPos = circlingPos + newPos; waitCounter = 0; */ break;} } } //no, so go there! dir.y = 0; float realMax = maxSpeed; float dist=dir.Length2D(); //If we are close to our goal, we should go slow enough to be able to break in time //if in attack mode dont slow down if (flyState!=FLY_ATTACKING && dist < breakDistance) { realMax = dist/(speed.Length2D()+0.01f) * decRate; //logOutput.Print("Break! %f %f %f", maxSpeed, dir.Length2D(), realMax); } wantedSpeed = dir.Normalize() * realMax; UpdateAirPhysics(); //Point toward goal or forward if ((flyState == FLY_ATTACKING) || (flyState == FLY_CIRCLING)) { dir = circlingPos - pos; } else { dir = goalPos - pos; } if(dir.SqLength2D()>1) wantedHeading = GetHeadingFromVector(dir.x, dir.z); }
void CTAAirMoveType::Update() { //Handy stuff. Wonder if there is a better way? float3 &pos=owner->pos; SyncedFloat3 &rightdir = owner->rightdir; SyncedFloat3 &frontdir = owner->frontdir; SyncedFloat3 &updir = owner->updir; float3 &speed = owner->speed; //This is only set to false after the plane has finished constructing if (useHeading){ useHeading = false; SetState(AIRCRAFT_TAKEOFF); } //Allow us to stop if wanted if (wantToStop) ExecuteStop(); float3 lastSpeed = speed; if(owner->stunned){ wantedSpeed=ZeroVector; UpdateAirPhysics(); } else { #ifdef DIRECT_CONTROL_ALLOWED if(owner->directControl){ DirectControlStruct* dc=owner->directControl; SetState(AIRCRAFT_FLYING); float3 forward=dc->viewDir; float3 flatForward=forward; flatForward.y=0; flatForward.Normalize(); float3 right=forward.cross(UpVector); wantedSpeed=ZeroVector; if(dc->forward) wantedSpeed+=flatForward; if(dc->back) wantedSpeed-=flatForward; if(dc->right) wantedSpeed+=right; if(dc->left) wantedSpeed-=right; wantedSpeed.Normalize(); wantedSpeed*=maxSpeed; UpdateAirPhysics(); wantedHeading=GetHeadingFromVector(flatForward.x,flatForward.z); } else #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_FLYING) SetState(AIRCRAFT_FLYING); flyState=FLY_LANDING; goalPos=pos; reservedLandingPos=pos; wantedHeight=pos.y-ground->GetHeight(pos.x,pos.z); 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(unit->unitDef->buildSpeed/30,unit); owner->currentFuel = 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){ airBaseHandler->LeaveLandingPad(reservedPad); reservedPad=0; padStatus=0; goalPos=oldGoalPos; SetState(AIRCRAFT_TAKEOFF); } } } //Main state handling switch (aircraftState) { case AIRCRAFT_LANDED: UpdateLanded(); break; case AIRCRAFT_TAKEOFF: UpdateTakeoff(); break; case AIRCRAFT_FLYING: UpdateFlying(); break; case AIRCRAFT_LANDING: UpdateLanding(); break; case AIRCRAFT_HOVERING: UpdateHovering(); break; } } } deltaSpeed = speed - lastSpeed; deltaSpeed.y = 0; //Banking requires this //Turn and bank and move UpdateHeading(); UpdateBanking(aircraftState == AIRCRAFT_HOVERING); //updates dirs owner->midPos=pos+frontdir*owner->relMidPos.z + updir*owner->relMidPos.y + rightdir*owner->relMidPos.x; //Push other units out of the way if(pos!=oldpos && aircraftState!=AIRCRAFT_TAKEOFF && padStatus==0){ oldpos=pos; if(!dontCheckCol){ vector<CUnit*> nearUnits=qf->GetUnitsExact(pos,owner->radius+6); vector<CUnit*>::iterator ui; for(ui=nearUnits.begin();ui!=nearUnits.end();++ui){ if((*ui)->transporter) continue; 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)->mass>=100000 || (*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).SqLength(); //dont think they should take damage when they dont try to avoid it // 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 colSpeed=-owner->speed.dot(dif)+u->speed.dot(dif); owner->speed+=dif*colSpeed*(1-part); u->speed-=dif*colSpeed*(part); // 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(pos.x<0){ pos.x+=0.6f; owner->midPos.x+=0.6f; } else if(pos.x>float3::maxxpos){ pos.x-=0.6f; owner->midPos.x-=0.6f; } if(pos.z<0){ pos.z+=0.6f; owner->midPos.z+=0.6f; }else if(pos.z>float3::maxzpos){ pos.z-=0.6f; owner->midPos.z-=0.6f; } } }
bool CHoverAirMoveType::Update() { float3& pos = owner->pos; float3& speed = owner->speed; AAirMoveType::Update(); if (owner->stunned || owner->beingBuilt) { wantedSpeed = ZeroVector; wantToStop = true; } // Allow us to stop if wanted if (wantToStop) { ExecuteStop(); } const float3 lastSpeed = speed; if (owner->fpsControlPlayer != NULL) { SetState(AIRCRAFT_FLYING); const FPSUnitController& con = owner->fpsControlPlayer->fpsController; const float3 forward = con.viewDir; const float3 right = forward.cross(UpVector); const float3 nextPos = pos + speed; float3 flatForward = forward; flatForward.y = 0.0f; flatForward.Normalize(); wantedSpeed = ZeroVector; if (con.forward) wantedSpeed += flatForward; if (con.back ) wantedSpeed -= flatForward; if (con.right ) wantedSpeed += right; if (con.left ) wantedSpeed -= right; wantedSpeed.Normalize(); wantedSpeed *= maxSpeed; if (!nextPos.IsInBounds()) { speed = ZeroVector; } UpdateAirPhysics(); wantedHeading = GetHeadingFromVector(flatForward.x, flatForward.z); } else { if (reservedPad != NULL) { MoveToRepairPad(); if (padStatus >= 1) { flyState = FLY_LANDING; } } // Main state handling switch (aircraftState) { case AIRCRAFT_LANDED: UpdateLanded(); break; case AIRCRAFT_TAKEOFF: UpdateTakeoff(); break; case AIRCRAFT_FLYING: UpdateFlying(); break; case AIRCRAFT_LANDING: UpdateLanding(); break; case AIRCRAFT_HOVERING: UpdateHovering(); break; case AIRCRAFT_CRASHING: break; } } // Banking requires deltaSpeed.y = 0 deltaSpeed = speed - lastSpeed; deltaSpeed.y = 0.0f; // Turn and bank and move; update dirs UpdateHeading(); UpdateBanking(aircraftState == AIRCRAFT_HOVERING); return (HandleCollisions()); }
void CHoverAirMoveType::UpdateFlying() { const float3& pos = owner->pos; const float3& speed = owner->speed; // Direction to where we would like to be float3 goalVec = goalPos - pos; owner->restTime = 0; // don't change direction for waypoints we just flew over and missed slightly if (flyState != FLY_LANDING && owner->commandAI->HasMoreMoveCommands()) { float3 goalDir = goalVec; if ((goalDir != ZeroVector) && (goalDir.UnsafeANormalize()).SqDistance(goalVec) < 1.0f) { goalVec = owner->frontdir; } } const float goalDistSq2D = goalVec.SqLength2D(); const float gHeight = UseSmoothMesh()? std::max(smoothGround->GetHeight(pos.x, pos.z), ground->GetApproximateHeight(pos.x, pos.z)): ground->GetHeightAboveWater(pos.x, pos.z); const bool closeToGoal = (flyState == FLY_ATTACKING)? (goalDistSq2D < ( 400.0f)): (goalDistSq2D < (maxDrift * maxDrift)) && (math::fabs(gHeight - pos.y + wantedHeight) < maxDrift); if (closeToGoal) { switch (flyState) { case FLY_CRUISING: { const bool hasMoreMoveCmds = owner->commandAI->HasMoreMoveCommands(); const bool noland = dontLand || !autoLand || hasMoreMoveCmds; // NOTE: should CMD_LOAD_ONTO be here? const bool trans = (dynamic_cast<CTransportUnit*>(owner) != NULL); const bool hasLoadCmds = trans && !owner->commandAI->commandQue.empty() && (owner->commandAI->commandQue.front().GetID() == CMD_LOAD_ONTO || owner->commandAI->commandQue.front().GetID() == CMD_LOAD_UNITS); // [?] transport aircraft need some time to detect that they can pickup const bool canLoad = trans && (++waitCounter < ((GAME_SPEED << 1) - 5)); if (noland || (canLoad && hasLoadCmds)) { if (trans) { wantedSpeed = ZeroVector; SetState(AIRCRAFT_HOVERING); if (waitCounter > (GAME_SPEED << 1)) { wantedHeight = orgWantedHeight; } } else { wantedSpeed = ZeroVector; if (!hasMoreMoveCmds) { wantToStop = true; SetState(AIRCRAFT_HOVERING); } } } else { wantedHeight = orgWantedHeight; SetState(AIRCRAFT_LANDING); } return; } case FLY_CIRCLING: // break; if ((++waitCounter) > ((GAME_SPEED * 3) + 10)) { if (airStrafe) { float3 relPos = pos - circlingPos; if (relPos.x < 0.0001f && relPos.x > -0.0001f) { relPos.x = 0.0001f; } relPos.y = 0.0f; relPos.Normalize(); static CMatrix44f rot(0.0f,fastmath::PI/4.0f,0.0f); float3 newPos = rot.Mul(relPos); // Make sure the point is on the circle newPos = newPos * goalDistance; // Go there in a straight line goalPos = circlingPos + newPos; } waitCounter = 0; } break; case FLY_ATTACKING: { if (airStrafe) { float3 relPos = pos - circlingPos; if (relPos.x < 0.0001f && relPos.x > -0.0001f) { relPos.x = 0.0001f; } relPos.y = 0; relPos.Normalize(); CMatrix44f rot; if (gs->randFloat() > 0.5f) { rot.RotateY(0.6f + gs->randFloat() * 0.6f); } else { rot.RotateY(-(0.6f + gs->randFloat() * 0.6f)); } float3 newPos = rot.Mul(relPos); newPos = newPos * goalDistance; // Go there in a straight line goalPos = circlingPos + newPos; } break; } case FLY_LANDING: { break; } } } // not there yet, so keep going goalVec.y = 0.0f; // if we are close to our goal and not in attack mode, // we should go slow enough to be able to break in time const float goalDist = goalVec.Length() + 0.1f; const float approachSpeed = (flyState != FLY_ATTACKING && goalDist < brakeDistance)? (goalDist / (speed.Length2D() + 0.01f) * decRate): maxSpeed; wantedSpeed = (goalVec / goalDist) * approachSpeed; UpdateAirPhysics(); // Point toward goal or forward - unless we just passed it to get to another goal if ((flyState == FLY_ATTACKING) || (flyState == FLY_CIRCLING)) { goalVec = circlingPos - pos; } else { const bool b0 = (flyState != FLY_LANDING && (owner->commandAI->HasMoreMoveCommands())); const bool b1 = (goalDist < 120.0f) && ((goalVec.SafeNormalize()).SqDistance(goalVec) > 1.0f); if (b0 && b1) { goalVec = owner->frontdir; } else { goalVec = goalPos - pos; } } if (goalVec.SqLength2D() > 1.0f) { wantedHeading = GetHeadingFromVector(goalVec.x, goalVec.z); } }
void CFactory::Update() { if(beingBuilt){ CUnit::Update(); return; } if(quedBuild && inBuildStance){ float3 buildPos = CalcBuildPos(); bool canBuild=true; std::vector<CUnit*> units=qf->GetUnitsExact(buildPos,16); for(std::vector<CUnit*>::iterator ui=units.begin();ui!=units.end();++ui){ if((*ui)!=this) canBuild=false; } if(canBuild){ quedBuild=false; CUnit* b=unitLoader.LoadUnit(nextBuild,buildPos+float3(0.01,0.01,0.01),team,true,buildFacing); AddDeathDependence(b); curBuild=b; animator->AnimAction("StartBuilding"); if(unitDef->sounds.build.id) sound->PlaySound(unitDef->sounds.build.id, pos, unitDef->sounds.build.volume); } else { helper->BuggerOff(buildPos-float3(0.01,0,0.02),radius+8); } } if(curBuild && !beingBuilt){ lastBuild=gs->frameNum; int buildPiece = GetBuildPiece(); CMatrix44f mat=localmodel->GetPieceMatrix(buildPiece); int h=GetHeadingFromVector(mat[2],mat[10]); curBuild->heading=h; float3 buildPos = curBuild->pos = CalcBuildPos(buildPiece); if(curBuild->floatOnWater) curBuild->pos.y=ground->GetHeight(buildPos.x,buildPos.z)-curBuild->unitDef->waterline; curBuild->midPos=curBuild->pos+UpVector*curBuild->relMidPos.y; if(curBuild->AddBuildPower(buildSpeed,this)){ std::vector<int> args; args.push_back(0); animator->AnimAction("QueryNanoPiece",args); if(unitDef->showNanoSpray){ float3 relWeaponFirePos=localmodel->GetPiecePos(args[0]); float3 weaponPos=pos + frontdir*relWeaponFirePos.z + updir*relWeaponFirePos.y + rightdir*relWeaponFirePos.x; float3 dif=curBuild->midPos-weaponPos; float l=dif.Length(); dif/=l; dif+=gs->randVector()*0.15f; float3 color= unitDef->nanoColor; if(gu->teamNanospray){ unsigned char* tcol=gs->Team(team)->color; color = float3(tcol[0]*(1./255.),tcol[1]*(1./255.),tcol[2]*(1./255.)); } new CGfxProjectile(weaponPos,dif,(int)l,color); } } else { if(!curBuild->beingBuilt){ if(group) curBuild->SetGroup(group); Command c; c.id=CMD_MOVE_STATE; c.options=0; c.params.push_back(moveState); curBuild->commandAI->GiveCommand(c); c.params.clear(); c.id=CMD_FIRE_STATE; c.params.push_back(fireState); curBuild->commandAI->GiveCommand(c); if(curBuild->commandAI->commandQue.empty() || (dynamic_cast<CMobileCAI*>(curBuild->commandAI) && ((CMobileCAI*)curBuild->commandAI)->unimportantMove)){ if(((CFactoryCAI*)commandAI)->newUnitCommands.empty()){ SendToEmptySpot(curBuild); } else { for(std::deque<Command>::iterator ci=((CFactoryCAI*)commandAI)->newUnitCommands.begin();ci!=((CFactoryCAI*)commandAI)->newUnitCommands.end();++ci) curBuild->commandAI->GiveCommand(*ci); } } StopBuild(); } } } if(lastBuild+200 < gs->frameNum && !quedBuild && opening && uh->CanCloseYard(this)){ readmap->CloseBlockingYard(this); opening=false; animator->AnimAction(ANIMFN_Deactivate); } CBuilding::Update(); }
void CTAAirMoveType::UpdateFlying() { float3 &pos = owner->pos; float3 &speed = owner->speed; // Direction to where we would like to be float3 dir = goalPos - pos; owner->restTime = 0; // don't change direction for waypoints we just flew over and missed slightly if (flyState != FLY_LANDING && (owner->commandAI->HasMoreMoveCommands() && dir.Length2D() < 100) && (goalPos - pos).Normalize().distance(dir) < 1) { dir = owner->frontdir; } // are we there yet? bool closeToGoal = (dir.SqLength2D() < maxDrift * maxDrift) && (fabs(ground->GetHeight(pos.x, pos.z) - pos.y + wantedHeight) < maxDrift); if (flyState == FLY_ATTACKING) closeToGoal = (dir.SqLength2D() < 400); if (closeToGoal) { // pretty close switch (flyState) { case FLY_CRUISING: if (dontLand || (++waitCounter < 55 && dynamic_cast<CTransportUnit*>(owner)) || !autoLand) { // transport aircraft need some time to detect that they can pickup if (dynamic_cast<CTransportUnit*>(owner)) { wantedSpeed = ZeroVector; if (waitCounter > 60) { wantedHeight = orgWantedHeight; } } else { wantedSpeed = ZeroVector; if (!owner->commandAI->HasMoreMoveCommands()) wantToStop = true; SetState(AIRCRAFT_HOVERING); } } else { wantedHeight = orgWantedHeight; SetState(AIRCRAFT_LANDING); } return; case FLY_CIRCLING: // break; waitCounter++; if (waitCounter > 100) { if (owner->unitDef->airStrafe) { float3 relPos = pos - circlingPos; if (relPos.x < 0.0001f && relPos.x > -0.0001f) relPos.x = 0.0001f; relPos.y = 0; relPos.Normalize(); CMatrix44f rot; rot.RotateY(1.0f); float3 newPos = rot.Mul(relPos); // Make sure the point is on the circle newPos = newPos.Normalize() * goalDistance; //Go there in a straight line goalPos = circlingPos + newPos; } waitCounter = 0; } break; case FLY_ATTACKING:{ if (owner->unitDef->airStrafe) { float3 relPos = pos - circlingPos; if (relPos.x < 0.0001f && relPos.x > -0.0001f) relPos.x = 0.0001f; relPos.y = 0; relPos.Normalize(); CMatrix44f rot; if (gs->randFloat() > 0.5f) rot.RotateY(0.6f + gs->randFloat() * 0.6f); else rot.RotateY(-(0.6f + gs->randFloat() * 0.6f)); float3 newPos = rot.Mul(relPos); newPos = newPos.Normalize() * goalDistance; // Go there in a straight line goalPos = circlingPos + newPos; } break; } case FLY_LANDING:{ break; } } } // not there yet, so keep going dir.y = 0; float realMax = maxSpeed; float dist = dir.Length2D(); // If we are close to our goal, we should go slow enough to be able to break in time // new additional rule: if in attack mode or if we have more move orders then this is // an intermediate waypoint, don't slow down (FIXME) /// if (flyState != FLY_ATTACKING && dist < breakDistance && !owner->commandAI->HasMoreMoveCommands()) { if (flyState != FLY_ATTACKING && dist < breakDistance) { realMax = dist / (speed.Length2D() + 0.01f) * decRate; } wantedSpeed = dir.Normalize() * realMax; UpdateAirPhysics(); // Point toward goal or forward - unless we just passed it to get to another goal if ((flyState == FLY_ATTACKING) || (flyState == FLY_CIRCLING)) { dir = circlingPos - pos; } else if (flyState != FLY_LANDING && (owner->commandAI->HasMoreMoveCommands() && dist < 120) && (goalPos - pos).Normalize().distance(dir) > 1) { dir = owner->frontdir; } else { dir = goalPos - pos; } if (dir.SqLength2D() > 1) { int h = GetHeadingFromVector(dir.x, dir.z); wantedHeading = (h == 0)? wantedHeading: h; } }
void CWeapon::Update() { if(hasCloseTarget) { std::vector<int> args; args.push_back(0); if(useWeaponPosForAim) { owner->cob->Call(COBFN_QueryPrimary+weaponNum,args); } else { owner->cob->Call(COBFN_AimFromPrimary+weaponNum,args); } relWeaponPos=owner->localmodel->GetPiecePos(args[0]); } if(targetType==Target_Unit) { if(lastErrorVectorUpdate<gs->frameNum-16) { float3 newErrorVector(gs->randVector()); errorVectorAdd=(newErrorVector-errorVector)*(1.0f/16.0f); lastErrorVectorUpdate=gs->frameNum; } errorVector+=errorVectorAdd; if(weaponDef->selfExplode) { //assumes that only flakker like units that need to hit aircrafts has this,change to a separate tag later targetPos=helper->GetUnitErrorPos(targetUnit,owner->allyteam)+targetUnit->speed*(0.5+predictSpeedMod*0.5)*predict; } else { targetPos=helper->GetUnitErrorPos(targetUnit,owner->allyteam)+targetUnit->speed*predictSpeedMod*predict; } targetPos+=errorVector*(weaponDef->targetMoveError*30*targetUnit->speed.Length()*(1.0-owner->limExperience)); if(!weaponDef->waterweapon && targetPos.y<1) targetPos.y=1; } if(weaponDef->interceptor) CheckIntercept(); if(targetType!=Target_None) { if(onlyForward) { float3 goaldir=targetPos-owner->pos; goaldir.Normalize(); angleGood=owner->frontdir.dot(goaldir) > maxAngleDif; } else if(lastRequestedDir.dot(wantedDir)<maxAngleDif || lastRequest+15<gs->frameNum) { angleGood=false; lastRequestedDir=wantedDir; lastRequest=gs->frameNum; short int heading=GetHeadingFromVector(wantedDir.x,wantedDir.z); short int pitch=(short int) (asin(wantedDir.dot(owner->updir))*(32768/PI)); std::vector<int> args; args.push_back(short(heading-owner->heading)); args.push_back(pitch); owner->cob->Call(COBFN_AimPrimary+weaponNum,args,ScriptCallback,this,0); } } if(weaponDef->stockpile && numStockpileQued) { float p=1.0/reloadTime; if(gs->Team(owner->team)->metal>=metalFireCost*p && gs->Team(owner->team)->energy>=energyFireCost*p) { owner->UseEnergy(energyFireCost*p); owner->UseMetal(metalFireCost*p); buildPercent+=p; } if(buildPercent>=1) { buildPercent=0; numStockpileQued--; numStockpiled++; owner->commandAI->StockpileChanged(this); } } if(salvoLeft==0 #ifdef DIRECT_CONTROL_ALLOWED && (!owner->directControl || owner->directControl->mouse1 || owner->directControl->mouse2) #endif && targetType!=Target_None && angleGood && subClassReady && reloadStatus<=gs->frameNum && (weaponDef->stockpile || (gs->Team(owner->team)->metal>=metalFireCost && gs->Team(owner->team)->energy>=energyFireCost)) && (!weaponDef->stockpile || numStockpiled) && (weaponDef->waterweapon || weaponPos.y>0) ) { std::vector<int> args; args.push_back(0); owner->cob->Call(COBFN_QueryPrimary+weaponNum,args); relWeaponPos=owner->localmodel->GetPiecePos(args[0]); weaponPos=owner->pos+owner->frontdir*relWeaponPos.z+owner->updir*relWeaponPos.y+owner->rightdir*relWeaponPos.x; useWeaponPosForAim=reloadTime/16+8; if(TryTarget(targetPos,haveUserTarget,targetUnit)) { if(weaponDef->stockpile) { numStockpiled--; owner->commandAI->StockpileChanged(this); } else { owner->UseEnergy(energyFireCost); owner->UseMetal(metalFireCost); } if(weaponDef->stockpile) reloadStatus=gs->frameNum+60; else reloadStatus=gs->frameNum+(int)(reloadTime/owner->reloadSpeed); salvoLeft=salvoSize; nextSalvo=gs->frameNum; salvoError=gs->randVector()*(owner->isMoving?weaponDef->movingAccuracy:accuracy); if(targetType==Target_Pos || (targetType==Target_Unit && !(targetUnit->losStatus[owner->allyteam] & LOS_INLOS))) //area firing stuff is to effective at radar firing... salvoError*=1.3; owner->lastMuzzleFlameSize=muzzleFlareSize; owner->lastMuzzleFlameDir=wantedDir; owner->cob->Call(COBFN_FirePrimary+weaponNum); } } if(salvoLeft && nextSalvo<=gs->frameNum) { salvoLeft--; nextSalvo=gs->frameNum+salvoDelay; owner->lastFireWeapon=gs->frameNum; std::vector<int> args; args.push_back(0); owner->cob->Call(/*COBFN_AimFromPrimary+weaponNum/*/COBFN_QueryPrimary+weaponNum/**/,args); relWeaponPos=owner->localmodel->GetPiecePos(args[0]); weaponPos=owner->pos+owner->frontdir*relWeaponPos.z+owner->updir*relWeaponPos.y+owner->rightdir*relWeaponPos.x; // info->AddLine("RelPosFire %f %f %f",relWeaponPos.x,relWeaponPos.y,relWeaponPos.z); owner->isCloaked=false; owner->curCloakTimeout=gs->frameNum+owner->cloakTimeout; Fire(); //Rock the unit in the direction of the fireing float3 rockDir = wantedDir; rockDir.y = 0; rockDir = -rockDir.Normalize(); std::vector<int> rockAngles; rockAngles.push_back((int)(500 * rockDir.z)); rockAngles.push_back((int)(500 * rockDir.x)); owner->cob->Call(COBFN_RockUnit, rockAngles); owner->commandAI->WeaponFired(this); if(salvoLeft==0) { owner->cob->Call(COBFN_EndBurst+weaponNum); } #ifdef TRACE_SYNC tracefile << "Weapon fire: "; tracefile << weaponPos.x << " " << weaponPos.y << " " << weaponPos.z << " " << targetPos.x << " " << targetPos.y << " " << targetPos.z << "\n"; #endif } }
void CFactory::Update() { if (beingBuilt) { // factory under construction CUnit::Update(); return; } if (quedBuild && !opening && !stunned) { cob->Call(COBFN_Activate); groundBlockingObjectMap->OpenBlockingYard(this, yardMap); opening = true; } if (quedBuild && inBuildStance && !stunned) { // start building a unit float3 buildPos = CalcBuildPos(); bool canBuild = true; std::vector<CUnit*> units = qf->GetUnitsExact(buildPos, 16); for (std::vector<CUnit*>::iterator ui = units.begin(); ui != units.end(); ++ui) { if ((*ui) != this) canBuild = false; } if (canBuild) { quedBuild = false; CUnit* b = unitLoader.LoadUnit(nextBuild, buildPos + float3(0.01f, 0.01f, 0.01f), team, true, buildFacing, this); b->lineage = this->lineage; if (!unitDef->canBeAssisted) { b->soloBuilder = this; b->AddDeathDependence(this); } AddDeathDependence(b); curBuild = b; cob->Call("StartBuilding"); int soundIdx = unitDef->sounds.build.getRandomIdx(); if (soundIdx >= 0) { sound->PlaySample( unitDef->sounds.build.getID(soundIdx), pos, unitDef->sounds.build.getVolume(0)); } } else { helper->BuggerOff(buildPos - float3(0.01f, 0, 0.02f), radius + 8); } } if (curBuild && !beingBuilt) { if (!stunned) { // factory not under construction and // nanolathing unit: continue building lastBuild = gs->frameNum; // buildPiece is the rotating platform const int buildPiece = GetBuildPiece(); CMatrix44f mat = localmodel->GetPieceMatrix(buildPiece); const int h = GetHeadingFromVector(mat[2], mat[10]); // rotate unit nanoframe with platform curBuild->heading = (h + GetHeadingFromFacing(buildFacing)) & 65535; const float3 buildPos = CalcBuildPos(buildPiece); curBuild->pos = buildPos; if (curBuild->floatOnWater) { curBuild->pos.y = ground->GetHeight(buildPos.x, buildPos.z); curBuild->pos.y -= curBuild->unitDef->waterline; } curBuild->midPos = curBuild->pos + (UpVector * curBuild->relMidPos.y); const CCommandQueue& queue = commandAI->commandQue; if(!queue.empty() && (queue.front().id == CMD_WAIT)) { curBuild->AddBuildPower(0, this); } else { if (curBuild->AddBuildPower(buildSpeed, this)) { CreateNanoParticle(); } } } if (!curBuild->beingBuilt && (!unitDef->fullHealthFactory || (curBuild->health >= curBuild->maxHealth))) { if (group && curBuild->group == 0) { curBuild->SetGroup(group); } bool userOrders = true; if (curBuild->commandAI->commandQue.empty() || (dynamic_cast<CMobileCAI*>(curBuild->commandAI) && ((CMobileCAI*)curBuild->commandAI)->unimportantMove)) { userOrders = false; const CFactoryCAI* facAI = (CFactoryCAI*) commandAI; const CCommandQueue& newUnitCmds = facAI->newUnitCommands; if (newUnitCmds.empty()) { SendToEmptySpot(curBuild); } else { // XXX the pathfinder sometimes... makes mistakes, try to hack around it // XXX note this qualifies as HACK HACK HACK float3 testpos = curBuild->pos + frontdir * (this->radius - 1.0f); Command c; c.id = CMD_MOVE; c.params.push_back(testpos.x); c.params.push_back(testpos.y); c.params.push_back(testpos.z); curBuild->commandAI->GiveCommand(c); for (CCommandQueue::const_iterator ci = newUnitCmds.begin(); ci != newUnitCmds.end(); ++ci) { c = *ci; c.options |= SHIFT_KEY; curBuild->commandAI->GiveCommand(c); } } waitCommandsAI.AddLocalUnit(curBuild, this); } eventHandler.UnitFromFactory(curBuild, this, userOrders); StopBuild(); } } if (((lastBuild + 200) < gs->frameNum) && !stunned && !quedBuild && opening && groundBlockingObjectMap->CanCloseYard(this)) { // close the factory after inactivity groundBlockingObjectMap->CloseBlockingYard(this, yardMap); opening = false; cob->Call(COBFN_Deactivate); } CBuilding::Update(); }
void CWeapon::Update() { if(hasCloseTarget){ std::vector<int> args; args.push_back(0); if(useWeaponPosForAim){ owner->animator->AnimAction(ANIMFN_QueryPrimary+weaponNum,args); } else { owner->animator->AnimAction(ANIMFN_AimFromPrimary+weaponNum,args); } relWeaponPos=owner->localmodel->GetPiecePos(args[0]); } if(targetType==Target_Unit){ if(lastErrorVectorUpdate<gs->frameNum-16){ float3 newErrorVector(gs->randVector()); errorVectorAdd=(newErrorVector-errorVector)*(1.0f/16.0f); lastErrorVectorUpdate=gs->frameNum; } errorVector+=errorVectorAdd; if (predict > 50000) { /* to prevent runaway prediction (happens sometimes when a missile is moving *away* from it's target), we may need to disable missiles in case they fly around too long */ predict = 50000; } if(weaponDef->selfExplode){ //assumes that only flakker like units that need to hit aircrafts has this,change to a separate tag later targetPos=helper->GetUnitErrorPos(targetUnit,owner->allyteam)+targetUnit->speed*(0.5+predictSpeedMod*0.5)*predict; } else { targetPos=helper->GetUnitErrorPos(targetUnit,owner->allyteam)+targetUnit->speed*predictSpeedMod*predict; } targetPos+=errorVector*(weaponDef->targetMoveError*30*targetUnit->speed.Length()*(1.0-owner->limExperience)); float appHeight=ground->GetApproximateHeight(targetPos.x,targetPos.z)+2; if(targetPos.y < appHeight) targetPos.y=appHeight; if(!weaponDef->waterweapon && targetPos.y<1) targetPos.y=1; } if(weaponDef->interceptor) CheckIntercept(); if(targetType!=Target_None){ if(onlyForward){ float3 goaldir=targetPos-owner->pos; goaldir.Normalize(); angleGood=owner->frontdir.dot(goaldir) > maxAngleDif; } else if(lastRequestedDir.dot(wantedDir)<maxAngleDif || lastRequest+15<gs->frameNum){ angleGood=false; lastRequestedDir=wantedDir; lastRequest=gs->frameNum; short int heading=GetHeadingFromVector(wantedDir.x,wantedDir.z); short int pitch=(short int) (asin(wantedDir.dot(owner->updir))*(32768/PI)); std::vector<int> args; args.push_back(short(heading-owner->heading)); args.push_back(pitch); owner->animator->AnimAction(ANIMFN_AimPrimary+weaponNum,args,ScriptCallback,this,0); } } if(weaponDef->stockpile && numStockpileQued){ float p=1.0/reloadTime; if(gs->Team(owner->team)->metal>=metalFireCost*p && gs->Team(owner->team)->energy>=energyFireCost*p){ owner->UseEnergy(energyFireCost*p); owner->UseMetal(metalFireCost*p); buildPercent+=p; } else { // update the energy and metal required counts gs->Team(owner->team)->energyPullAmount += energyFireCost*p; gs->Team(owner->team)->metalPullAmount += metalFireCost*p; } if(buildPercent>=1){ buildPercent=0; numStockpileQued--; numStockpiled++; owner->commandAI->StockpileChanged(this); } } if(salvoLeft==0 #ifdef DIRECT_CONTROL_ALLOWED && (!owner->directControl || owner->directControl->mouse1 || owner->directControl->mouse2) #endif && targetType!=Target_None && angleGood && subClassReady && reloadStatus<=gs->frameNum && (!weaponDef->stockpile || numStockpiled) && (weaponDef->waterweapon || weaponPos.y>0) && (owner->unitDef->maxFuel==0 || owner->currentFuel > 0) ){ if ((weaponDef->stockpile || (gs->Team(owner->team)->metal>=metalFireCost && gs->Team(owner->team)->energy>=energyFireCost))) { std::vector<int> args; args.push_back(0); owner->animator->AnimAction(ANIMFN_QueryPrimary+weaponNum,args); relWeaponPos=owner->localmodel->GetPiecePos(args[0]); weaponPos=owner->pos+owner->frontdir*relWeaponPos.z+owner->updir*relWeaponPos.y+owner->rightdir*relWeaponPos.x; useWeaponPosForAim=reloadTime/16+8; if(TryTarget(targetPos,haveUserTarget,targetUnit)){ if(weaponDef->stockpile){ numStockpiled--; owner->commandAI->StockpileChanged(this); } else { owner->UseEnergy(energyFireCost); owner->UseMetal(metalFireCost); owner->currentFuel = max(0.0f, owner->currentFuel - fuelUsage); } if(weaponDef->stockpile) reloadStatus=gs->frameNum+60; else reloadStatus=gs->frameNum+(int)(reloadTime/owner->reloadSpeed); salvoLeft=salvoSize; nextSalvo=gs->frameNum; salvoError=gs->randVector()*(owner->isMoving?weaponDef->movingAccuracy:accuracy); if(targetType==Target_Pos || (targetType==Target_Unit && !(targetUnit->losStatus[owner->allyteam] & LOS_INLOS))) //area firing stuff is to effective at radar firing... salvoError*=1.3; owner->lastMuzzleFlameSize=muzzleFlareSize; owner->lastMuzzleFlameDir=wantedDir; owner->animator->AnimAction(ANIMFN_FirePrimary+weaponNum); } } else { if (TryTarget(targetPos,haveUserTarget,targetUnit) && !weaponDef->stockpile) { // update the energy and metal required counts const int minPeriod = max(1, (int)(reloadTime / owner->reloadSpeed)); const float averageFactor = 1.0f / (float)minPeriod; gs->Team(owner->team)->energyPullAmount += averageFactor * energyFireCost; gs->Team(owner->team)->metalPullAmount += averageFactor * metalFireCost; } } } if(salvoLeft && nextSalvo<=gs->frameNum){ salvoLeft--; nextSalvo=gs->frameNum+salvoDelay; owner->lastFireWeapon=gs->frameNum; // add a shot count if it helps to end the current 'attack ground' command // (the 'salvoLeft' check is done because the positions may already have been adjusted) if ((salvoLeft == (salvoSize - 1)) && ((targetType == Target_Pos) && (targetPos == owner->userAttackPos)) || ((targetType == Target_Unit) && (targetUnit == owner->userTarget))) { owner->commandShotCount++; } std::vector<int> args; args.push_back(0); owner->animator->AnimAction(/*ANIMFN_AimFromPrimary+weaponNum/*/ANIMFN_QueryPrimary+weaponNum/**/,args); relWeaponPos=owner->localmodel->GetPiecePos(args[0]); weaponPos=owner->pos+owner->frontdir*relWeaponPos.z+owner->updir*relWeaponPos.y+owner->rightdir*relWeaponPos.x; // info->AddLine("RelPosFire %f %f %f",relWeaponPos.x,relWeaponPos.y,relWeaponPos.z); owner->isCloaked=false; owner->curCloakTimeout=gs->frameNum+owner->cloakTimeout; Fire(); //Rock the unit in the direction of the fireing float3 rockDir = wantedDir; rockDir.y = 0; rockDir = -rockDir.Normalize(); std::vector<int> rockAngles; rockAngles.push_back((int)(500 * rockDir.z)); rockAngles.push_back((int)(500 * rockDir.x)); owner->animator->AnimAction(ANIMFN_RockUnit, rockAngles); owner->commandAI->WeaponFired(this); if(salvoLeft==0){ owner->animator->AnimAction(ANIMFN_EndBurst+weaponNum); } #ifdef TRACE_SYNC tracefile << "Weapon fire: "; tracefile << weaponPos.x << " " << weaponPos.y << " " << weaponPos.z << " " << targetPos.x << " " << targetPos.y << " " << targetPos.z << "\n"; #endif } }
bool CTAAirMoveType::Update() { float3& pos = owner->pos; float3& speed = owner->speed; // This is only set to false after the plane has finished constructing if (useHeading) { useHeading = false; SetState(AIRCRAFT_TAKEOFF); } if (owner->stunned || owner->beingBuilt) { wantedSpeed = ZeroVector; wantToStop = true; } // Allow us to stop if wanted if (wantToStop) { ExecuteStop(); } const float3 lastSpeed = speed; if (owner->fpsControlPlayer != NULL) { SetState(AIRCRAFT_FLYING); const FPSUnitController& con = owner->fpsControlPlayer->fpsController; const float3 forward = con.viewDir; const float3 right = forward.cross(UpVector); const float3 nextPos = pos + speed; float3 flatForward = forward; flatForward.y = 0.0f; flatForward.Normalize(); wantedSpeed = ZeroVector; if (con.forward) wantedSpeed += flatForward; if (con.back ) wantedSpeed -= flatForward; if (con.right ) wantedSpeed += right; if (con.left ) wantedSpeed -= right; wantedSpeed.Normalize(); wantedSpeed *= maxSpeed; if (!nextPos.IsInBounds()) { speed = ZeroVector; } UpdateAirPhysics(); wantedHeading = GetHeadingFromVector(flatForward.x, flatForward.z); } else { if (reservedPad) { CUnit* unit = reservedPad->GetUnit(); const float3 relPos = unit->script->GetPiecePos(reservedPad->GetPiece()); const 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_FLYING) { SetState(AIRCRAFT_FLYING); } flyState = FLY_LANDING; goalPos = pos; reservedLandingPos = pos; wantedHeight = pos.y - ground->GetHeightAboveWater(pos.x, pos.z); 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 / 30, 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) { airBaseHandler->LeaveLandingPad(reservedPad); reservedPad = NULL; padStatus = 0; goalPos = oldGoalPos; SetState(AIRCRAFT_TAKEOFF); } } } // Main state handling switch (aircraftState) { case AIRCRAFT_LANDED: UpdateLanded(); break; case AIRCRAFT_TAKEOFF: UpdateTakeoff(); break; case AIRCRAFT_FLYING: UpdateFlying(); break; case AIRCRAFT_LANDING: UpdateLanding(); break; case AIRCRAFT_HOVERING: UpdateHovering(); break; case AIRCRAFT_CRASHING: break; } } // Banking requires deltaSpeed.y = 0 deltaSpeed = speed - lastSpeed; deltaSpeed.y = 0.0f; // Turn and bank and move; update dirs UpdateHeading(); UpdateBanking(aircraftState == AIRCRAFT_HOVERING); owner->UpdateMidPos(); return (HandleCollisions()); }
void CWeapon::Update() { if(hasCloseTarget){ std::vector<int> args; args.push_back(0); if(useWeaponPosForAim){ //if we couldn't get a line of fire from the muzzle try if we can get it from the aim piece owner->cob->Call(COBFN_QueryPrimary+weaponNum,args); } else { owner->cob->Call(COBFN_AimFromPrimary+weaponNum,args); } relWeaponMuzzlePos=owner->localmodel->GetPiecePos(args[0]); owner->cob->Call(COBFN_AimFromPrimary+weaponNum,args); relWeaponPos=owner->localmodel->GetPiecePos(args[0]); } if(targetType==Target_Unit){ if(lastErrorVectorUpdate<gs->frameNum-16){ float3 newErrorVector(gs->randVector()); errorVectorAdd=(newErrorVector-errorVector)*(1.0f/16.0f); lastErrorVectorUpdate=gs->frameNum; } errorVector+=errorVectorAdd; if (predict > 50000) { /* to prevent runaway prediction (happens sometimes when a missile is moving *away* from it's target), we may need to disable missiles in case they fly around too long */ predict = 50000; } float3 lead = targetUnit->speed * (weaponDef->predictBoost+predictSpeedMod * (1.0f - weaponDef->predictBoost)) * predict; if (weaponDef->leadLimit >= 0.0f && lead.Length() > weaponDef->leadLimit + weaponDef->leadBonus * owner->experience) { lead *= (weaponDef->leadLimit + weaponDef->leadBonus*owner->experience) / (lead.Length() + 0.01f); } targetPos = helper->GetUnitErrorPos(targetUnit, owner->allyteam) + lead; targetPos += errorVector * (weaponDef->targetMoveError * 30 * targetUnit->speed.Length() * (1.0f - owner->limExperience)); float appHeight = ground->GetApproximateHeight(targetPos.x, targetPos.z) + 2; if (targetPos.y < appHeight) targetPos.y = appHeight; if (!weaponDef->waterweapon && targetPos.y < 1.0f) targetPos.y = 1.0f; } if (weaponDef->interceptor) { CheckIntercept(); } if (targetType != Target_None){ if (onlyForward) { float3 goaldir = targetPos - owner->pos; goaldir.Normalize(); angleGood = (owner->frontdir.dot(goaldir) > maxAngleDif); } else if (lastRequestedDir.dot(wantedDir) < maxAngleDif || lastRequest + 15 < gs->frameNum) { angleGood=false; lastRequestedDir=wantedDir; lastRequest=gs->frameNum; short int heading=GetHeadingFromVector(wantedDir.x,wantedDir.z); short int pitch=(short int) (asin(wantedDir.dot(owner->updir))*(32768/PI)); std::vector<int> args; args.push_back(short(heading - owner->heading)); args.push_back(pitch); owner->cob->Call(COBFN_AimPrimary+weaponNum,args,ScriptCallback,this,0); } } if(weaponDef->stockpile && numStockpileQued){ float p=1.0f/stockpileTime; if(gs->Team(owner->team)->metal>=metalFireCost*p && gs->Team(owner->team)->energy>=energyFireCost*p){ owner->UseEnergy(energyFireCost*p); owner->UseMetal(metalFireCost*p); buildPercent+=p; } else { // update the energy and metal required counts gs->Team(owner->team)->energyPull += energyFireCost*p; gs->Team(owner->team)->metalPull += metalFireCost*p; } if(buildPercent>=1){ const int oldCount = numStockpiled; buildPercent=0; numStockpileQued--; numStockpiled++; owner->commandAI->StockpileChanged(this); eventHandler.StockpileChanged(owner, this, oldCount); } } if ((salvoLeft == 0) #ifdef DIRECT_CONTROL_ALLOWED && (!owner->directControl || owner->directControl->mouse1 || owner->directControl->mouse2) #endif && (targetType != Target_None) && angleGood && subClassReady && (reloadStatus <= gs->frameNum) && (!weaponDef->stockpile || numStockpiled) && (weaponDef->fireSubmersed || (weaponMuzzlePos.y > 0)) && ((owner->unitDef->maxFuel == 0) || (owner->currentFuel > 0)) ) { if ((weaponDef->stockpile || (gs->Team(owner->team)->metal >= metalFireCost && gs->Team(owner->team)->energy >= energyFireCost))) { std::vector<int> args; args.push_back(0); owner->cob->Call(COBFN_QueryPrimary + weaponNum, args); owner->localmodel->GetEmitDirPos(args[0], relWeaponMuzzlePos, weaponDir); weaponMuzzlePos = owner->pos + owner->frontdir * relWeaponMuzzlePos.z + owner->updir * relWeaponMuzzlePos.y + owner->rightdir * relWeaponMuzzlePos.x; useWeaponPosForAim = reloadTime / 16 + 8; weaponDir = owner->frontdir * weaponDir.z + owner->updir * weaponDir.y + owner->rightdir * weaponDir.x; weaponDir.Normalize(); if (TryTarget(targetPos,haveUserTarget,targetUnit) && !CobBlockShot(targetUnit)) { if(weaponDef->stockpile){ const int oldCount = numStockpiled; numStockpiled--; owner->commandAI->StockpileChanged(this); eventHandler.StockpileChanged(owner, this, oldCount); } else { owner->UseEnergy(energyFireCost); owner->UseMetal(metalFireCost); owner->currentFuel = std::max(0.0f, owner->currentFuel - fuelUsage); } reloadStatus=gs->frameNum+(int)(reloadTime/owner->reloadSpeed); salvoLeft=salvoSize; nextSalvo=gs->frameNum; salvoError=gs->randVector()*(owner->isMoving?weaponDef->movingAccuracy:accuracy); if(targetType==Target_Pos || (targetType==Target_Unit && !(targetUnit->losStatus[owner->allyteam] & LOS_INLOS))) //area firing stuff is to effective at radar firing... salvoError*=1.3f; owner->lastMuzzleFlameSize=muzzleFlareSize; owner->lastMuzzleFlameDir=wantedDir; owner->cob->Call(COBFN_FirePrimary+weaponNum); } } else { // FIXME -- never reached? if (TryTarget(targetPos,haveUserTarget,targetUnit) && !weaponDef->stockpile) { // update the energy and metal required counts const int minPeriod = std::max(1, (int)(reloadTime / owner->reloadSpeed)); const float averageFactor = 1.0f / (float)minPeriod; gs->Team(owner->team)->energyPull += averageFactor * energyFireCost; gs->Team(owner->team)->metalPull += averageFactor * metalFireCost; } } } if(salvoLeft && nextSalvo<=gs->frameNum ){ salvoLeft--; nextSalvo=gs->frameNum+salvoDelay; owner->lastFireWeapon=gs->frameNum; int projectiles = projectilesPerShot; while(projectiles > 0) { --projectiles; // add to the commandShotCount if this is the last salvo, // and it is being directed towards the current target // (helps when deciding if a queued ground attack order has been completed) if ((salvoLeft == 0) && (owner->commandShotCount >= 0) && ((targetType == Target_Pos) && (targetPos == owner->userAttackPos)) || ((targetType == Target_Unit) && (targetUnit == owner->userTarget))) { owner->commandShotCount++; } std::vector<int> args; args.push_back(0); owner->cob->Call(COBFN_Shot+weaponNum,0); owner->cob->Call(COBFN_AimFromPrimary+weaponNum,args); relWeaponPos=owner->localmodel->GetPiecePos(args[0]); owner->cob->Call(/*COBFN_AimFromPrimary+weaponNum*/COBFN_QueryPrimary+weaponNum/**/,args); owner->localmodel->GetEmitDirPos(args[0], relWeaponMuzzlePos, weaponDir); 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; weaponDir = owner->frontdir * weaponDir.z + owner->updir * weaponDir.y + owner->rightdir * weaponDir.x; weaponDir.Normalize(); // logOutput.Print("RelPosFire %f %f %f",relWeaponPos.x,relWeaponPos.y,relWeaponPos.z); if (owner->unitDef->decloakOnFire && (owner->scriptCloak <= 2)) { if (owner->isCloaked) { owner->isCloaked = false; eventHandler.UnitDecloaked(owner); } owner->curCloakTimeout = gs->frameNum + owner->cloakTimeout; } Fire(); } //Rock the unit in the direction of the fireing float3 rockDir = wantedDir; rockDir.y = 0; rockDir = -rockDir.Normalize(); std::vector<int> rockAngles; rockAngles.push_back((int)(500 * rockDir.z)); rockAngles.push_back((int)(500 * rockDir.x)); owner->cob->Call(COBFN_RockUnit, rockAngles); owner->commandAI->WeaponFired(this); if(salvoLeft==0){ owner->cob->Call(COBFN_EndBurst+weaponNum); } #ifdef TRACE_SYNC tracefile << "Weapon fire: "; tracefile << weaponPos.x << " " << weaponPos.y << " " << weaponPos.z << " " << targetPos.x << " " << targetPos.y << " " << targetPos.z << "\n"; #endif } }
void CMobileCAI::ExecuteAttack(Command &c) { assert(owner->unitDef->canAttack); // limit how far away we fly based on our movestate if (tempOrder && orderTarget) { const float3& closestPos = ClosestPointOnLine(commandPos1, commandPos2, owner->pos); const float curTargetDist = LinePointDist(closestPos, commandPos2, orderTarget->pos); const float maxTargetDist = (500 * owner->moveState + owner->maxRange); if (owner->moveState < MOVESTATE_ROAM && curTargetDist > maxTargetDist) { StopMove(); FinishCommand(); return; } } // check if we are in direct command of attacker if (!inCommand) { if (c.params.size() == 1) { CUnit* targetUnit = unitHandler->GetUnit(c.params[0]); // check if we have valid target parameter and that we aren't attacking ourselves if (targetUnit == NULL) { StopMove(); FinishCommand(); return; } if (targetUnit == owner) { StopMove(); FinishCommand(); return; } if (targetUnit->GetTransporter() != NULL && !modInfo.targetableTransportedUnits) { StopMove(); FinishCommand(); return; } const float3 tgtErrPos = targetUnit->pos + owner->posErrorVector * 128; const float3 tgtPosDir = (tgtErrPos - owner->pos).Normalize(); SetGoal(tgtErrPos - tgtPosDir * targetUnit->radius, owner->pos); SetOrderTarget(targetUnit); owner->AttackUnit(targetUnit, (c.options & INTERNAL_ORDER) == 0, c.GetID() == CMD_MANUALFIRE); inCommand = true; } else if (c.params.size() >= 3) { // user gave force-fire attack command SetGoal(c.GetPos(0), owner->pos); inCommand = true; } } // if our target is dead or we lost it then stop attacking // NOTE: unit should actually just continue to target area! if (targetDied || (c.params.size() == 1 && UpdateTargetLostTimer(int(c.params[0])) == 0)) { // cancel keeppointingto StopMove(); FinishCommand(); return; } // user clicked on enemy unit (note that we handle aircrafts slightly differently) if (orderTarget != NULL) { bool tryTargetRotate = false; bool tryTargetHeading = false; float edgeFactor = 0.0f; // percent offset to target center const float3 targetMidPosVec = owner->midPos - orderTarget->midPos; const float targetGoalDist = (orderTarget->pos + owner->posErrorVector * 128.0f).SqDistance2D(goalPos); const float targetPosDist = Square(10.0f + orderTarget->pos.distance2D(owner->pos) * 0.2f); const float minPointingDist = std::min(1.0f * owner->losRadius * loshandler->losDiv, owner->maxRange * 0.9f); // FIXME? targetMidPosMaxDist is 3D, but compared with a 2D value const float targetMidPosDist2D = targetMidPosVec.Length2D(); //const float targetMidPosMaxDist = owner->maxRange - (orderTarget->speed.SqLength() / owner->unitDef->maxAcc); if (!owner->weapons.empty()) { if (!(c.options & ALT_KEY) && SkipParalyzeTarget(orderTarget)) { StopMove(); FinishCommand(); return; } } for (unsigned int wNum = 0; wNum < owner->weapons.size(); wNum++) { CWeapon* w = owner->weapons[wNum]; if (c.GetID() == CMD_MANUALFIRE) { assert(owner->unitDef->canManualFire); if (!w->weaponDef->manualfire) { continue; } } tryTargetRotate = w->TryTargetRotate(orderTarget, (c.options & INTERNAL_ORDER) == 0); tryTargetHeading = w->TryTargetHeading(GetHeadingFromVector(-targetMidPosVec.x, -targetMidPosVec.z), orderTarget->pos, orderTarget != NULL, orderTarget); if (tryTargetRotate || tryTargetHeading) break; edgeFactor = math::fabs(w->targetBorder); } // if w->AttackUnit() returned true then we are already // in range with our biggest (?) weapon, so stop moving // also make sure that we're not locked in close-in/in-range state loop // due to rotates invoked by in-range or out-of-range states if (tryTargetRotate) { const bool canChaseTarget = (!tempOrder || owner->moveState != MOVESTATE_HOLDPOS); const bool targetBehind = (targetMidPosVec.dot(orderTarget->speed) < 0.0f); if (canChaseTarget && tryTargetHeading && targetBehind) { SetGoal(owner->pos + (orderTarget->speed * 80), owner->pos, SQUARE_SIZE, orderTarget->speed.Length() * 1.1f); } else { StopMove(); if (gs->frameNum > lastCloseInTry + MAX_CLOSE_IN_RETRY_TICKS) { owner->moveType->KeepPointingTo(orderTarget->midPos, minPointingDist, true); } } owner->AttackUnit(orderTarget, (c.options & INTERNAL_ORDER) == 0, c.GetID() == CMD_MANUALFIRE); } // if we're on hold pos in a temporary order, then none of the close-in // code below should run, and the attack command is cancelled. else if (tempOrder && owner->moveState == MOVESTATE_HOLDPOS) { StopMove(); FinishCommand(); return; } // if ((our movetype has type HoverAirMoveType and length of 2D vector from us to target // less than 90% of our maximum range) OR squared length of 2D vector from us to target // less than 1024) then we are close enough else if (targetMidPosDist2D < (owner->maxRange * 0.9f)) { if (dynamic_cast<CHoverAirMoveType*>(owner->moveType) != NULL || (targetMidPosVec.SqLength2D() < 1024)) { StopMove(); owner->moveType->KeepPointingTo(orderTarget->midPos, minPointingDist, true); } // if (((first weapon range minus first weapon length greater than distance to target) // and length of 2D vector from us to target less than 90% of our maximum range) // then we are close enough, but need to move sideways to get a shot. //assumption is flawed: The unit may be aiming or otherwise unable to shoot else if (owner->unitDef->strafeToAttack && targetMidPosDist2D < (owner->maxRange * 0.9f)) { moveDir ^= (owner->moveType->progressState == AMoveType::Failed); const float sin = moveDir ? 3.0/5 : -3.0/5; const float cos = 4.0 / 5; float3 goalDiff; goalDiff.x = targetMidPosVec.dot(float3(cos, 0, -sin)); goalDiff.z = targetMidPosVec.dot(float3(sin, 0, cos)); goalDiff *= (targetMidPosDist2D < (owner->maxRange * 0.3f)) ? 1/cos : cos; goalDiff += orderTarget->pos; SetGoal(goalDiff, owner->pos); } } // if 2D distance of (target position plus attacker error vector times 128) // to goal position greater than // (10 plus 20% of 2D distance between attacker and target) then we need to close // in on target more else if (targetGoalDist > targetPosDist) { // if the target isn't in LOS, go to its approximate position // otherwise try to go precisely to the target // this should fix issues with low range weapons (mainly melee) const float3 errPos = ((orderTarget->losStatus[owner->allyteam] & LOS_INLOS)? ZeroVector: owner->posErrorVector * 128.0f); const float3 tgtPos = orderTarget->pos + errPos; const float3 norm = (tgtPos - owner->pos).Normalize(); const float3 goal = tgtPos - norm * (orderTarget->radius * edgeFactor * 0.8f); SetGoal(goal, owner->pos); if (lastCloseInTry < gs->frameNum + MAX_CLOSE_IN_RETRY_TICKS) lastCloseInTry = gs->frameNum; } } // user wants to attack the ground; cycle through our // weapons until we find one that can accomodate him else if (c.params.size() >= 3) { const float3 attackPos = c.GetPos(0); const float3 attackVec = attackPos - owner->pos; bool foundWeapon = false; for (unsigned int wNum = 0; wNum < owner->weapons.size(); wNum++) { CWeapon* w = owner->weapons[wNum]; if (foundWeapon) break; // XXX HACK - special weapon overrides any checks if (c.GetID() == CMD_MANUALFIRE) { assert(owner->unitDef->canManualFire); if (!w->weaponDef->manualfire) continue; if (attackVec.SqLength() >= (w->range * w->range)) continue; StopMove(); owner->AttackGround(attackPos, (c.options & INTERNAL_ORDER) == 0, c.GetID() == CMD_MANUALFIRE); owner->moveType->KeepPointingTo(attackPos, owner->maxRange * 0.9f, true); foundWeapon = true; } else { // NOTE: // we call TryTargetHeading which is less restrictive than TryTarget // (eg. the former succeeds even if the unit has not already aligned // itself with <attackVec>) if (w->TryTargetHeading(GetHeadingFromVector(attackVec.x, attackVec.z), attackPos, (c.options & INTERNAL_ORDER) == 0, NULL)) { if (w->TryTargetRotate(attackPos, (c.options & INTERNAL_ORDER) == 0)) { StopMove(); owner->AttackGround(attackPos, (c.options & INTERNAL_ORDER) == 0, c.GetID() == CMD_MANUALFIRE); foundWeapon = true; } // for gunships, this pitches the nose down such that // TryTargetRotate (which also checks range for itself) // has a bigger chance of succeeding // // hence it must be called as soon as we get in range // and may not depend on what TryTargetRotate returns // (otherwise we might never get a firing solution) owner->moveType->KeepPointingTo(attackPos, owner->maxRange * 0.9f, true); } } } #if 0 // no weapons --> no need to stop at an arbitrary distance? else if (diff.SqLength2D() < 1024) { StopMove(); owner->moveType->KeepPointingTo(attackPos, owner->maxRange * 0.9f, true); } #endif // if we are unarmed and more than 10 elmos distant // from target position, then keeping moving closer if (owner->weapons.empty() && attackPos.SqDistance2D(goalPos) > 100) { SetGoal(attackPos, owner->pos); } }
void CFactory::Update() { if(beingBuilt){ CUnit::Update(); return; } if(quedBuild && inBuildStance){ std::vector<long> args; args.push_back(0); cob->Call("QueryBuildInfo",args); float3 relBuildPos=localmodel->GetPiecePos(args[0]); float3 buildPos=pos + frontdir*relBuildPos.z + updir*relBuildPos.y + rightdir*relBuildPos.x; bool canBuild=true; std::vector<CUnit*> units=qf->GetUnitsExact(buildPos,16); for(std::vector<CUnit*>::iterator ui=units.begin();ui!=units.end();++ui){ if((*ui)!=this) canBuild=false; } if(canBuild){ quedBuild=false; CUnit* b=unitLoader.LoadUnit(nextBuild,buildPos+float3(0.01,0.01,0.01),team,true); AddDeathDependence(b); curBuild=b; cob->Call("StartBuilding"); if(unitDef->sounds.build.id) sound->PlaySound(unitDef->sounds.build.id, pos, unitDef->sounds.build.volume); } else { helper->BuggerOff(buildPos-float3(0.01,0,0.02),radius+8); } } if(curBuild && !beingBuilt){ lastBuild=gs->frameNum; std::vector<long> args; args.push_back(0); cob->Call("QueryBuildInfo",args); CMatrix44f mat=localmodel->GetPieceMatrix(args[0]); int h=GetHeadingFromVector(mat[2],mat[10]); curBuild->heading=h; // if(curBuild->unitDef->canfly){ //hack to get naval air plant to work correctly, how to do it correctly ? float3 relBuildPos=localmodel->GetPiecePos(args[0]); float3 buildPos=pos + frontdir*relBuildPos.z + updir*relBuildPos.y + rightdir*relBuildPos.x; curBuild->pos=buildPos; if(curBuild->floatOnWater) curBuild->pos.y=ground->GetHeight(buildPos.x,buildPos.z)-curBuild->unitDef->waterline; curBuild->midPos=curBuild->pos+UpVector*curBuild->relMidPos.y; // } if(curBuild->AddBuildPower(buildSpeed,this)){ std::vector<long> args; args.push_back(0); cob->Call("QueryNanoPiece",args); float3 relWeaponFirePos=localmodel->GetPiecePos(args[0]); float3 weaponPos=pos + frontdir*relWeaponFirePos.z + updir*relWeaponFirePos.y + rightdir*relWeaponFirePos.x; float3 dif=curBuild->midPos-weaponPos; float l=dif.Length(); dif/=l; dif+=gs->randVector()*0.15f; new CGfxProjectile(weaponPos,dif,l,float3(0.2f,0.7f,0.2f)); } else { if(!curBuild->beingBuilt){ if(group) curBuild->SetGroup(group); Command c; c.id=CMD_MOVE_STATE; c.options=0; c.params.push_back(moveState); curBuild->commandAI->GiveCommand(c); c.params.clear(); c.id=CMD_FIRE_STATE; c.params.push_back(fireState); curBuild->commandAI->GiveCommand(c); if(curBuild->commandAI->commandQue.empty() || (dynamic_cast<CMobileCAI*>(curBuild->commandAI) && ((CMobileCAI*)curBuild->commandAI)->unimportantMove)){ if(((CFactoryCAI*)commandAI)->newUnitCommands.empty()){ SendToEmptySpot(curBuild); } else { for(std::deque<Command>::iterator ci=((CFactoryCAI*)commandAI)->newUnitCommands.begin();ci!=((CFactoryCAI*)commandAI)->newUnitCommands.end();++ci) curBuild->commandAI->GiveCommand(*ci); } } StopBuild(); } } } if(lastBuild+200 < gs->frameNum && !quedBuild && opening && uh->CanCloseYard(this)){ readmap->CloseBlockingYard(this); opening=false; cob->Call(COBFN_Deactivate); } CBuilding::Update(); }
void CTAAirMoveType::UpdateFlying() { float3 &pos = owner->pos; float3 &speed = owner->speed; // Direction to where we would like to be float3 dir = goalPos - pos; owner->restTime = 0; // don't change direction for waypoints we just flew over and missed slightly if (flyState != FLY_LANDING && owner->commandAI->HasMoreMoveCommands() && dir != ZeroVector && dir.SqLength2D() < 10000.0f) { float3 ndir = dir; ndir = ndir.UnsafeANormalize(); if (ndir.SqDistance(dir) < 1.0f) { dir = owner->frontdir; } } const float gHeight = UseSmoothMesh()? std::max(smoothGround->GetHeight(pos.x, pos.z), ground->GetApproximateHeight(pos.x, pos.z)): ground->GetHeightAboveWater(pos.x, pos.z); // are we there yet? bool closeToGoal = (dir.SqLength2D() < maxDrift * maxDrift) && (fabs(gHeight - pos.y + wantedHeight) < maxDrift); if (flyState == FLY_ATTACKING) { closeToGoal = (dir.SqLength2D() < 400); } if (closeToGoal) { // pretty close switch (flyState) { case FLY_CRUISING: { bool trans = dynamic_cast<CTransportUnit*>(owner); bool noland = dontLand || !autoLand; // should CMD_LOAD_ONTO be here? bool hasLoadCmds = trans && !owner->commandAI->commandQue.empty() && (owner->commandAI->commandQue.front().id == CMD_LOAD_ONTO || owner->commandAI->commandQue.front().id == CMD_LOAD_UNITS); if (noland || (trans && ++waitCounter < 55 && hasLoadCmds)) { // transport aircraft need some time to detect that they can pickup if (trans) { wantedSpeed = ZeroVector; SetState(AIRCRAFT_HOVERING); if (waitCounter > 60) { wantedHeight = orgWantedHeight; } } else { wantedSpeed = ZeroVector; if (!owner->commandAI->HasMoreMoveCommands()) wantToStop = true; SetState(AIRCRAFT_HOVERING); } } else { wantedHeight = orgWantedHeight; SetState(AIRCRAFT_LANDING); } return; } case FLY_CIRCLING: // break; waitCounter++; if (waitCounter > 100) { if (owner->unitDef->airStrafe) { float3 relPos = pos - circlingPos; if (relPos.x < 0.0001f && relPos.x > -0.0001f) { relPos.x = 0.0001f; } relPos.y = 0.0f; relPos.Normalize(); static CMatrix44f rot(0.0f,fastmath::PI/4.0f,0.0f); float3 newPos = rot.Mul(relPos); // Make sure the point is on the circle newPos = newPos * goalDistance; // Go there in a straight line goalPos = circlingPos + newPos; } waitCounter = 0; } break; case FLY_ATTACKING: { if (owner->unitDef->airStrafe) { float3 relPos = pos - circlingPos; if (relPos.x < 0.0001f && relPos.x > -0.0001f) { relPos.x = 0.0001f; } relPos.y = 0; relPos.Normalize(); CMatrix44f rot; if (gs->randFloat() > 0.5f) { rot.RotateY(0.6f + gs->randFloat() * 0.6f); } else { rot.RotateY(-(0.6f + gs->randFloat() * 0.6f)); } float3 newPos = rot.Mul(relPos); newPos = newPos * goalDistance; // Go there in a straight line goalPos = circlingPos + newPos; } break; } case FLY_LANDING: { break; } } } // not there yet, so keep going dir.y = 0; float realMax = maxSpeed; float dist = dir.Length() + 0.1f; // If we are close to our goal, we should go slow enough to be able to break in time // new additional rule: if in attack mode or if we have more move orders then this is // an intermediate waypoint, don't slow down (FIXME) if (flyState != FLY_ATTACKING && dist < brakeDistance) { realMax = dist / (speed.Length2D() + 0.01f) * decRate; } wantedSpeed = (dir / dist) * realMax; UpdateAirPhysics(); // Point toward goal or forward - unless we just passed it to get to another goal if ((flyState == FLY_ATTACKING) || (flyState == FLY_CIRCLING)) { dir = circlingPos - pos; } else if (flyState != FLY_LANDING && (owner->commandAI->HasMoreMoveCommands() && dist < 120) && (goalPos - pos).Normalize().SqDistance(dir) > 1) { dir = owner->frontdir; } else { dir = goalPos - pos; } if (dir.SqLength2D() > 1) { const int h = GetHeadingFromVector(dir.x, dir.z); wantedHeading = (h == 0)? wantedHeading : h; } }
void CTAAirMoveType::Update() { float3& pos = owner->pos; float3& speed = owner->speed; // This is only set to false after the plane has finished constructing if (useHeading) { useHeading = false; SetState(AIRCRAFT_TAKEOFF); } // Allow us to stop if wanted if (wantToStop) ExecuteStop(); float3 lastSpeed = speed; if (owner->stunned) { wantedSpeed = ZeroVector; UpdateAirPhysics(); } else { if (owner->directControl) { DirectControlStruct* dc = owner->directControl; SetState(AIRCRAFT_FLYING); float3 forward = dc->viewDir; float3 flatForward = forward; flatForward.y = 0; flatForward.Normalize(); float3 right = forward.cross(UpVector); float3 nextPos = pos + speed; wantedSpeed = ZeroVector; if (dc->forward) wantedSpeed += flatForward; if (dc->back) wantedSpeed -= flatForward; if (dc->right) wantedSpeed += right; if (dc->left) wantedSpeed -= right; wantedSpeed.Normalize(); wantedSpeed *= maxSpeed; if (!nextPos.CheckInBounds()) { speed = ZeroVector; } UpdateAirPhysics(); wantedHeading = GetHeadingFromVector(flatForward.x, flatForward.z); } else { 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_FLYING) SetState(AIRCRAFT_FLYING); flyState = FLY_LANDING; goalPos = pos; reservedLandingPos = pos; wantedHeight = pos.y - ground->GetHeight(pos.x, pos.z); 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 / 30, 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) { airBaseHandler->LeaveLandingPad(reservedPad); reservedPad = 0; padStatus = 0; goalPos = oldGoalPos; SetState(AIRCRAFT_TAKEOFF); } } } // Main state handling switch (aircraftState) { case AIRCRAFT_LANDED: UpdateLanded(); break; case AIRCRAFT_TAKEOFF: UpdateTakeoff(); break; case AIRCRAFT_FLYING: UpdateFlying(); break; case AIRCRAFT_LANDING: UpdateLanding(); break; case AIRCRAFT_HOVERING: UpdateHovering(); break; case AIRCRAFT_CRASHING: break; } } } // Banking requires deltaSpeed.y = 0 deltaSpeed = speed - lastSpeed; deltaSpeed.y = 0; // Turn and bank and move UpdateHeading(); UpdateBanking(aircraftState == AIRCRAFT_HOVERING); // updates dirs owner->UpdateMidPos(); // Push other units out of the way if (pos != oldpos && aircraftState != AIRCRAFT_TAKEOFF && padStatus == 0) { oldpos = pos; if (!dontCheckCol && collide) { vector<CUnit*> nearUnits = qf->GetUnitsExact(pos, owner->radius + 6); vector<CUnit*>::iterator ui; for (ui = nearUnits.begin(); ui != nearUnits.end(); ++ui) { if ((*ui)->transporter) continue; 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)->mass >= 100000 || (*ui)->immobile) { pos -= dif * (dist - totRad); owner->UpdateMidPos(); owner->speed *= 0.99f; } 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 colSpeed = -owner->speed.dot(dif) + u->speed.dot(dif); owner->speed += dif * colSpeed * (1 - part); u->speed -= dif * colSpeed * (part); } } } } if (pos.x < 0) { pos.x += 0.6f; owner->midPos.x += 0.6f; } else if (pos.x > float3::maxxpos) { pos.x -= 0.6f; owner->midPos.x -= 0.6f; } if (pos.z < 0) { pos.z += 0.6f; owner->midPos.z += 0.6f; } else if (pos.z > float3::maxzpos) { pos.z -= 0.6f; owner->midPos.z -= 0.6f; } } }
void CFactory::Update() { if (beingBuilt) { // factory under construction CUnit::Update(); return; } if (quedBuild && !opening && !stunned) { script->Activate(); groundBlockingObjectMap->OpenBlockingYard(this, curYardMap); opening = true; } if (quedBuild && inBuildStance && !stunned) { // start building a unit const float3 buildPos = CalcBuildPos(); const CSolidObject* solidObj = groundBlockingObjectMap->GroundBlocked(buildPos); if (solidObj == NULL || (dynamic_cast<const CUnit*>(solidObj) == this)) { quedBuild = false; CUnit* b = unitLoader.LoadUnit(nextBuild, buildPos + float3(0.01f, 0.01f, 0.01f), team, true, buildFacing, this); if (!unitDef->canBeAssisted) { b->soloBuilder = this; b->AddDeathDependence(this); } AddDeathDependence(b); curBuild = b; script->StartBuilding(); int soundIdx = unitDef->sounds.build.getRandomIdx(); if (soundIdx >= 0) { Channels::UnitReply.PlaySample( unitDef->sounds.build.getID(soundIdx), pos, unitDef->sounds.build.getVolume(0)); } } else { helper->BuggerOff(buildPos - float3(0.01f, 0, 0.02f), radius + 8, true, true, NULL); } } if (curBuild && !beingBuilt) { if (!stunned) { // factory not under construction and // nanolathing unit: continue building lastBuild = gs->frameNum; // buildPiece is the rotating platform const int buildPiece = GetBuildPiece(); const CMatrix44f& mat = script->GetPieceMatrix(buildPiece); const int h = GetHeadingFromVector(mat[2], mat[10]); //! x.z, z.z // rotate unit nanoframe with platform curBuild->heading = (h + GetHeadingFromFacing(buildFacing)) & 65535; const float3 buildPos = CalcBuildPos(buildPiece); curBuild->pos = buildPos; if (curBuild->floatOnWater) { float waterline = ground->GetHeight(buildPos.x, buildPos.z) - curBuild->unitDef->waterline; if (waterline > curBuild->pos.y) curBuild->pos.y = waterline; } curBuild->midPos = curBuild->pos + (UpVector * curBuild->relMidPos.y); const CCommandQueue& queue = commandAI->commandQue; if(!queue.empty() && (queue.front().id == CMD_WAIT)) { curBuild->AddBuildPower(0, this); } else { if (curBuild->AddBuildPower(buildSpeed, this)) { CreateNanoParticle(); } } } if (!curBuild->beingBuilt && (!unitDef->fullHealthFactory || (curBuild->health >= curBuild->maxHealth))) { if (group && curBuild->group == 0) { curBuild->SetGroup(group); } bool userOrders = true; if (curBuild->commandAI->commandQue.empty() || (dynamic_cast<CMobileCAI*>(curBuild->commandAI) && ((CMobileCAI*)curBuild->commandAI)->unimportantMove)) { userOrders = false; AssignBuildeeOrders(curBuild); waitCommandsAI.AddLocalUnit(curBuild, this); } eventHandler.UnitFromFactory(curBuild, this, userOrders); StopBuild(); } } if (((lastBuild + 200) < gs->frameNum) && !stunned && !quedBuild && opening && groundBlockingObjectMap->CanCloseYard(this)) { // close the factory after inactivity groundBlockingObjectMap->CloseBlockingYard(this, curYardMap); opening = false; script->Deactivate(); } CBuilding::Update(); }
/** * @brief Causes this CMobileCAI to execute the attack order c */ void CMobileCAI::ExecuteAttack(Command &c) { assert(owner->unitDef->canAttack); // limit how far away we fly if (tempOrder && (owner->moveState < 2) && orderTarget && LinePointDist(ClosestPointOnLine(commandPos1, commandPos2, owner->pos), commandPos2, orderTarget->pos) > (500 * owner->moveState + owner->maxRange)) { StopMove(); FinishCommand(); return; } // check if we are in direct command of attacker if (!inCommand) { // don't start counting until the owner->AttackGround() order is given owner->commandShotCount = -1; if (c.params.size() == 1) { CUnit* targetUnit = uh->GetUnit(c.params[0]); // check if we have valid target parameter and that we aren't attacking ourselves if (targetUnit != NULL && targetUnit != owner) { float3 fix = targetUnit->pos + owner->posErrorVector * 128; float3 diff = float3(fix - owner->pos).Normalize(); SetGoal(fix - diff * targetUnit->radius, owner->pos); orderTarget = targetUnit; AddDeathDependence(orderTarget); inCommand = true; } else { // unit may not fire on itself, cancel order StopMove(); FinishCommand(); return; } } else if (c.params.size() >= 3) { // user gave force-fire attack command float3 pos(c.params[0], c.params[1], c.params[2]); SetGoal(pos, owner->pos); inCommand = true; } } else if ((c.params.size() == 3) && (owner->commandShotCount > 0) && (commandQue.size() > 1)) { // the trailing CMD_SET_WANTED_MAX_SPEED in a command pair does not count if ((commandQue.size() > 2) || (commandQue.back().id != CMD_SET_WANTED_MAX_SPEED)) { StopMove(); FinishCommand(); return; } } // if our target is dead or we lost it then stop attacking // NOTE: unit should actually just continue to target area! if (targetDied || (c.params.size() == 1 && UpdateTargetLostTimer(int(c.params[0])) == 0)) { // cancel keeppointingto StopMove(); FinishCommand(); return; } // user clicked on enemy unit (note that we handle aircrafts slightly differently) if (orderTarget) { //bool b1 = owner->AttackUnit(orderTarget, c.id == CMD_DGUN); bool b2 = false; bool b3 = false; bool b4 = false; float edgeFactor = 0.f; // percent offset to target center float3 diff = owner->pos - orderTarget->midPos; if (owner->weapons.size() > 0) { if (!(c.options & ALT_KEY) && SkipParalyzeTarget(orderTarget)) { StopMove(); FinishCommand(); return; } CWeapon* w = owner->weapons.front(); // if we have at least one weapon then check if we // can hit target with our first (meanest) one b2 = w->TryTargetRotate(orderTarget, c.id == CMD_DGUN); b3 = Square(w->range - (w->relWeaponPos).Length()) > (orderTarget->pos.SqDistance(owner->pos)); b4 = w->TryTargetHeading(GetHeadingFromVector(-diff.x, -diff.z), orderTarget->pos, orderTarget != NULL, orderTarget); edgeFactor = fabs(w->targetBorder); } float diffLength2d = diff.Length2D(); // if w->AttackUnit() returned true then we are already // in range with our biggest weapon so stop moving // also make sure that we're not locked in close-in/in-range state loop // due to rotates invoked by in-range or out-of-range states if (b2) { if (!(tempOrder && owner->moveState == 0) && (diffLength2d * 1.4f > owner->maxRange - orderTarget->speed.SqLength() / owner->unitDef->maxAcc) && b4 && diff.dot(orderTarget->speed) < 0) { SetGoal(owner->pos + (orderTarget->speed * 80), owner->pos, SQUARE_SIZE, orderTarget->speed.Length() * 1.1f); } else { StopMove(); // FIXME kill magic frame number if (gs->frameNum > lastCloseInTry + MAX_CLOSE_IN_RETRY_TICKS) { owner->moveType->KeepPointingTo(orderTarget->midPos, std::min((float) owner->losRadius * loshandler->losDiv, owner->maxRange * 0.9f), true); } } owner->AttackUnit(orderTarget, c.id == CMD_DGUN); } // if we're on hold pos in a temporary order, then none of the close-in // code below should run, and the attack command is cancelled. else if (tempOrder && owner->moveState == 0) { StopMove(); FinishCommand(); return; } // if ((our movetype has type TAAirMoveType and length of 2D vector from us to target // less than 90% of our maximum range) OR squared length of 2D vector from us to target // less than 1024) then we are close enough else if(diffLength2d < (owner->maxRange * 0.9f)){ if (dynamic_cast<CTAAirMoveType*>(owner->moveType) || (diff.SqLength2D() < 1024)) { StopMove(); owner->moveType->KeepPointingTo(orderTarget->midPos, std::min((float) owner->losRadius * loshandler->losDiv, owner->maxRange * 0.9f), true); } // if (((first weapon range minus first weapon length greater than distance to target) // and length of 2D vector from us to target less than 90% of our maximum range) // then we are close enough, but need to move sideways to get a shot. //assumption is flawed: The unit may be aiming or otherwise unable to shoot else if (owner->unitDef->strafeToAttack && b3 && diffLength2d < (owner->maxRange * 0.9f)) { moveDir ^= (owner->moveType->progressState == AMoveType::Failed); float sin = moveDir ? 3.0/5 : -3.0/5; float cos = 4.0/5; float3 goalDiff(0, 0, 0); goalDiff.x = diff.dot(float3(cos, 0, -sin)); goalDiff.z = diff.dot(float3(sin, 0, cos)); goalDiff *= (diffLength2d < (owner->maxRange * 0.3f)) ? 1/cos : cos; goalDiff += orderTarget->pos; SetGoal(goalDiff, owner->pos); } } // if 2D distance of (target position plus attacker error vector times 128) // to goal position greater than // (10 plus 20% of 2D distance between attacker and target) then we need to close // in on target more else if ((orderTarget->pos + owner->posErrorVector * 128).SqDistance2D(goalPos) > Square(10 + orderTarget->pos.distance2D(owner->pos) * 0.2f)) { // if the target isn't in LOS, go to its approximate position // otherwise try to go precisely to the target // this should fix issues with low range weapons (mainly melee) float3 fix = orderTarget->pos + (orderTarget->losStatus[owner->allyteam] & LOS_INLOS ? float3(0.f,0.f,0.f) : owner->posErrorVector * 128); float3 norm = float3(fix - owner->pos).Normalize(); float3 goal = fix - norm*(orderTarget->radius*edgeFactor*0.8f); SetGoal(goal, owner->pos); if (lastCloseInTry < gs->frameNum + MAX_CLOSE_IN_RETRY_TICKS) lastCloseInTry = gs->frameNum; } } // user is attacking ground else if (c.params.size() >= 3) { const float3 pos(c.params[0], c.params[1], c.params[2]); const float3 diff = owner->pos - pos; if (owner->weapons.size() > 0) { // if we have at least one weapon then check if // we can hit position with our first (assumed // to be meanest) one CWeapon* w = owner->weapons.front(); // XXX hack - dgun overrides any checks if (c.id == CMD_DGUN) { float rr = owner->maxRange * owner->maxRange; for (vector<CWeapon*>::iterator it = owner->weapons.begin(); it != owner->weapons.end(); ++it) { if (dynamic_cast<CDGunWeapon*>(*it)) rr = (*it)->range * (*it)->range; } if (diff.SqLength() < rr) { StopMove(); owner->AttackGround(pos, c.id == CMD_DGUN); owner->moveType->KeepPointingTo(pos, owner->maxRange * 0.9f, true); } } else { const bool inAngle = w->TryTargetRotate(pos, c.id == CMD_DGUN); const bool inRange = diff.SqLength2D() < Square(w->range - (w->relWeaponPos).Length2D()); if (inAngle || inRange) { StopMove(); owner->AttackGround(pos, c.id == CMD_DGUN); owner->moveType->KeepPointingTo(pos, owner->maxRange * 0.9f, true); } } } else if (diff.SqLength2D() < 1024) { StopMove(); owner->moveType->KeepPointingTo(pos, owner->maxRange * 0.9f, true); } // if we are more than 10 units distant from target position then keeping moving closer else if (pos.SqDistance2D(goalPos) > 100) { SetGoal(pos, owner->pos); } } }