// When the state of this mission changes, it may make changes to the player // information or show new UI panels. PlayerInfo::MissionCallback() will be // used as the callback for any UI panel that returns a value. bool Mission::Do(Trigger trigger, PlayerInfo &player, UI *ui) const { if(trigger == ACCEPT) { ++player.Conditions()[name + ": offered"]; ++player.Conditions()[name + ": active"]; } else if(trigger == DECLINE) ++player.Conditions()[name + ": offered"]; else if(trigger == FAIL) --player.Conditions()[name + ": active"]; else if(trigger == COMPLETE) { --player.Conditions()[name + ": active"]; ++player.Conditions()[name + ": done"]; } // "Jobs" should never show dialogs when offered, nor should they call the // player's mission callback. if(trigger == OFFER && location == JOB) ui = nullptr; auto it = actions.find(trigger); if(it == actions.end()) { // If a mission has no "on offer" field, it is automatically accepted. if(trigger == OFFER && location != JOB) player.MissionCallback(Conversation::ACCEPT); return true; } if(!it->second.CanBeDone(player)) return false; // Set a condition for the player's net worth. Limit it to the range of a 32-bit int. static const int64_t limit = 2000000000; player.Conditions()["net worth"] = min(limit, max(-limit, player.Accounts().NetWorth())); // Set the "reputation" conditions so we can check if this action changed // any of them. for(const auto &it : GameData::Governments()) { int rep = it.second.Reputation(); player.Conditions()["reputation: " + it.first] = rep; } it->second.Do(player, ui, destination ? destination->GetSystem() : nullptr); // Check if any reputation conditions were updated. for(const auto &it : GameData::Governments()) { int rep = it.second.Reputation(); int newRep = player.Conditions()["reputation: " + it.first]; if(newRep != rep) it.second.AddReputation(newRep - rep); } return true; }
// When the state of this mission changes, it may make changes to the player // information or show new UI panels. PlayerInfo::MissionCallback() will be // used as the callback for any UI panel that returns a value. bool Mission::Do(Trigger trigger, PlayerInfo &player, UI *ui) { if(trigger == STOPOVER) { // If this is not one of this mission's stopover planets, or if it is // not the very last one that must be visited, do nothing. auto it = stopovers.find(player.GetPlanet()); if(it == stopovers.end()) return false; for(const NPC &npc : npcs) if(npc.IsLeftBehind(player.GetSystem())) { ui->Push(new Dialog("This is a stop for one of your missions, but you have left a ship behind.")); return false; } stopovers.erase(it); if(!stopovers.empty()) return false; } if(trigger == ACCEPT) { ++player.Conditions()[name + ": offered"]; ++player.Conditions()[name + ": active"]; } else if(trigger == DECLINE) ++player.Conditions()[name + ": offered"]; else if(trigger == FAIL) --player.Conditions()[name + ": active"]; else if(trigger == COMPLETE) { --player.Conditions()[name + ": active"]; ++player.Conditions()[name + ": done"]; } // "Jobs" should never show dialogs when offered, nor should they call the // player's mission callback. if(trigger == OFFER && location == JOB) ui = nullptr; auto it = actions.find(trigger); if(it == actions.end()) { // If a mission has no "on offer" field, it is automatically accepted. if(trigger == OFFER && location != JOB) player.MissionCallback(Conversation::ACCEPT); return true; } if(!it->second.CanBeDone(player)) return false; // Set the "reputation" conditions so we can check if this action changed // any of them. for(const auto &it : GameData::Governments()) { int rep = it.second.Reputation(); player.Conditions()["reputation: " + it.first] = rep; } it->second.Do(player, ui, destination ? destination->GetSystem() : nullptr); // Check if any reputation conditions were updated. for(const auto &it : GameData::Governments()) { int rep = it.second.Reputation(); int newRep = player.Conditions()["reputation: " + it.first]; if(newRep != rep) it.second.AddReputation(newRep - rep); } return true; }
void MissionAction::Do(PlayerInfo &player, UI *ui, const System *destination) const { bool isOffer = (trigger == "offer"); if(!conversation.IsEmpty()) { ConversationPanel *panel = new ConversationPanel(player, conversation, destination); if(isOffer) panel->SetCallback(&player, &PlayerInfo::MissionCallback); ui->Push(panel); } else if(!dialogText.empty()) { map<string, string> subs; subs["<first>"] = player.FirstName(); subs["<last>"] = player.LastName(); if(player.Flagship()) subs["<ship>"] = player.Flagship()->Name(); string text = Format::Replace(dialogText, subs); if(isOffer) ui->Push(new Dialog(text, player, destination)); else ui->Push(new Dialog(text)); } else if(isOffer && ui) player.MissionCallback(Conversation::ACCEPT); Ship *flagship = player.Flagship(); for(const auto &it : gifts) { int count = it.second; string name = it.first->Name(); if(!count || name.empty()) continue; string message; if(abs(count) == 1) { char c = tolower(name.front()); bool isVowel = (c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u'); message = (isVowel ? "An " : "A ") + name + " was "; } else message = to_string(abs(count)) + " " + name + "s were "; if(count > 0) message += "added to your "; else message += "removed from your "; bool didCargo = false; bool didShip = false; int cargoCount = player.Cargo().Get(it.first); if(count < 0 && cargoCount) { int moved = min(cargoCount, -count); count += moved; player.Cargo().Transfer(it.first, moved); didCargo = true; } while(flagship && count) { int moved = (count > 0) ? 1 : -1; if(flagship->Attributes().CanAdd(*it.first, moved)) { flagship->AddOutfit(it.first, moved); didShip = true; } else break; count -= moved; } if(count > 0) { player.Cargo().Transfer(it.first, -count); didCargo = true; if(count > 0) { string special = "The " + name + (count == 1 ? " was" : "s were"); special += " put in your cargo hold because there is not enough space to install "; special += (count == 1) ? "it" : "them"; special += " in your ship."; ui->Push(new Dialog(special)); } } if(didCargo && didShip) message += "cargo hold and your flagship."; else if(didCargo) message += "cargo hold."; else message += "flagship."; Messages::Add(message); } if(payment) player.Accounts().AddCredits(payment); for(const auto &it : events) player.AddEvent(*GameData::Events().Get(it.first), player.GetDate() + it.second); conditions.Apply(player.Conditions()); }
void MissionAction::Do(PlayerInfo &player, UI *ui, const System *destination) const { bool isOffer = (trigger == "offer"); if(!conversation.IsEmpty() && ui) { ConversationPanel *panel = new ConversationPanel(player, conversation, destination); if(isOffer) panel->SetCallback(&player, &PlayerInfo::MissionCallback); ui->Push(panel); } else if(!dialogText.empty() && ui) { map<string, string> subs; subs["<first>"] = player.FirstName(); subs["<last>"] = player.LastName(); if(player.Flagship()) subs["<ship>"] = player.Flagship()->Name(); string text = Format::Replace(dialogText, subs); if(isOffer) ui->Push(new Dialog(text, player, destination)); else ui->Push(new Dialog(text)); } else if(isOffer && ui) player.MissionCallback(Conversation::ACCEPT); // If multiple outfits are being transferred, first remove them before // adding any new ones. for(const auto &it : gifts) if(it.second < 0) DoGift(player, it.first, it.second, ui); for(const auto &it : gifts) if(it.second > 0) DoGift(player, it.first, it.second, ui); if(payment) player.Accounts().AddCredits(payment); for(const auto &it : events) player.AddEvent(*GameData::Events().Get(it.first), player.GetDate() + it.second); if(!fail.empty()) { // Failing missions invalidates iterators into the player's mission list, // but does not immediately delete those missions. So, the safe way to // iterate over all missions is to make a copy of the list before we // begin to remove items from it. vector<const Mission *> failedMissions; for(const Mission &mission : player.Missions()) if(fail.count(mission.Identifier())) failedMissions.push_back(&mission); for(const Mission *mission : failedMissions) player.RemoveMission(Mission::FAIL, *mission, ui); } // Check if applying the conditions changes the player's reputations. player.SetReputationConditions(); conditions.Apply(player.Conditions()); player.CheckReputationConditions(); }