void OutfitInfoDisplay::UpdateRequirements(const Outfit &outfit, const PlayerInfo &player, bool canSell)
{
	requirementLabels.clear();
	requirementValues.clear();
	requirementsHeight = 20;
	
	int day = player.GetDate().DaysSinceEpoch();
	int64_t cost = outfit.Cost();
	int64_t buyValue = player.StockDepreciation().Value(&outfit, day);
	int64_t sellValue = player.FleetDepreciation().Value(&outfit, day);
	
	if(buyValue == cost)
		requirementLabels.push_back("cost:");
	else
	{
		ostringstream out;
		out << "cost (" << (100 * buyValue) / cost << "%):";
		requirementLabels.push_back(out.str());
	}
	requirementValues.push_back(Format::Number(buyValue));
	requirementsHeight += 20;
	
	if(canSell && sellValue != buyValue)
	{
		if(sellValue == cost)
			requirementLabels.push_back("sells for:");
		else
		{
			ostringstream out;
			out << "sells for (" << (100 * sellValue) / cost << "%):";
			requirementLabels.push_back(out.str());
		}
		requirementValues.push_back(Format::Number(sellValue));
		requirementsHeight += 20;
	}
	
	static const string names[] = {
		"outfit space needed:", "outfit space",
		"weapon capacity needed:", "weapon capacity",
		"engine capacity needed:", "engine capacity",
		"gun ports needed:", "gun ports",
		"turret mounts needed:", "turret mounts"
	};
	static const int NAMES =  sizeof(names) / sizeof(names[0]);
	for(int i = 0; i + 1 < NAMES; i += 2)
		if(outfit.Get(names[i + 1]))
		{
			requirementLabels.push_back(string());
			requirementValues.push_back(string());
			requirementsHeight += 10;
		
			requirementLabels.push_back(names[i]);
			requirementValues.push_back(Format::Number(-outfit.Get(names[i + 1])));
			requirementsHeight += 20;
		}
}
Beispiel #2
0
// "Instantiate" a mission by replacing randomly selected values and places
// with a single choice, and then replacing any wildcard text as well.
Mission Mission::Instantiate(const PlayerInfo &player) const
{
	Mission result;
	// If anything goes wrong below, this mission should not be offered.
	result.hasFailed = true;
	result.isVisible = isVisible;
	result.hasPriority = hasPriority;
	result.isMinor = isMinor;
	result.autosave = autosave;
	result.location = location;
	result.repeat = repeat;
	result.name = name;
	result.waypoints = waypoints;
	// If one of the waypoints is the current system, it is already visited.
	auto it = result.waypoints.find(player.GetSystem());
	if(it != result.waypoints.end())
		result.waypoints.erase(it);
	
	// First, pick values for all the variables.
	
	// If a specific destination is not specified in the mission, pick a random
	// one out of all the destinations that satisfy the mission requirements.
	result.destination = destination;
	if(!result.destination && !destinationFilter.IsEmpty())
	{
		// Find a destination that satisfies the filter.
		vector<const Planet *> options;
		for(const auto &it : GameData::Planets())
		{
			// Skip entries with incomplete data.
			if(it.second.Name().empty() || (clearance.empty() && !it.second.CanLand()))
				continue;
			if(it.second.IsWormhole() || !it.second.HasSpaceport())
				continue;
			if(destinationFilter.Matches(&it.second, player.GetSystem()))
				options.push_back(&it.second);
		}
		if(!options.empty())
			result.destination = options[Random::Int(options.size())];
		else
			return result;
	}
	// If no destination is specified, it is the same as the source planet. Also
	// use the source planet if the given destination is not a valid planet name.
	if(!result.destination || !result.destination->GetSystem())
	{
		if(player.GetPlanet())
			result.destination = player.GetPlanet();
		else
			return result;
	}
	
	// If cargo is being carried, see if we are supposed to replace a generic
	// cargo name with something more specific.
	if(!cargo.empty())
	{
		const Trade::Commodity *commodity = nullptr;
		if(cargo == "random")
			commodity = PickCommodity(*player.GetSystem(), *result.destination->GetSystem());
		else
		{
			for(const Trade::Commodity &option : GameData::Commodities())
				if(option.name == cargo)
				{
					commodity = &option;
					break;
				}
		}
		if(commodity)
			result.cargo = commodity->items[Random::Int(commodity->items.size())];
		else
			result.cargo = cargo;
	}
	// Pick a random cargo amount, if requested.
	if(cargoSize || cargoLimit)
	{
		if(cargoProb)
			result.cargoSize = Random::Polya(cargoLimit, cargoProb) + cargoSize;
		else if(cargoLimit > cargoSize)
			result.cargoSize = cargoSize + Random::Int(cargoLimit - cargoSize + 1);
		else
			result.cargoSize = cargoSize;
	}
	// Pick a random passenger count, if requested.
	if(passengers | passengerLimit)
	{
		if(passengerProb)
			result.passengers = Random::Polya(passengerLimit, passengerProb) + passengers;
		else if(passengerLimit > passengers)
			result.passengers = passengers + Random::Int(passengerLimit - passengers + 1);
		else
			result.passengers = passengers;
	}
	result.illegalCargoFine = illegalCargoFine;
	
	// How far is it to the destination?
	DistanceMap distance(player.GetSystem());
	int jumps = distance.Distance(result.destination->GetSystem());
	int defaultPayment = (jumps + 1) * (150 * result.cargoSize + 1500 * result.passengers);
	int defaultDeadline = doDefaultDeadline ? (2 * jumps) : 0;
	
	// Set the deadline, if requested.
	if(daysToDeadline || defaultDeadline)
	{
		result.hasDeadline = true;
		result.deadline = player.GetDate() + (defaultDeadline + daysToDeadline);
	}
	
	// Copy the completion conditions. No need to copy the offer conditions,
	// because they have already been checked.
	result.toComplete = toComplete;
	result.toFail = toFail;
	
	// Generate the substitutions map.
	map<string, string> subs;
	subs["<commodity>"] = result.cargo;
	subs["<tons>"] = to_string(result.cargoSize) + (result.cargoSize == 1 ? " ton" : " tons");
	subs["<cargo>"] = subs["<tons>"] + " of " + subs["<commodity>"];
	subs["<bunks>"] = to_string(result.passengers);
	subs["<passengers>"] = (result.passengers == 1) ? "passenger" : "passengers";
	subs["<fare>"] = (result.passengers == 1) ? "a passenger" : (subs["<bunks>"] + " passengers");
	if(player.GetPlanet())
		subs["<origin>"] = player.GetPlanet()->Name();
	else if(player.BoardingShip())
		subs["<origin>"] = player.BoardingShip()->Name();
	subs["<planet>"] = result.destination ? result.destination->Name() : "";
	subs["<system>"] = result.destination ? result.destination->GetSystem()->Name() : "";
	subs["<destination>"] = subs["<planet>"] + " in the " + subs["<system>"] + " system";
	subs["<date>"] = result.deadline.ToString();
	subs["<day>"] = result.deadline.LongString();
	
	// Instantiate the NPCs. This also fills in the "<npc>" substitution.
	for(const NPC &npc : npcs)
		result.npcs.push_back(npc.Instantiate(subs, player.GetSystem()));
	
	// Instantiate the actions. The "complete" action is always first so that
	// the "<payment>" substitution can be filled in.
	for(const auto &it : actions)
		result.actions[it.first] = it.second.Instantiate(subs, defaultPayment);
	for(const auto &it : onEnter)
		result.onEnter[it.first] = it.second.Instantiate(subs, defaultPayment);
	
	// Perform substitution in the name and description.
	result.displayName = Format::Replace(displayName, subs);
	result.description = Format::Replace(description, subs);
	result.clearance = Format::Replace(clearance, subs);
	result.blocked = Format::Replace(blocked, subs);
	result.clearanceFilter = clearanceFilter;
	result.hasFullClearance = hasFullClearance;
	
	result.hasFailed = false;
	return result;
}
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());
}
Beispiel #4
0
// "Instantiate" a mission by replacing randomly selected values and places
// with a single choice, and then replacing any wildcard text as well.
Mission Mission::Instantiate(const PlayerInfo &player) const
{
	Mission result;
	// If anything goes wrong below, this mission should not be offered.
	result.hasFailed = true;
	result.isVisible = isVisible;
	result.hasPriority = hasPriority;
	result.isMinor = isMinor;
	result.autosave = autosave;
	result.location = location;
	result.repeat = repeat;
	result.name = name;
	result.waypoints = waypoints;
	// If one of the waypoints is the current system, it is already visited.
	auto it = result.waypoints.find(player.GetSystem());
	if(it != result.waypoints.end())
		result.waypoints.erase(it);
	
	// Copy the stopover planet list, and populate the list based on the filters
	// that were given.
	result.stopovers = stopovers;
	// Make sure they all exist in a valid system.
	for(auto it = result.stopovers.begin(); it != result.stopovers.end(); )
	{
		if((*it)->GetSystem())
			++it;
		else
			it = result.stopovers.erase(it);
	}
	for(const LocationFilter &filter : stopoverFilters)
	{
		const Planet *planet = PickPlanet(filter, player);
		if(!planet)
			return result;
		result.stopovers.insert(planet);
	}
	
	// First, pick values for all the variables.
	
	// If a specific destination is not specified in the mission, pick a random
	// one out of all the destinations that satisfy the mission requirements.
	result.destination = destination;
	if(!result.destination && !destinationFilter.IsEmpty())
	{
		result.destination = PickPlanet(destinationFilter, player);
		if(!result.destination)
			return result;
	}
	// If no destination is specified, it is the same as the source planet. Also
	// use the source planet if the given destination is not a valid planet name.
	if(!result.destination || !result.destination->GetSystem())
	{
		if(player.GetPlanet())
			result.destination = player.GetPlanet();
		else
			return result;
	}
	
	// If cargo is being carried, see if we are supposed to replace a generic
	// cargo name with something more specific.
	if(!cargo.empty())
	{
		const Trade::Commodity *commodity = nullptr;
		if(cargo == "random")
			commodity = PickCommodity(*player.GetSystem(), *result.destination->GetSystem());
		else
		{
			for(const Trade::Commodity &option : GameData::Commodities())
				if(option.name == cargo)
				{
					commodity = &option;
					break;
				}
			for(const Trade::Commodity &option : GameData::SpecialCommodities())
				if(option.name == cargo)
				{
					commodity = &option;
					break;
				}
		}
		if(commodity)
			result.cargo = commodity->items[Random::Int(commodity->items.size())];
		else
			result.cargo = cargo;
	}
	// Pick a random cargo amount, if requested.
	if(cargoSize || cargoLimit)
	{
		if(cargoProb)
			result.cargoSize = Random::Polya(cargoLimit, cargoProb) + cargoSize;
		else if(cargoLimit > cargoSize)
			result.cargoSize = cargoSize + Random::Int(cargoLimit - cargoSize + 1);
		else
			result.cargoSize = cargoSize;
	}
	// Pick a random passenger count, if requested.
	if(passengers | passengerLimit)
	{
		if(passengerProb)
			result.passengers = Random::Polya(passengerLimit, passengerProb) + passengers;
		else if(passengerLimit > passengers)
			result.passengers = passengers + Random::Int(passengerLimit - passengers + 1);
		else
			result.passengers = passengers;
	}
	result.illegalCargoFine = illegalCargoFine;
	
	// How far is it to the destination?
	DistanceMap distance(player.GetSystem());
	int jumps = distance.Distance(result.destination->GetSystem());
	int payload = result.cargoSize + 10 * result.passengers;
	
	// Set the deadline, if requested.
	if(deadlineBase || deadlineMultiplier)
		result.deadline = player.GetDate() + deadlineBase + deadlineMultiplier * jumps;
	
	// Copy the conditions. The offer conditions must be copied too, because they
	// may depend on a condition that other mission offers might change.
	result.toOffer = toOffer;
	result.toComplete = toComplete;
	result.toFail = toFail;
	
	// Generate the substitutions map.
	map<string, string> subs;
	subs["<commodity>"] = result.cargo;
	subs["<tons>"] = to_string(result.cargoSize) + (result.cargoSize == 1 ? " ton" : " tons");
	subs["<cargo>"] = subs["<tons>"] + " of " + subs["<commodity>"];
	subs["<bunks>"] = to_string(result.passengers);
	subs["<passengers>"] = (result.passengers == 1) ? "passenger" : "passengers";
	subs["<fare>"] = (result.passengers == 1) ? "a passenger" : (subs["<bunks>"] + " passengers");
	if(player.GetPlanet())
		subs["<origin>"] = player.GetPlanet()->Name();
	else if(player.BoardingShip())
		subs["<origin>"] = player.BoardingShip()->Name();
	subs["<planet>"] = result.destination ? result.destination->Name() : "";
	subs["<system>"] = result.destination ? result.destination->GetSystem()->Name() : "";
	subs["<destination>"] = subs["<planet>"] + " in the " + subs["<system>"] + " system";
	subs["<date>"] = result.deadline.ToString();
	subs["<day>"] = result.deadline.LongString();
	if(!result.stopovers.empty())
	{
		string planets;
		const Planet * const *last = &*--result.stopovers.end();
		int count = 0;
		// Iterate by reference to the pointers so we can check when we're at
		// the very last one in the set.
		for(const Planet * const &planet : result.stopovers)
		{
			if(count++)
				planets += (&planet != last) ? ", " : (count > 2 ? ", and " : " and ");
			planets += planet->Name() + " in the " + planet->GetSystem()->Name() + " system";
		}
		subs["<stopovers>"] = planets;
	}
	
	// Instantiate the NPCs. This also fills in the "<npc>" substitution.
	for(const NPC &npc : npcs)
		result.npcs.push_back(npc.Instantiate(subs, player.GetSystem()));
	
	// Instantiate the actions. The "complete" action is always first so that
	// the "<payment>" substitution can be filled in.
	for(const auto &it : actions)
		result.actions[it.first] = it.second.Instantiate(subs, jumps, payload);
	for(const auto &it : onEnter)
		result.onEnter[it.first] = it.second.Instantiate(subs, jumps, payload);
	
	// Perform substitution in the name and description.
	result.displayName = Format::Replace(displayName, subs);
	result.description = Format::Replace(description, subs);
	result.clearance = Format::Replace(clearance, subs);
	result.blocked = Format::Replace(blocked, subs);
	result.clearanceFilter = clearanceFilter;
	result.hasFullClearance = hasFullClearance;
	
	result.hasFailed = false;
	return result;
}
Beispiel #5
0
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();
}