void ShopPanel::DrawShip(const Ship &ship, const Point ¢er, bool isSelected) const { const Sprite *sprite = ship.GetSprite().GetSprite(); const Sprite *back = SpriteSet::Get( isSelected ? "ui/shipyard selected" : "ui/shipyard unselected"); SpriteShader::Draw(back, center); // Make sure the ship sprite leaves 10 pixels padding all around. float zoomSize = SHIP_SIZE - 60.f; // Draw the ship name. const string &name = ship.Name().empty() ? ship.ModelName() : ship.Name(); const Font &font = FontSet::Get(14); Point offset(-.5f * font.Width(name), -.5f * SHIP_SIZE + 10.f); font.Draw(name, center + offset, *GameData::Colors().Get("bright")); float zoom = min(1.f, zoomSize / max(sprite->Width(), sprite->Height())); int swizzle = GameData::PlayerGovernment()->GetSwizzle(); SpriteShader::Draw(sprite, center, zoom, swizzle); }
void ShipInfoDisplay::UpdateAttributes(const Ship &ship) { bool isGeneric = ship.Name().empty() || ship.GetPlanet(); attributeLabels.clear(); attributeValues.clear(); attributesHeight = 20; const Outfit &attributes = ship.Attributes(); attributeLabels.push_back("cost:"); attributeValues.push_back(Format::Number(ship.Cost())); attributesHeight += 20; attributeLabels.push_back(string()); attributeValues.push_back(string()); attributesHeight += 10; if(attributes.Get("shield generation")) { attributeLabels.push_back("shields charge / max:"); attributeValues.push_back(Format::Number(60. * attributes.Get("shield generation")) + " / " + Format::Number(attributes.Get("shields"))); } else { attributeLabels.push_back("shields:"); attributeValues.push_back(Format::Number(attributes.Get("shields"))); } attributesHeight += 20; if(attributes.Get("hull repair rate")) { attributeLabels.push_back("hull repair / max:"); attributeValues.push_back(Format::Number(60. * attributes.Get("hull repair rate")) + " / " + Format::Number(attributes.Get("hull"))); } else { attributeLabels.push_back("hull:"); attributeValues.push_back(Format::Number(attributes.Get("hull"))); } attributesHeight += 20; double emptyMass = ship.Mass(); attributeLabels.push_back(isGeneric ? "mass with no cargo:" : "mass:"); attributeValues.push_back(Format::Number(emptyMass)); attributesHeight += 20; attributeLabels.push_back(isGeneric ? "cargo space:" : "cargo:"); if(isGeneric) attributeValues.push_back(Format::Number(attributes.Get("cargo space"))); else attributeValues.push_back(Format::Number(ship.Cargo().Used()) + " / " + Format::Number(attributes.Get("cargo space"))); attributesHeight += 20; attributeLabels.push_back("required crew / bunks:"); attributeValues.push_back(Format::Number(ship.RequiredCrew()) + " / " + Format::Number(attributes.Get("bunks"))); attributesHeight += 20; attributeLabels.push_back(isGeneric ? "fuel capacity:" : "fuel:"); double fuelCapacity = attributes.Get("fuel capacity"); if(isGeneric) attributeValues.push_back(Format::Number(fuelCapacity)); else attributeValues.push_back(Format::Number(ship.Fuel() * fuelCapacity) + " / " + Format::Number(fuelCapacity)); attributesHeight += 20; double fullMass = emptyMass + (isGeneric ? attributes.Get("cargo space") : ship.Cargo().Used()); isGeneric &= (fullMass != emptyMass); attributeLabels.push_back(string()); attributeValues.push_back(string()); attributesHeight += 10; attributeLabels.push_back(isGeneric ? "movement, full / no cargo:" : "movement:"); attributeValues.push_back(string()); attributesHeight += 20; attributeLabels.push_back("max speed:"); attributeValues.push_back(Format::Number(60. * attributes.Get("thrust") / attributes.Get("drag"))); attributesHeight += 20; attributeLabels.push_back("acceleration:"); if(!isGeneric) attributeValues.push_back(Format::Number(3600. * attributes.Get("thrust") / fullMass)); else attributeValues.push_back(Format::Number(3600. * attributes.Get("thrust") / fullMass) + " / " + Format::Number(3600. * attributes.Get("thrust") / emptyMass)); attributesHeight += 20; attributeLabels.push_back("turning:"); if(!isGeneric) attributeValues.push_back(Format::Number(60. * attributes.Get("turn") / fullMass)); else attributeValues.push_back(Format::Number(60. * attributes.Get("turn") / fullMass) + " / " + Format::Number(60. * attributes.Get("turn") / emptyMass)); attributesHeight += 20; // Find out how much outfit, engine, and weapon space the chassis has. map<string, double> chassis; static const string names[] = { "outfit space free:", "outfit space", " weapon capacity:", "weapon capacity", " engine capacity:", "engine capacity", "gun ports free:", "gun ports", "turret mounts free:", "turret mounts" }; static const int NAMES = sizeof(names) / sizeof(names[0]); for(int i = 1; i < NAMES; i += 2) chassis[names[i]] = attributes.Get(names[i]); for(const auto &it : ship.Outfits()) for(auto &cit : chassis) cit.second -= it.second * it.first->Get(cit.first); attributeLabels.push_back(string()); attributeValues.push_back(string()); attributesHeight += 10; for(int i = 0; i < NAMES; i += 2) { attributeLabels.push_back(names[i]); attributeValues.push_back(Format::Number(attributes.Get(names[i + 1])) + " / " + Format::Number(chassis[names[i + 1]])); attributesHeight += 20; } if(ship.BaysFree(false)) { attributeLabels.push_back("drone bays:"); attributeValues.push_back(to_string(ship.BaysFree(false))); attributesHeight += 20; } if(ship.BaysFree(true)) { attributeLabels.push_back("fighter bays:"); attributeValues.push_back(to_string(ship.BaysFree(true))); attributesHeight += 20; } tableLabels.clear(); energyTable.clear(); heatTable.clear(); // Skip a spacer and the table header. attributesHeight += 30; tableLabels.push_back("idle:"); energyTable.push_back(Format::Number( 60. * (attributes.Get("energy generation") + attributes.Get("solar collection")))); heatTable.push_back(Format::Number( 60. * (attributes.Get("heat generation") - attributes.Get("cooling")))); attributesHeight += 20; tableLabels.push_back("moving:"); energyTable.push_back(Format::Number( -60. * (attributes.Get("thrusting energy") + attributes.Get("reverse thrusting energy") + attributes.Get("turning energy")))); heatTable.push_back(Format::Number( 60. * (attributes.Get("thrusting heat") + attributes.Get("reverse thrusting heat") + attributes.Get("turning heat")))); attributesHeight += 20; double firingEnergy = 0.; double firingHeat = 0.; for(const auto &it : ship.Outfits()) if(it.first->IsWeapon() && it.first->Reload()) { firingEnergy += it.second * it.first->FiringEnergy() / it.first->Reload(); firingHeat += it.second * it.first->FiringHeat() / it.first->Reload(); } tableLabels.push_back("firing:"); energyTable.push_back(Format::Number(-60. * firingEnergy)); heatTable.push_back(Format::Number(60. * firingHeat)); attributesHeight += 20; double shieldEnergy = attributes.Get("shield energy"); double hullEnergy = attributes.Get("hull energy"); tableLabels.push_back((shieldEnergy && hullEnergy) ? "shields / hull:" : hullEnergy ? "repairing hull:" : "charging shields:"); energyTable.push_back(Format::Number(-60. * (shieldEnergy + hullEnergy))); double shieldHeat = attributes.Get("shield heat"); double hullHeat = attributes.Get("hull heat"); heatTable.push_back(Format::Number(60. * (shieldHeat + hullHeat))); attributesHeight += 20; tableLabels.push_back("max:"); energyTable.push_back(Format::Number(attributes.Get("energy capacity"))); heatTable.push_back(Format::Number(60. * emptyMass * .1 * attributes.Get("heat dissipation"))); // Pad by 10 pixels on the top and bottom. attributesHeight += 30; }
void NetGameClient::DoObjKill(NetMsg* msg) { if (!msg) return; NetObjKill obj_kill; obj_kill.Unpack(msg->Data()); Ship* ship = FindShipByObjID(obj_kill.GetObjID()); if (ship) { Ship* killer = FindShipByObjID(obj_kill.GetKillerID()); Text killer_name = Game::GetText("NetGameClient.unknown"); if (killer) killer_name = killer->Name(); // log the kill: switch (obj_kill.GetKillType()) { default: case NetObjKill::KILL_MISC: Print("Ship '%s' destroyed (misc) (%s)\n", ship->Name(), FormatGameTime()); break; case NetObjKill::KILL_PRIMARY: case NetObjKill::KILL_SECONDARY: Print("Ship '%s' killed by '%s' (%s)\n", ship->Name(), killer_name.data(), FormatGameTime()); break; case NetObjKill::KILL_COLLISION: Print("Ship '%s' killed in collision with '%s' (%s)\n", ship->Name(), killer_name.data(), FormatGameTime()); break; case NetObjKill::KILL_CRASH: Print("Ship '%s' destroyed (crash) (%s)\n", ship->Name(), FormatGameTime()); case NetObjKill::KILL_DOCK: Print("Ship '%s' docked (%s)\n", ship->Name(), FormatGameTime()); } // record the kill in the stats: if (killer && obj_kill.GetKillType() != NetObjKill::KILL_DOCK) { ShipStats* kstats = ShipStats::Find(killer->Name()); if (kstats) { if (obj_kill.GetKillType() == NetObjKill::KILL_PRIMARY) kstats->AddEvent(SimEvent::GUNS_KILL, ship->Name()); else if (obj_kill.GetKillType() == NetObjKill::KILL_SECONDARY) kstats->AddEvent(SimEvent::MISSILE_KILL, ship->Name()); } if (killer && killer->GetIFF() != ship->GetIFF()) { if (ship->GetIFF() > 0 || killer->GetIFF() > 1) kstats->AddPoints(ship->Value()); } } ShipStats* killee = ShipStats::Find(ship->Name()); if (killee) { if (obj_kill.GetKillType() == NetObjKill::KILL_DOCK) killee->AddEvent(SimEvent::DOCK, killer_name); else killee->AddEvent(SimEvent::DESTROYED, killer_name); } if (obj_kill.GetKillType() == NetObjKill::KILL_DOCK) { FlightDeck* deck = killer->GetFlightDeck(obj_kill.GetFlightDeck()); sim->NetDockShip(ship, killer, deck); } else { ship->InflictNetDamage(ship->Integrity()); ship->DeathSpiral(); if (!obj_kill.GetRespawn()) ship->SetRespawnCount(0); else ship->SetRespawnLoc(obj_kill.GetRespawnLoc()); } } // this shouldn't happen in practice, // but if it does, this is what should be done: else { Shot* shot = FindShotByObjID(obj_kill.GetObjID()); if (shot) { ::Print("NetGameClient::DoObjKill destroying shot '%s'\n", shot->Name()); shot->Destroy(); } } }
void MissionEvent::Execute(bool silent) { Starshatter* stars = Starshatter::GetInstance(); HUDView* hud = HUDView::GetInstance(); Sim* sim = Sim::GetSim(); Ship* player = sim->GetPlayerShip(); Ship* ship = 0; Ship* src = 0; Ship* tgt = 0; Element* elem = 0; int pan = 0; bool end_mission = false; if (event_ship.length()) ship = sim->FindShip(event_ship); else ship = player; if (event_source.length()) src = sim->FindShip(event_source); if (event_target.length()) tgt = sim->FindShip(event_target); if (ship) elem = ship->GetElement(); else if (event_ship.length()) { elem = sim->FindElement(event_ship); if (elem) ship = elem->GetShip(1); } // expire the delay, if any remains delay = 0; // fire the event action switch (event) { case MESSAGE: if (event_message.length() > 0) { if (ship) { RadioMessage* msg = new(__FILE__,__LINE__) RadioMessage(ship, src, event_param[0]); msg->SetInfo(event_message); msg->SetChannel(ship->GetIFF()); if (tgt) msg->AddTarget(tgt); RadioTraffic::Transmit(msg); } else if (elem) { RadioMessage* msg = new(__FILE__,__LINE__) RadioMessage(elem, src, event_param[0]); msg->SetInfo(event_message); msg->SetChannel(elem->GetIFF()); if (tgt) msg->AddTarget(tgt); RadioTraffic::Transmit(msg); } } if (event_sound.length() > 0) { pan = event_param[0]; } break; case OBJECTIVE: if (elem) { if (event_param[0]) { elem->ClearInstructions(); elem->ClearObjectives(); } Instruction* obj = new(__FILE__,__LINE__) Instruction(event_param[0], 0); obj->SetTarget(event_target); elem->AddObjective(obj); if (elem->Contains(player)) { HUDView* hud = HUDView::GetInstance(); if (hud) hud->ShowHUDInst(); } } break; case INSTRUCTION: if (elem) { if (event_param[0]) elem->ClearInstructions(); elem->AddInstruction(event_message); if (elem->Contains(player) && event_message.length() > 0) { HUDView* hud = HUDView::GetInstance(); if (hud) hud->ShowHUDInst(); } } break; case IFF: if (elem) { elem->SetIFF(event_param[0]); } else if (ship) { ship->SetIFF(event_param[0]); } break; case DAMAGE: if (ship) { ship->InflictDamage(event_param[0]); if (ship->Integrity() < 1) { NetUtil::SendObjKill(ship, 0, NetObjKill::KILL_MISC); ship->DeathSpiral(); Print(" %s Killed By Scripted Event %d (%s)\n", (const char*) ship->Name(), id, FormatGameTime()); } } else { Print(" EVENT %d: Could not apply damage to ship '%s' (not found).\n", id, (const char*) event_ship); } break; case JUMP: if (ship) { SimRegion* rgn = sim->FindRegion(event_target); if (rgn && ship->GetRegion() != rgn) { if (rgn->IsOrbital()) { QuantumDrive* quantum_drive = ship->GetQuantumDrive(); if (quantum_drive) { quantum_drive->SetDestination(rgn, Point(0,0,0)); quantum_drive->Engage(true); // request immediate jump } else if (ship->IsAirborne()) { ship->MakeOrbit(); } } else { ship->DropOrbit(); } } } break; case HOLD: if (elem) elem->SetHoldTime(event_param[0]); break; case SKIP: { for (int i = 0; i < event_nparams; i++) { int skip_id = event_param[i]; ListIter<MissionEvent> iter = sim->GetEvents(); while (++iter) { MissionEvent* e = iter.value(); if (e->EventID() == skip_id) { if (e->status != COMPLETE) e->status = SKIPPED; } } } } break; case END_MISSION: Print(" END MISSION By Scripted Event %d (%s)\n", id, FormatGameTime()); end_mission = true; break; // // NOTE: CUTSCENE EVENTS DO NOT APPLY IN MULTIPLAYER // case BEGIN_SCENE: Print(" ------------------------------------\n"); Print(" Begin Cutscene '%s'\n", event_message.data()); stars->BeginCutscene(); break; case END_SCENE: Print(" End Cutscene '%s'\n", event_message.data()); Print(" ------------------------------------\n"); stars->EndCutscene(); break; case CAMERA: if (stars->InCutscene()) { CameraDirector* cam_dir = CameraDirector::GetInstance(); if (!cam_dir->GetShip()) cam_dir->SetShip(player); switch (event_param[0]) { case 1: if (cam_dir->GetMode() != CameraDirector::MODE_COCKPIT) cam_dir->SetMode(CameraDirector::MODE_COCKPIT, event_rect.x); break; case 2: if (cam_dir->GetMode() != CameraDirector::MODE_CHASE) cam_dir->SetMode(CameraDirector::MODE_CHASE, event_rect.x); break; case 3: if (cam_dir->GetMode() != CameraDirector::MODE_ORBIT) cam_dir->SetMode(CameraDirector::MODE_ORBIT, event_rect.x); break; case 4: if (cam_dir->GetMode() != CameraDirector::MODE_TARGET) cam_dir->SetMode(CameraDirector::MODE_TARGET, event_rect.x); break; } if (event_target.length()) { ::Print("Mission Event %d: setting camera target to %s\n", id, (const char*) event_target); Ship* s_tgt = 0; if (event_target.indexOf("body:") < 0) s_tgt = sim->FindShip(event_target); if (s_tgt) { ::Print(" found ship %s\n", s_tgt->Name()); cam_dir->SetViewOrbital(0); if (cam_dir->GetViewObject() != s_tgt) { if (event_param[0] == 6) { s_tgt->DropCam(event_param[1], event_param[2]); cam_dir->SetShip(s_tgt); cam_dir->SetMode(CameraDirector::MODE_DROP, 0); } else { Ship* cam_ship = cam_dir->GetShip(); if (cam_ship && cam_ship->IsDropCam()) { cam_ship->CompleteTransition(); } if (cam_dir->GetShip() != sim->GetPlayerShip()) cam_dir->SetShip(sim->GetPlayerShip()); cam_dir->SetViewObject(s_tgt, true); // immediate, no transition } } } else { const char* body_name = event_target.data(); if (!strncmp(body_name, "body:", 5)) body_name += 5; Orbital* orb = sim->FindOrbitalBody(body_name); if (orb) { ::Print(" found body %s\n", orb->Name()); cam_dir->SetViewOrbital(orb); } } } if (event_param[0] == 3) { cam_dir->SetOrbitPoint(event_point.x, event_point.y, event_point.z); } else if (event_param[0] == 5) { cam_dir->SetOrbitRates(event_point.x, event_point.y, event_point.z); } } break; case VOLUME: if (stars->InCutscene()) { AudioConfig* audio_cfg = AudioConfig::GetInstance(); audio_cfg->SetEfxVolume(event_param[0]); audio_cfg->SetWrnVolume(event_param[0]); } break; case DISPLAY: if (stars->InCutscene()) { DisplayView* disp_view = DisplayView::GetInstance(); if (disp_view) { Color color; color.Set(event_param[0]); if (event_message.length() && event_source.length()) { if (event_message.contains('$')) { Campaign* campaign = Campaign::GetCampaign(); Player* user = Player::GetCurrentPlayer(); CombatGroup* group = campaign->GetPlayerGroup(); if (user) { event_message = FormatTextReplace(event_message, "$NAME", user->Name().data()); event_message = FormatTextReplace(event_message, "$RANK", Player::RankName(user->Rank())); } if (group) { event_message = FormatTextReplace(event_message, "$GROUP", group->GetDescription()); } if (event_message.contains("$TIME")) { char timestr[32]; FormatDayTime(timestr, campaign->GetTime(), true); event_message = FormatTextReplace(event_message, "$TIME", timestr); } } disp_view->AddText( event_message, FontMgr::Find(event_source), color, event_rect, event_point.y, event_point.x, event_point.z); } else if (event_target.length()) { DataLoader* loader = DataLoader::GetLoader(); if (loader) { loader->SetDataPath(0); loader->LoadBitmap(event_target, image, 0, true); } if (image.Width() && image.Height()) disp_view->AddImage( &image, color, Video::BLEND_ALPHA, event_rect, event_point.y, event_point.x, event_point.z); } } } break; case FIRE_WEAPON: if (ship) { // fire single weapon: if (event_param[0] >= 0) { ship->FireWeapon(event_param[0]); } // fire all weapons: else { ListIter<WeaponGroup> g_iter = ship->Weapons(); while (++g_iter) { ListIter<Weapon> w_iter = g_iter->GetWeapons(); while (++w_iter) { Weapon* w = w_iter.value(); w->Fire(); } } } } break; default: break; } sim->ProcessEventTrigger(TRIGGER_EVENT, id); if (!silent && !sound && event_sound.length()) { DataLoader* loader = DataLoader::GetLoader(); bool use_fs = loader->IsFileSystemEnabled(); DWORD flags = pan ? Sound::LOCKED|Sound::LOCALIZED : Sound::LOCKED|Sound::AMBIENT; loader->UseFileSystem(true); loader->SetDataPath("Sounds/"); loader->LoadSound(event_sound, sound, flags); loader->SetDataPath(0); if (!sound) { loader->SetDataPath("Mods/Sounds/"); loader->LoadSound(event_sound, sound, flags); loader->SetDataPath(0); } if (!sound) { loader->LoadSound(event_sound, sound, flags); } loader->UseFileSystem(use_fs); // fire and forget: if (sound) { if (sound->GetFlags() & Sound::STREAMED) { sound->SetFlags(flags | sound->GetFlags()); sound->SetVolume(AudioConfig::VoxVolume()); sound->Play(); } else { sound->SetFlags(flags); sound->SetVolume(AudioConfig::VoxVolume()); sound->SetPan(pan); sound->SetFilename(event_sound); sound->AddToSoundCard(); sound->Play(); } } } status = COMPLETE; if (end_mission) { StarServer* server = StarServer::GetInstance(); if (stars) { stars->EndMission(); } else if (server) { // end mission event uses event_target member // to forward server to next mission in the chain: if (event_target.length()) server->SetNextMission(event_target); server->SetGameMode(StarServer::MENU_MODE); } } }
bool NetGameClient::DoJoinBacklog(NetJoinAnnounce* join_ann) { bool finished = false; if (!join_ann) return finished; Sim* sim = Sim::GetSim(); if (!sim) return finished; DWORD nid = join_ann->GetNetID(); DWORD oid = join_ann->GetObjID(); Text name = join_ann->GetName(); Text elem_name = join_ann->GetElement(); Text region = join_ann->GetRegion(); Point loc = join_ann->GetLocation(); Point velocity = join_ann->GetVelocity(); int index = join_ann->GetIndex(); int shld_lvl = join_ann->GetShield(); Ship* ship = 0; char ship_name[128]; strcpy_s(ship_name, Game::GetText("NetGameClient.no-ship").data()); if (nid && oid) { NetPlayer* remote_player = FindPlayerByObjID(oid); if (remote_player) { remote_player->SetName(name); remote_player->SetObjID(oid); if (index > 0) sprintf_s(ship_name, "%s %d", elem_name.data(), index); else sprintf_s(ship_name, "%s", elem_name.data()); } else { Element* element = sim->FindElement(elem_name); if (element) { ship = element->GetShip(index); } if (ship) { strcpy_s(ship_name, ship->Name()); SimRegion* rgn = ship->GetRegion(); if (rgn && region != rgn->Name()) { SimRegion* dst = sim->FindRegion(region); if (dst) dst->InsertObject(ship); } ship->MoveTo(loc); ship->SetVelocity(velocity); Shield* shield = ship->GetShield(); if (shield) shield->SetNetShieldLevel(shld_lvl); NetPlayer* remote_player = new(__FILE__,__LINE__) NetPlayer(nid); remote_player->SetName(name); remote_player->SetObjID(oid); remote_player->SetShip(ship); players.append(remote_player); finished = true; if (name == "Server A.I. Ship") { Print("NetGameClient::DoJoinBacklog() Remote Player '%s' has joined as '%s' with ID %d\n", name.data(), ship_name, oid); } else { HUDView::Message(Game::GetText("NetGameClient.remote-join").data(), name.data(), ship_name); } } } } return finished; }
void NetGameClient::DoJoinAnnounce(NetMsg* msg) { if (!msg) return; Sim* sim = Sim::GetSim(); if (!sim) return; NetJoinAnnounce* join_ann = new(__FILE__,__LINE__) NetJoinAnnounce; bool saved = false; if (join_ann->Unpack(msg->Data())) { DWORD nid = msg->NetID(); DWORD oid = join_ann->GetObjID(); Text name = join_ann->GetName(); Text elem_name = join_ann->GetElement(); Text region = join_ann->GetRegion(); Point loc = join_ann->GetLocation(); Point velocity = join_ann->GetVelocity(); int index = join_ann->GetIndex(); int shld_lvl = join_ann->GetShield(); join_ann->SetNetID(nid); Ship* ship = 0; char ship_name[128]; strcpy_s(ship_name, Game::GetText("NetGameClient.no-ship").data()); if (local_player && player_name == name) { HUDView::Message(Game::GetText("NetGameClient.local-accept"), name.data(), local_player->Name()); objid = oid; netid = nid; local_player->SetObjID(oid); local_player->SetNetObserver(false); Observe(local_player); SimRegion* rgn = local_player->GetRegion(); if (rgn && region != rgn->Name()) { SimRegion* dst = sim->FindRegion(region); if (dst) dst->InsertObject(local_player); } local_player->MoveTo(loc); local_player->SetVelocity(velocity); Shield* shield = local_player->GetShield(); if (shield) shield->SetNetShieldLevel(shld_lvl); } else { NetPlayer* remote_player = FindPlayerByObjID(oid); if (remote_player) { remote_player->SetName(name); remote_player->SetObjID(oid); if (index > 0) sprintf_s(ship_name, "%s %d", elem_name.data(), index); else sprintf_s(ship_name, "%s", elem_name.data()); } else { Element* element = sim->FindElement(elem_name); if (element) { ship = element->GetShip(index); } else { Print("NetGameClient::DoJoinAnnounce() could not find elem %s for player '%s' objid %d\n", elem_name.data(), name.data(), oid); NetUtil::SendElemRequest(elem_name.data()); } if (!ship) { // save it for later: join_backlog.append(join_ann); saved = true; } else { strcpy_s(ship_name, ship->Name()); SimRegion* rgn = ship->GetRegion(); if (rgn && region != rgn->Name()) { SimRegion* dst = sim->FindRegion(region); if (dst) dst->InsertObject(ship); } ship->MoveTo(loc); ship->SetVelocity(velocity); Shield* shield = ship->GetShield(); if (shield) shield->SetNetShieldLevel(shld_lvl); NetPlayer* remote_player = new(__FILE__,__LINE__) NetPlayer(nid); remote_player->SetName(name); remote_player->SetObjID(oid); remote_player->SetShip(ship); players.append(remote_player); if (name == "Server A.I. Ship") { Print("Remote Player '%s' has joined as '%s' with ID %d\n", name.data(), ship_name, oid); } else { HUDView::Message(Game::GetText("NetGameClient.remote-join").data(), name.data(), ship_name); } } } } } if (!saved) delete join_ann; }
void Engine::CalculateStep() { FrameTimer loadTimer; // Clear the list of objects to draw. draw[calcTickTock].Clear(step); radar[calcTickTock].Clear(); if(!player.GetSystem()) return; // Now, all the ships must decide what they are doing next. ai.Step(ships, player); const Ship *flagship = player.Flagship(); bool wasHyperspacing = (flagship && flagship->IsEnteringHyperspace()); // Now, move all the ships. We must finish moving all of them before any of // them fire, or their turrets will be targeting where a given ship was // instead of where it is now. This is also where ships get deleted, and // where they may create explosions if they are dying. for(auto it = ships.begin(); it != ships.end(); ) { int hyperspaceType = (*it)->HyperspaceType(); bool wasHere = (flagship && (*it)->GetSystem() == flagship->GetSystem()); bool wasHyperspacing = (*it)->IsHyperspacing(); // Give the ship the list of effects so that it can draw explosions, // ion sparks, jump drive flashes, etc. if(!(*it)->Move(effects, flotsam)) { // If Move() returns false, it means the ship should be removed from // play. That may be because it was destroyed, because it is an // ordinary ship that has been out of system for long enough to be // "forgotten," or because it is a fighter that just docked with its // mothership. Report it destroyed if that's really what happened: if((*it)->IsDestroyed()) eventQueue.emplace_back(nullptr, *it, ShipEvent::DESTROY); it = ships.erase(it); } else { if(&**it != flagship) { // Did this ship just begin hyperspacing? if(wasHere && !wasHyperspacing && (*it)->IsHyperspacing()) Audio::Play( Audio::Get(hyperspaceType >= 200 ? "jump out" : "hyperdrive out"), (*it)->Position()); // Did this ship just jump into the player's system? if(!wasHere && flagship && (*it)->GetSystem() == flagship->GetSystem()) Audio::Play( Audio::Get(hyperspaceType >= 200 ? "jump in" : "hyperdrive in"), (*it)->Position()); } // Boarding: bool autoPlunder = !(*it)->GetGovernment()->IsPlayer(); shared_ptr<Ship> victim = (*it)->Board(autoPlunder); if(victim) eventQueue.emplace_back(*it, victim, (*it)->GetGovernment()->IsEnemy(victim->GetGovernment()) ? ShipEvent::BOARD : ShipEvent::ASSIST); ++it; } } if(!wasHyperspacing && flagship && flagship->IsEnteringHyperspace()) Audio::Play(Audio::Get(flagship->HyperspaceType() >= 200 ? "jump drive" : "hyperdrive")); if(flagship && player.GetSystem() != flagship->GetSystem()) { // Wormhole travel: if(!wasHyperspacing) for(const auto &it : player.GetSystem()->Objects()) if(it.GetPlanet() && it.GetPlanet()->IsWormhole() && it.GetPlanet()->WormholeDestination(player.GetSystem()) == flagship->GetSystem()) player.Visit(it.GetPlanet()); doFlash = Preferences::Has("Show hyperspace flash"); player.SetSystem(flagship->GetSystem()); EnterSystem(); } // Draw the planets. Point newCenter = center; Point newCenterVelocity; if(flagship) { newCenter = flagship->Position(); newCenterVelocity = flagship->Velocity(); } else doClick = false; draw[calcTickTock].SetCenter(newCenter, newCenterVelocity); radar[calcTickTock].SetCenter(newCenter); for(const StellarObject &object : player.GetSystem()->Objects()) if(object.HasSprite()) { // Don't apply motion blur to very large planets and stars. if(object.Width() >= 280.) draw[calcTickTock].AddUnblurred(object); else draw[calcTickTock].Add(object); double r = max(2., object.Radius() * .03 + .5); radar[calcTickTock].Add(RadarType(object), object.Position(), r, r - 1.); if(object.GetPlanet()) object.GetPlanet()->DeployDefense(ships); Point position = object.Position() - newCenter; if(doClick && object.GetPlanet() && (clickPoint - position).Length() < object.Radius()) { if(&object == player.Flagship()->GetTargetPlanet()) { if(!object.GetPlanet()->CanLand()) Messages::Add("The authorities on " + object.GetPlanet()->Name() + " refuse to let you land."); else { clickCommands |= Command::LAND; Messages::Add("Landing on " + object.GetPlanet()->Name() + "."); } } else player.Flagship()->SetTargetPlanet(&object); } } // Add all neighboring systems to the radar. const System *targetSystem = flagship ? flagship->GetTargetSystem() : nullptr; const vector<const System *> &links = (flagship && flagship->Attributes().Get("jump drive")) ? player.GetSystem()->Neighbors() : player.GetSystem()->Links(); for(const System *system : links) radar[calcTickTock].AddPointer( (system == targetSystem) ? Radar::SPECIAL : Radar::INACTIVE, system->Position() - player.GetSystem()->Position()); // Now that the planets have been drawn, we can draw the asteroids on top // of them. This could be done later, as long as it is done before the // collision detection. asteroids.Step(); asteroids.Draw(draw[calcTickTock], newCenter); // Move existing projectiles. Do this before ships fire, which will create // new projectiles, since those should just stay where they are created for // this turn. This is also where projectiles get deleted, which may also // result in a "die" effect or a sub-munition being created. We could not // move the projectiles before this because some of them are homing and need // to know the current positions of the ships. list<Projectile> newProjectiles; for(auto it = projectiles.begin(); it != projectiles.end(); ) { if(!it->Move(effects)) { it->MakeSubmunitions(newProjectiles); it = projectiles.erase(it); } else ++it; } projectiles.splice(projectiles.end(), newProjectiles); // Move the flotsam, which should be drawn underneath the ships. for(auto it = flotsam.begin(); it != flotsam.end(); ) { if(!it->Move(effects)) { it = flotsam.erase(it); continue; } Ship *collector = nullptr; for(const shared_ptr<Ship> &ship : ships) { if(ship->GetSystem() != player.GetSystem() || ship->CannotAct()) continue; if(ship.get() == it->Source() || ship->Cargo().Free() < it->UnitSize()) continue; const Mask &mask = ship->GetMask(step); if(mask.Contains(it->Position() - ship->Position(), ship->Facing())) { collector = ship.get(); break; } } if(collector) { string name; if(collector->IsYours()) { if(collector->GetParent()) name = "Your ship \"" + collector->Name() + "\" picked up "; else name = "You picked up "; } string commodity; int amount = 0; if(it->OutfitType()) { amount = collector->Cargo().Add(it->OutfitType(), it->Count()); if(!name.empty()) { if(it->OutfitType()->Get("installable") < 0.) commodity = it->OutfitType()->Name(); else Messages::Add(name + Format::Number(amount) + " " + it->OutfitType()->Name() + (amount == 1 ? "." : "s.")); } } else { amount = collector->Cargo().Add(it->CommodityType(), it->Count()); if(!name.empty()) commodity = it->CommodityType(); } if(!commodity.empty()) Messages::Add(name + (amount == 1 ? "a ton" : Format::Number(amount) + " tons") + " of " + Format::LowerCase(commodity) + "."); it = flotsam.erase(it); continue; } // Draw this flotsam. draw[calcTickTock].Add(*it); ++it; } // Keep track of the relative strength of each government in this system. Do // not add more ships to make a winning team even stronger. This is mostly // to avoid having the player get mobbed by pirates, say, if they hang out // in one system for too long. map<const Government *, int64_t> strength; // Now, ships fire new projectiles, which includes launching fighters. If an // anti-missile system is ready to fire, it does not actually fire unless a // missile is detected in range during collision detection, below. vector<Ship *> hasAntiMissile; double clickRange = 50.; const Ship *previousTarget = nullptr; const Ship *clickTarget = nullptr; if(player.Flagship() && player.Flagship()->GetTargetShip()) previousTarget = &*player.Flagship()->GetTargetShip(); bool showFlagship = false; bool hasHostiles = false; for(shared_ptr<Ship> &ship : ships) if(ship->GetSystem() == player.GetSystem()) { strength[ship->GetGovernment()] += ship->Cost(); // Note: if a ship "fires" a fighter, that fighter was already in // existence and under the control of the same AI as the ship, but // its system was null to mark that it was not active. ship->Launch(ships); if(ship->Fire(projectiles, effects)) hasAntiMissile.push_back(ship.get()); int scan = ship->Scan(); if(scan) { shared_ptr<Ship> target = ship->GetTargetShip(); if(target && target->IsTargetable()) eventQueue.emplace_back(ship, target, scan); } // This is a good opportunity to draw all the ships in system. if(!ship->HasSprite()) continue; // Draw the flagship separately, on top of everything else. if(ship.get() != flagship) { AddSprites(*ship); if(ship->IsThrusting()) { for(const auto &it : ship->Attributes().FlareSounds()) if(it.second > 0) Audio::Play(it.first, ship->Position()); } } else showFlagship = true; // Do not show cloaked ships on the radar, except the player's ships. bool isPlayer = ship->GetGovernment()->IsPlayer(); if(ship->Cloaking() == 1. && !isPlayer) continue; if(doClick && &*ship != player.Flagship() && ship->IsTargetable()) { Point position = ship->Position() - newCenter; const Mask &mask = ship->GetMask(step); double range = mask.Range(clickPoint - position, ship->Facing()); if(range <= clickRange) { clickRange = range; clickTarget = ship.get(); player.Flagship()->SetTargetShip(ship); // If we've found an enemy within the click zone, favor // targeting it rather than any other ship. Otherwise, keep // checking for hits because another ship might be an enemy. if(!range && ship->GetGovernment()->IsEnemy()) doClick = false; } } double size = sqrt(ship->Width() + ship->Height()) * .14 + .5; bool isYourTarget = (flagship && ship == flagship->GetTargetShip()); int type = RadarType(*ship); hasHostiles |= (type == Radar::HOSTILE); radar[calcTickTock].Add(isYourTarget ? Radar::SPECIAL : type, ship->Position(), size); } if(flagship && showFlagship) { AddSprites(*flagship); if(flagship->IsThrusting()) { for(const auto &it : flagship->Attributes().FlareSounds()) if(it.second > 0) Audio::Play(it.first); } } if(clickTarget && clickTarget == previousTarget) clickCommands |= Command::BOARD; if(alarmTime) --alarmTime; else if(hasHostiles && !hadHostiles) { if(Preferences::Has("Warning siren")) Audio::Play(Audio::Get("alarm")); alarmTime = 180; hadHostiles = true; } else if(!hasHostiles) hadHostiles = false; // Collision detection: if(grudgeTime) --grudgeTime; for(Projectile &projectile : projectiles) { // The asteroids can collide with projectiles, the same as any other // object. If the asteroid turns out to be closer than the ship, it // shields the ship (unless the projectile has a blast radius). Point hitVelocity; double closestHit = 0.; shared_ptr<Ship> hit; const Government *gov = projectile.GetGovernment(); // If this "projectile" is a ship explosion, it always explodes. if(gov) { closestHit = asteroids.Collide(projectile, step, &hitVelocity); // Projectiles can only collide with ships that are in the current // system and are not landing, and that are hostile to this projectile. for(shared_ptr<Ship> &ship : ships) if(ship->GetSystem() == player.GetSystem() && !ship->IsLanding() && ship->Cloaking() < 1.) { if(ship.get() != projectile.Target() && !gov->IsEnemy(ship->GetGovernment())) continue; // This returns a value of 0 if the projectile has a trigger // radius and the ship is within it. double range = projectile.CheckCollision(*ship, step); if(range < closestHit) { closestHit = range; hit = ship; hitVelocity = ship->Velocity(); } } } if(closestHit < 1.) { // Create the explosion the given distance along the projectile's // motion path for this step. projectile.Explode(effects, closestHit, hitVelocity); // If this projectile has a blast radius, find all ships within its // radius. Otherwise, only one is damaged. if(projectile.HasBlastRadius()) { // Even friendly ships can be hit by the blast. for(shared_ptr<Ship> &ship : ships) if(ship->GetSystem() == player.GetSystem() && ship->Zoom() == 1.) if(projectile.InBlastRadius(*ship, step, closestHit)) { int eventType = ship->TakeDamage(projectile, ship != hit); if(eventType) eventQueue.emplace_back( projectile.GetGovernment(), ship, eventType); } } else if(hit) { int eventType = hit->TakeDamage(projectile); if(eventType) eventQueue.emplace_back( projectile.GetGovernment(), hit, eventType); } if(hit) DoGrudge(hit, projectile.GetGovernment()); } else if(projectile.MissileStrength()) { bool isEnemy = projectile.GetGovernment() && projectile.GetGovernment()->IsEnemy(); radar[calcTickTock].Add( isEnemy ? Radar::SPECIAL : Radar::INACTIVE, projectile.Position(), 1.); // If the projectile did not hit anything, give the anti-missile // systems a chance to shoot it down. for(Ship *ship : hasAntiMissile) if(ship == projectile.Target() || gov->IsEnemy(ship->GetGovernment()) || ship->GetGovernment()->IsEnemy(gov)) if(ship->FireAntiMissile(projectile, effects)) { projectile.Kill(); break; } } else if(projectile.HasBlastRadius()) radar[calcTickTock].Add(Radar::SPECIAL, projectile.Position(), 1.8); // Now, we can draw the projectile. The motion blur should be reduced // depending on how much motion blur is in the sprite itself: double innateVelocity = 2. * projectile.GetWeapon().Velocity(); Point relativeVelocity = projectile.Velocity() - projectile.Unit() * innateVelocity; draw[calcTickTock].AddProjectile(projectile, relativeVelocity, closestHit); } // Finally, draw all the effects, and then move them (because their motion // is not dependent on anything else, and this way we do all the work on // them in a single place. for(auto it = effects.begin(); it != effects.end(); ) { draw[calcTickTock].AddUnblurred(*it); if(!it->Move()) it = effects.erase(it); else ++it; } // Add incoming ships. for(const System::FleetProbability &fleet : player.GetSystem()->Fleets()) if(!Random::Int(fleet.Period())) { const Government *gov = fleet.Get()->GetGovernment(); if(!gov) continue; int64_t enemyStrength = 0; for(const auto &it : strength) if(gov->IsEnemy(it.first)) enemyStrength += it.second; if(enemyStrength && strength[gov] > 2 * enemyStrength) continue; fleet.Get()->Enter(*player.GetSystem(), ships); } if(!Random::Int(36000) && !player.GetSystem()->Links().empty()) { // Loop through all persons once to see if there are any who can enter // this system. int sum = 0; for(const auto &it : GameData::Persons()) sum += it.second.Frequency(player.GetSystem()); if(sum) { // Adjustment factor: special persons will appear once every ten // minutes, but much less frequently if the game only specifies a // few of them. This way, they will become more common as I add // more, without needing to change the 10-minute constant above. sum = Random::Int(sum + 1000); for(const auto &it : GameData::Persons()) { const Person &person = it.second; sum -= person.Frequency(player.GetSystem()); if(sum < 0) { shared_ptr<Ship> ship = person.GetShip(); ship->Recharge(); ship->SetName(it.first); ship->SetGovernment(person.GetGovernment()); ship->SetPersonality(person.GetPersonality()); ship->SetHail(person.GetHail()); Fleet::Enter(*player.GetSystem(), *ship); ships.push_front(ship); break; } } } } // Occasionally have some ship hail you. if(!Random::Int(600) && !ships.empty()) { shared_ptr<Ship> source; unsigned i = Random::Int(ships.size()); for(const shared_ptr<Ship> &it : ships) if(!i--) { source = it; break; } if(source->GetGovernment() && !source->GetGovernment()->IsPlayer() && !source->IsDisabled() && source->Crew()) { string message = source->GetHail(); if(!message.empty() && source->GetSystem() == player.GetSystem()) Messages::Add(source->GetGovernment()->GetName() + " ship \"" + source->Name() + "\": " + message); } } // A mouse click should only be active for a single step. doClick = false; // Keep track of how much of the CPU time we are using. loadSum += loadTimer.Time(); if(++loadCount == 60) { load = loadSum; loadSum = 0.; loadCount = 0; } }
void FltDlg::OnFilter(AWEvent* event) { if (!filter_list || !hangar_list) return; int seln = filter_list->GetSelectedIndex(); hangar_list->ClearItems(); if (!ship) return; Hangar* hangar = ship->GetHangar(); // selected squadron: if (seln < hangar->NumSquadrons()) { int nslots = hangar->SquadronSize(seln); for (int i = 0; i < nslots; i++) { char txt[32]; sprintf_s(txt, " %2d ", i+1); const HangarSlot* s = hangar->GetSlot(seln, i); hangar_list->AddItemWithData(txt, i); hangar_list->SetItemText(i, 1, "--"); hangar_list->SetItemText(i, 2, hangar->StatusName(s)); FormatTime(txt, hangar->TimeRemaining(s)); hangar_list->SetItemText(i, 4, txt); } } // selected pending filter: else if (seln == hangar->NumSquadrons()) { int item = 0; for (int f = 0; f < ship->NumFlightDecks(); f++) { FlightDeck* deck = ship->GetFlightDeck(f); if (deck->IsLaunchDeck()) { for (int i = 0; i < deck->NumSlots(); i++) { int state = deck->State(i); int seq = deck->Sequence(i); double time = deck->TimeRemaining(i); Ship* deckship = deck->GetShip(i); int squadron = -1; int slot = -1; char txt[32]; sprintf_s(txt, "%d-%d ", f+1, i+1); hangar_list->AddItemWithData(txt, item); // use data for sort if (deckship) { hangar_list->SetItemText(item, 1, deckship->Name()); const HangarSlot* s = 0; for (int a = 0; !s && a < hangar->NumSquadrons(); a++) { for (int b = 0; !s && b < hangar->SquadronSize(a); b++) { const HangarSlot* test = hangar->GetSlot(a, b); if (hangar->GetShip(test) == deckship) { s = test; squadron = a; slot = b; } } } if (s) { hangar_list->SetItemText(item, 2, hangar->StatusName(s)); if (hangar->GetPackageElement(s)) hangar_list->SetItemText(item, 3, Mission::RoleName(hangar->GetPackageElement(s)->Type())); } } else { hangar_list->SetItemText(item, 1, "--"); hangar_list->SetItemText(item, 2, Game::GetText("FltDlg.Open")); } FormatTime(txt, time); hangar_list->SetItemText(item, 4, txt); hangar_list->SetItemData(item, 1, f); hangar_list->SetItemData(item, 2, i); hangar_list->SetItemData(item, 3, squadron); hangar_list->SetItemData(item, 4, slot); item++; } } } } // selected active filter: else if (seln == hangar->NumSquadrons()+1) { int item = 0; for (int i = 0; i < hangar->NumSquadrons(); i++) { int nslots = hangar->SquadronSize(i); for (int j = 0; j < nslots; j++) { const HangarSlot* s = hangar->GetSlot(i,j); if (hangar->GetState(s) >= Hangar::ACTIVE) { char txt[32]; sprintf_s(txt, " %2d ", item+1); hangar_list->AddItemWithData(txt, item); // use data for sort if (hangar->GetShip(s)) hangar_list->SetItemText(item, 1, hangar->GetShip(s)->Name()); hangar_list->SetItemText(item, 2, hangar->StatusName(s)); if (hangar->GetPackageElement(s)) hangar_list->SetItemText(item, 3, Mission::RoleName(hangar->GetPackageElement(s)->Type())); FormatTime(txt, hangar->TimeRemaining(s)); hangar_list->SetItemText(item, 4, txt); hangar_list->SetItemData(item, 1, i); hangar_list->SetItemData(item, 2, j); item++; } } } } }
void FltDlg::UpdateSelection() { if (!filter_list || !hangar_list || !ship) return; design = 0; bool package = false; bool alert = false; bool launch = false; bool stand = false; bool recall = false; Hangar* hangar = ship->GetHangar(); int seln = filter_list->GetSelectedIndex(); char txt[32]; int item; // selected squadron: if (seln < hangar->NumSquadrons()) { int nslots = hangar->SquadronSize(seln); for (item = 0; item < hangar_list->NumItems(); item++) { int i = hangar_list->GetItemData(item); const HangarSlot* s = hangar->GetSlot(seln, i); if (hangar->GetState(s) == Hangar::UNAVAIL) hangar_list->SetItemColor(item, Color::DarkGray); else if (hangar->GetState(s) == Hangar::MAINT) hangar_list->SetItemColor(item, Color::Gray); else hangar_list->SetItemColor(item, Color::White); if (hangar->GetState(s) > Hangar::STORAGE) { if (hangar->GetShip(s)) hangar_list->SetItemText(item, 1, hangar->GetShip(s)->Name()); else if (hangar->GetPackageElement(s)) hangar_list->SetItemText(item, 1, hangar->GetPackageElement(s)->Name()); else hangar_list->SetItemText(item, 1, hangar->SquadronName(seln)); if (hangar->GetPackageElement(s)) hangar_list->SetItemText(item, 3, Mission::RoleName(hangar->GetPackageElement(s)->Type())); else hangar_list->SetItemText(item, 3, "--"); } else { hangar_list->SetItemText(item, 1, "--"); hangar_list->SetItemText(item, 3, "--"); } hangar_list->SetItemText(item, 2, hangar->StatusName(s)); if (hangar->GetState(s) >= Hangar::ACTIVE) { FormatTime(txt, hangar->GetShip(s)->MissionClock() / 1000); hangar_list->SetItemText(item, 4, txt); } else if (hangar->GetState(s) == Hangar::MAINT || hangar->GetState(s) > Hangar::STORAGE) { FormatTime(txt, hangar->TimeRemaining(s)); hangar_list->SetItemText(item, 4, txt); } else { hangar_list->SetItemText(item, 4, ""); } if (hangar_list->IsSelected(item)) { if (!design) design = hangar->GetDesign(s); switch (hangar->GetState(s)) { case Hangar::STORAGE: alert = true; break; case Hangar::ALERT: launch = true; stand = true; break; case Hangar::QUEUED: stand = true; break; case Hangar::ACTIVE: recall = true; break; } } } } // selected pending filter: else if (seln == hangar->NumSquadrons()) { for (item = 0; item < hangar_list->NumItems(); item++) { int f = hangar_list->GetItemData(item, 1); int i = hangar_list->GetItemData(item, 2); int squadron = -1; int slot = -1; const HangarSlot* s = 0; FlightDeck* deck = ship->GetFlightDeck(f); if (deck->IsLaunchDeck()) { int state = deck->State(i); int seq = deck->Sequence(i); double time = deck->TimeRemaining(i); Ship* deckship = deck->GetShip(i); if (deckship) { hangar_list->SetItemText(item, 1, deckship->Name()); for (int a = 0; !s && a < hangar->NumSquadrons(); a++) { for (int b = 0; !s && b < hangar->SquadronSize(a); b++) { const HangarSlot* test = hangar->GetSlot(a,b); if (hangar->GetShip(test) == deckship) { s = test; squadron = a; slot = b; } } } if (s) { hangar_list->SetItemText(item, 2, hangar->StatusName(s)); if (hangar->GetPackageElement(s)) hangar_list->SetItemText(item, 3, Mission::RoleName(hangar->GetPackageElement(s)->Type())); } } else { hangar_list->SetItemText(item, 1, "--"); hangar_list->SetItemText(item, 2, Game::GetText("FltDlg.Open")); } FormatTime(txt, time); hangar_list->SetItemText(item, 4, txt); hangar_list->SetItemData(item, 3, squadron); hangar_list->SetItemData(item, 4, slot); if (hangar_list->IsSelected(item) && s) { if (!design) design = hangar->GetDesign(s); switch (hangar->GetState(s)) { case Hangar::ALERT: launch = true; stand = true; break; case Hangar::QUEUED: stand = true; break; } } } } } // selected active filter: else if (seln == hangar->NumSquadrons()+1) { int last_index = -1; for (item = 0; item < hangar_list->NumItems(); item++) { int squadron = hangar_list->GetItemData(item, 1); int slot = hangar_list->GetItemData(item, 2); int nslots = hangar->SquadronSize(squadron); if (slot >= 0 && slot < nslots) { const HangarSlot* s = hangar->GetSlot(squadron, slot); if (hangar->GetState(s) > Hangar::STORAGE) { if (hangar->GetShip(s)) hangar_list->SetItemText(item, 1, hangar->GetShip(s)->Name()); else if (hangar->GetPackageElement(s)) hangar_list->SetItemText(item, 1, hangar->GetPackageElement(s)->Name()); else hangar_list->SetItemText(item, 1, hangar->SquadronName(squadron)); if (hangar->GetPackageElement(s)) hangar_list->SetItemText(item, 3, Mission::RoleName(hangar->GetPackageElement(s)->Type())); else hangar_list->SetItemText(item, 3, "--"); hangar_list->SetItemText(item, 2, hangar->StatusName(s)); FormatTime(txt, hangar->GetShip(s)->MissionClock() / 1000); hangar_list->SetItemText(item, 4, txt); if (last_index < (int) hangar_list->GetItemData(item)) last_index = (int) hangar_list->GetItemData(item); } else { hangar_list->RemoveItem(item); item--; } } else { hangar_list->RemoveItem(item); item--; } } for (int i = 0; i < hangar->NumSquadrons(); i++) { int nslots = hangar->SquadronSize(i); for (int j = 0; j < nslots; j++) { const HangarSlot* s = hangar->GetSlot(i, j); if (hangar->GetState(s) >= Hangar::ACTIVE) { bool found = false; for (int n = 0; !found && n < hangar_list->NumItems(); n++) { if (hangar_list->GetItemData(n, 1) == (DWORD) i && hangar_list->GetItemData(n, 2) == (DWORD) j) found = true; } if (!found) { last_index++; char txt[32]; sprintf_s(txt, "%2d ", last_index+1); hangar_list->AddItemWithData(txt, last_index); // use data for sort if (hangar->GetShip(s)) hangar_list->SetItemText(item, 1, hangar->GetShip(s)->Name()); hangar_list->SetItemText(item, 2, hangar->StatusName(s)); if (hangar->GetPackageElement(s)) hangar_list->SetItemText(item, 3, Mission::RoleName(hangar->GetPackageElement(s)->Type())); FormatTime(txt, hangar->GetShip(s)->MissionClock() / 1000); hangar_list->SetItemText(item, 4, txt); hangar_list->SetItemData(item, 1, i); hangar_list->SetItemData(item, 2, j); item++; } } } } if (hangar_list->GetSelCount() > 0) recall = true; } if (package_btn) { bool pkg_ok = alert && mission_type > -1; if (pkg_ok && mission_type > 0) { pkg_ok = objective_list && objective_list->GetSelCount() > 0; if (pkg_ok) { int obj_index = objective_list->GetSelection(); DWORD obj_data = objective_list->GetItemData(obj_index, 2); if (obj_data > 1e9) pkg_ok = false; } } package_btn->SetEnabled(pkg_ok); } if (alert_btn) { alert_btn->SetEnabled(alert); for (int i = 0; i < 6; i++) if (mission_btn[i]) mission_btn[i]->SetEnabled(alert); } if (launch_btn) launch_btn->SetEnabled(launch); if (stand_btn) stand_btn->SetEnabled(stand); if (recall_btn) recall_btn->SetEnabled(recall); }