Example #1
0
// 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;
}
Example #2
0
void GameEvent::Apply(PlayerInfo &player)
{
	// Serialize the current reputation with other governments.
	player.SetReputationConditions();
	
	// Apply this event's ConditionSet to the player's conditions.
	conditionsToApply.Apply(player.Conditions());
	// Apply (and store a record of applying) this event's other general
	// changes (e.g. updating an outfitter's inventory).
	player.AddChanges(changes);
	
	// Update the current reputation with other governments (e.g. this
	// event's ConditionSet may have altered some reputations).
	player.CheckReputationConditions();
	
	for(const System *system : systemsToUnvisit)
		player.Unvisit(system);
	for(const Planet *planet : planetsToUnvisit)
		player.Unvisit(planet);
	
	// Perform visits after unvisits, as "unvisit <system>"
	// will unvisit any planets in that system.
	for(const System *system : systemsToVisit)
		player.Visit(system);
	for(const Planet *planet : planetsToVisit)
		player.Visit(planet);
}
Example #3
0
// Demand tribute, and get the planet's response.
string Planet::DemandTribute(PlayerInfo &player) const
{
	if(player.GetCondition("tribute: " + name))
		return "We are already paying you as much as we can afford.";
	if(!tribute || !defenseFleet || !defenseCount || player.GetCondition("combat rating") < defenseThreshold)
		return "Please don't joke about that sort of thing.";
	
	// The player is scary enough for this planet to take notice. Check whether
	// this is the first demand for tribute, or not.
	if(!isDefending)
	{
		isDefending = true;
		GameData::GetPolitics().Offend(defenseFleet->GetGovernment(), ShipEvent::PROVOKE);
		GameData::GetPolitics().Offend(GetGovernment(), ShipEvent::PROVOKE);
		return "Our defense fleet will make short work of you.";
	}
	
	// The player has already demanded tribute. Have they killed off the entire
	// defense fleet?
	bool isDefeated = (defenseDeployed == defenseCount);
	for(const shared_ptr<Ship> &ship : defenders)
		if(!ship->IsDisabled() && !ship->IsYours())
		{
			isDefeated = false;
			break;
		}
	
	if(!isDefeated)
		return "We're not ready to surrender yet.";
	
	player.Conditions()["tribute: " + name] = tribute;
	GameData::GetPolitics().DominatePlanet(this);
	return "We surrender. We will pay you " + Format::Number(tribute) + " credits per day to leave us alone.";
}
Example #4
0
// Check if it's possible to offer or complete this mission right now.
bool Mission::CanOffer(const PlayerInfo &player) const
{
	if(location == BOARDING || location == ASSISTING)
	{
		if(!player.BoardingShip())
			return false;
		
		if(!sourceFilter.Matches(*player.BoardingShip()))
			return false;
	}
	else
	{
		if(source && source != player.GetPlanet())
			return false;
	
		if(!sourceFilter.Matches(player.GetPlanet()))
			return false;
	}
	
	if(!toOffer.Test(player.Conditions()))
		return false;
	
	if(!toFail.IsEmpty() && toFail.Test(player.Conditions()))
		return false;
	
	if(repeat)
	{
		auto cit = player.Conditions().find(name + ": offered");
		if(cit != player.Conditions().end() && cit->second >= repeat)
			return false;
	}
	
	auto it = actions.find(OFFER);
	if(it != actions.end() && !it->second.CanBeDone(player))
		return false;
	
	it = actions.find(ACCEPT);
	if(it != actions.end() && !it->second.CanBeDone(player))
		return false;
	
	it = actions.find(DECLINE);
	if(it != actions.end() && !it->second.CanBeDone(player))
		return false;
	
	return true;
}
Example #5
0
bool Mission::HasFailed(const PlayerInfo &player) const
{
	if(!toFail.IsEmpty() && toFail.Test(player.Conditions()))
		return true;
	
	for(const NPC &npc : npcs)
		if(npc.HasFailed())
			return true;
	
	return hasFailed;
}
Example #6
0
void GameEvent::Apply(PlayerInfo &player)
{
	for(const auto &it : GameData::Governments())
	{
		int rep = it.second.Reputation();
		player.Conditions()["reputation: " + it.first] = rep;
	}
	
	conditionsToApply.Apply(player.Conditions());
	player.AddChanges(changes);
	
	for(const auto &it : GameData::Governments())
	{
		int rep = it.second.Reputation();
		int newRep = player.Conditions()["reputation: " + it.first];
		if(rep != newRep)
			it.second.AddReputation(newRep - rep);
	}
	
	for(const System *system : systemsToUnvisit)
		player.Unvisit(system);
}
Example #7
0
// Demand tribute, and get the planet's response.
string Planet::DemandTribute(PlayerInfo &player) const
{
	if(player.GetCondition("tribute: " + name))
		return "We are already paying you as much as we can afford.";
	if(!tribute || defenseFleets.empty())
		return "Please don't joke about that sort of thing.";
	if(player.GetCondition("combat rating") < defenseThreshold)
		return "You're not worthy of our time.";
	
	// The player is scary enough for this planet to take notice. Check whether
	// this is the first demand for tribute, or not.
	if(!isDefending)
	{
		isDefending = true;
		set<const Government *> toProvoke;
		for(const auto &fleet : defenseFleets)
			toProvoke.insert(fleet->GetGovernment());
		for(const auto &gov : toProvoke)
			gov->Offend(ShipEvent::PROVOKE);
		// Terrorizing a planet is not taken lightly by it or its allies.
		GetGovernment()->Offend(ShipEvent::ATROCITY);
		return "Our defense fleet will make short work of you.";
	}
	
	// The player has already demanded tribute. Have they defeated the entire defense fleet?
	bool isDefeated = (defenseDeployed == defenseFleets.size());
	for(const shared_ptr<Ship> &ship : defenders)
		if(!ship->IsDisabled() && !ship->IsYours())
		{
			isDefeated = false;
			break;
		}
	
	if(!isDefeated)
		return "We're not ready to surrender yet.";
	
	player.Conditions()["tribute: " + name] = tribute;
	GameData::GetPolitics().DominatePlanet(this);
	return "We surrender. We will pay you " + Format::Credits(tribute) + " credits per day to leave us alone.";
}
Example #8
0
bool Mission::CanComplete(const PlayerInfo &player) const
{
	if(player.GetPlanet() != destination || !waypoints.empty())
		return false;
	
	if(!toComplete.Test(player.Conditions()))
		return false;
	
	auto it = actions.find(COMPLETE);
	if(it != actions.end() && !it->second.CanBeDone(player))
		return false;
	
	for(const NPC &npc : npcs)
		if(!npc.HasSucceeded(player.GetSystem()))
			return false;
	
	// If any of the cargo for this mission is being carried by a ship that is
	// not in this system, the mission cannot be completed right now.
	for(const auto &ship : player.Ships())
		if(ship->GetSystem() != player.GetSystem() && ship->Cargo().Get(this))
			return false;
	
	return true;
}
Example #9
0
// 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;
}
Example #10
0
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());
}
Example #11
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();
}