Browse Source

Map and town events are now processed on start of player turn instead of
on start of new day, in line with H3

Ivan Savenko 1 year ago
parent
commit
26fdaacbe5
4 changed files with 85 additions and 130 deletions
  1. 22 4
      lib/mapping/CMap.cpp
  2. 1 1
      lib/mapping/CMap.h
  3. 60 123
      server/CGameHandler.cpp
  4. 2 2
      server/CGameHandler.h

+ 22 - 4
lib/mapping/CMap.cpp

@@ -52,14 +52,32 @@ CMapEvent::CMapEvent()
 
 }
 
-bool CMapEvent::earlierThan(const CMapEvent & other) const
+bool CMapEvent::occursToday(int currentDay) const
 {
-	return firstOccurrence < other.firstOccurrence;
+	if (currentDay == firstOccurrence + 1)
+		return true;
+
+	if (nextOccurrence == 0)
+		return false;
+
+	if (currentDay < firstOccurrence)
+		return false;
+
+	return (currentDay - firstOccurrence - 1) % nextOccurrence == 0;
 }
 
-bool CMapEvent::earlierThanOrEqual(const CMapEvent & other) const
+bool CMapEvent::affectsPlayer(PlayerColor color, bool isHuman) const
 {
-	return firstOccurrence <= other.firstOccurrence;
+	if (players.count(color) == 0)
+		return false;
+
+	if (!isHuman && !computerAffected)
+		return false;
+
+	if (isHuman && !humanAffected)
+		return false;
+
+	return true;
 }
 
 void CMapEvent::serializeJson(JsonSerializeFormat & handler)

+ 1 - 1
lib/mapping/CMap.h

@@ -136,7 +136,7 @@ public:
 	std::set<SpellID> allowedSpells;
 	std::set<ArtifactID> allowedArtifact;
 	std::set<SecondarySkill> allowedAbilities;
-	std::list<CMapEvent> events;
+	std::vector<CMapEvent> events;
 	int3 grailPos;
 	int grailRadius;
 

+ 60 - 123
server/CGameHandler.cpp

@@ -584,11 +584,6 @@ void CGameHandler::init(StartInfo *si, Load::ProgressAccumulator & progressTrack
 	reinitScripting();
 }
 
-static bool evntCmp(const CMapEvent &a, const CMapEvent &b)
-{
-	return a.earlierThan(b);
-}
-
 void CGameHandler::setPortalDwelling(const CGTownInstance * town, bool forced=false, bool clear = false)
 {// bool forced = true - if creature should be replaced, if false - only if no creature was set
 
@@ -635,6 +630,11 @@ void CGameHandler::onPlayerTurnStarted(PlayerColor which)
 {
 	events::PlayerGotTurn::defaultExecute(serverEventBus.get(), which);
 	turnTimerHandler->onPlayerGetTurn(which);
+
+	handleTimeEvents(which);
+
+	for (auto t : getPlayerState(which)->towns)
+		handleTownEvents(t);
 }
 
 void CGameHandler::onPlayerTurnEnded(PlayerColor which)
@@ -860,7 +860,6 @@ void CGameHandler::onNewTurn()
 	for (CGTownInstance *t : gs->map->towns)
 	{
 		PlayerColor player = t->tempOwner;
-		handleTownEvents(t, n);
 		if (newWeek) //first day of week
 		{
 			if (t->hasBuilt(BuildingSubID::PORTAL_OF_SUMMONING))
@@ -1017,7 +1016,6 @@ void CGameHandler::onNewTurn()
 		checkVictoryLossConditionsForAll(); // check for map turn limit
 
 	logGlobal->trace("Info about turn %d has been sent!", n.day);
-	handleTimeEvents();
 	//call objects
 	for (auto & elem : gs->map->objects)
 	{
@@ -3406,148 +3404,87 @@ bool CGameHandler::queryReply(QueryID qid, std::optional<int32_t> answer, Player
 	return true;
 }
 
-void CGameHandler::handleTimeEvents()
+void CGameHandler::handleTimeEvents(PlayerColor color)
 {
-	gs->map->events.sort(evntCmp);
-	while(gs->map->events.size() && gs->map->events.front().firstOccurrence+1 == gs->day)
+	for (auto const & event : gs->map->events)
 	{
-		CMapEvent ev = gs->map->events.front();
-
-		for (int player = 0; player < PlayerColor::PLAYER_LIMIT_I; player++)
-		{
-			auto color = PlayerColor(player);
-
-			const PlayerState * pinfo = getPlayerState(color, false); //do not output error if player does not exist
-
-			if (pinfo  //player exists
-				&& (ev.players & 1<<player) //event is enabled to this player
-				&& ((ev.computerAffected && !pinfo->human)
-					|| (ev.humanAffected && pinfo->human)
-				)
-			)
-			{
-				//give resources
-				giveResources(color, ev.resources);
-
-				//prepare dialog
-				InfoWindow iw;
-				iw.player = color;
-				iw.text = ev.message;
-
-				for (GameResID i : GameResID::ALL_RESOURCES())
-				{
-					if (ev.resources[i]) //if resource is changed, we add it to the dialog
-						iw.components.emplace_back(ComponentType::RESOURCE, i, ev.resources[i]);
-				}
+		if (!event.occursToday(gs->day))
+			continue;
 
-				sendAndApply(&iw); //show dialog
-			}
-		} //PLAYERS LOOP
+		if (!event.affectsPlayer(color, getPlayerState(color)->isHuman()))
+			continue;
 
-		if (ev.nextOccurrence)
-		{
-			gs->map->events.pop_front();
+		InfoWindow iw;
+		iw.player = color;
+		iw.text = event.message;
 
-			ev.firstOccurrence += ev.nextOccurrence;
-			auto it = gs->map->events.begin();
-			while(it != gs->map->events.end() && it->earlierThanOrEqual(ev))
-				it++;
-			gs->map->events.insert(it, ev);
-		}
-		else
+		//give resources
+		if (!event.resources.empty())
 		{
-			gs->map->events.pop_front();
+			giveResources(color, event.resources);
+			for (GameResID i : GameResID::ALL_RESOURCES())
+				if (event.resources[i])
+					iw.components.emplace_back(ComponentType::RESOURCE, i, event.resources[i]);
 		}
+		sendAndApply(&iw); //show dialog
 	}
-
-	//TODO send only if changed
-	UpdateMapEvents ume;
-	ume.events = gs->map->events;
-	sendAndApply(&ume);
 }
 
-void CGameHandler::handleTownEvents(CGTownInstance * town, NewTurn &n)
+void CGameHandler::handleTownEvents(CGTownInstance * town)
 {
-	std::sort(town->events.begin(), town->events.end(), evntCmp);
-	while(town->events.size() && town->events.front().firstOccurrence == gs->day)
+	for (auto const & event : town->events)
 	{
-		PlayerColor player = town->tempOwner;
-		CCastleEvent ev = town->events.front();
-		const PlayerState * pinfo = getPlayerState(player, false);
+		if (!event.occursToday(gs->day))
+			continue;
 
-		if (pinfo  //player exists
-			&& (ev.players & 1<<player.getNum()) //event is enabled to this player
-			&& ((ev.computerAffected && !pinfo->human)
-				|| (ev.humanAffected && pinfo->human)))
-		{
-			// dialog
-			InfoWindow iw;
-			iw.player = player;
-			iw.text = ev.message;
+		PlayerColor player = town->getOwner();
+		if (!event.affectsPlayer(player, getPlayerState(player)->isHuman()))
+			continue;
 
-			if (ev.resources.nonZero())
-			{
-				TResources was = n.res[player];
-				n.res[player] += ev.resources;
-				n.res[player].amax(0);
+		// dialog
+		InfoWindow iw;
+		iw.player = player;
+		iw.text = event.message;
 
-				for (GameResID i : GameResID::ALL_RESOURCES())
-					if (ev.resources[i] && pinfo->resources[i] != n.res.at(player)[i]) //if resource had changed, we add it to the dialog
-						iw.components.emplace_back(ComponentType::RESOURCE, i, n.res.at(player)[i] - was[i]);
-			}
+		if (event.resources.nonZero())
+		{
+			giveResources(player, event.resources);
 
-			for (auto & i : ev.buildings)
-			{
-				// Only perform action if:
-				// 1. Building exists in town (don't attempt to build Lvl 5 guild in Fortress
-				// 2. Building was not built yet
-				// othervice, silently ignore / skip it
-				if (town->town->buildings.count(i) && !town->hasBuilt(i))
-				{
-					buildStructure(town->id, i, true);
-					iw.components.emplace_back(ComponentType::BUILDING, BuildingTypeUniqueID(town->getFaction(), i));
-				}
-			}
+			for (GameResID i : GameResID::ALL_RESOURCES())
+				if (event.resources[i])
+					iw.components.emplace_back(ComponentType::RESOURCE, i, event.resources[i]);
+		}
 
-			if (!ev.creatures.empty() && !vstd::contains(n.cres, town->id))
+		for (auto & i : event.buildings)
+		{
+			// Only perform action if:
+			// 1. Building exists in town (don't attempt to build Lvl 5 guild in Fortress
+			// 2. Building was not built yet
+			// othervice, silently ignore / skip it
+			if (town->town->buildings.count(i) && !town->hasBuilt(i))
 			{
-				n.cres[town->id].tid = town->id;
-				n.cres[town->id].creatures = town->creatures;
+				buildStructure(town->id, i, true);
+				iw.components.emplace_back(ComponentType::BUILDING, BuildingTypeUniqueID(town->getFaction(), i));
 			}
-			auto & sac = n.cres[town->id];
+		}
+
+		if (!event.creatures.empty())
+		{
+			SetAvailableCreatures sac;
+			sac.tid = town->id;
+			sac.creatures = town->creatures;
 
-			for (si32 i=0;i<ev.creatures.size();i++) //creature growths
+			for (si32 i=0;i<event.creatures.size();i++) //creature growths
 			{
-				if (!town->creatures.at(i).second.empty() && ev.creatures.at(i) > 0)//there is dwelling
+				if (!town->creatures.at(i).second.empty() && event.creatures.at(i) > 0)//there is dwelling
 				{
-					sac.creatures[i].first += ev.creatures.at(i);
-					iw.components.emplace_back(ComponentType::CREATURE, town->creatures.at(i).second.back(), ev.creatures.at(i));
+					sac.creatures[i].first += event.creatures.at(i);
+					iw.components.emplace_back(ComponentType::CREATURE, town->creatures.at(i).second.back(), event.creatures.at(i));
 				}
 			}
-			sendAndApply(&iw); //show dialog
-		}
-
-		if (ev.nextOccurrence)
-		{
-			town->events.erase(town->events.begin());
-
-			ev.firstOccurrence += ev.nextOccurrence;
-			auto it = town->events.begin();
-			while(it != town->events.end() && it->earlierThanOrEqual(ev))
-				it++;
-			town->events.insert(it, ev);
-		}
-		else
-		{
-			town->events.erase(town->events.begin());
 		}
+		sendAndApply(&iw); //show dialog
 	}
-
-	//TODO send only if changed
-	UpdateCastleEvents uce;
-	uce.town = town->id;
-	uce.events = town->events;
-	sendAndApply(&uce);
 }
 
 bool CGameHandler::complain(const std::string &problem)

+ 2 - 2
server/CGameHandler.h

@@ -229,8 +229,8 @@ public:
 	void onNewTurn();
 	void addStatistics();
 
-	void handleTimeEvents();
-	void handleTownEvents(CGTownInstance *town, NewTurn &n);
+	void handleTimeEvents(PlayerColor player);
+	void handleTownEvents(CGTownInstance *town);
 	bool complain(const std::string &problem); //sends message to all clients, prints on the logs and return true
 	void objectVisited( const CGObjectInstance * obj, const CGHeroInstance * h );
 	void objectVisitEnded(const CObjectVisitQuery &query);