1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-02 09:02:03 +02:00

Map and town events are now processed on start of player turn instead of

on start of new day, in line with H3
This commit is contained in:
Ivan Savenko 2024-08-12 10:38:18 +00:00
parent 5523e08cb7
commit 26fdaacbe5
4 changed files with 89 additions and 134 deletions

View File

@ -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) void CMapEvent::serializeJson(JsonSerializeFormat & handler)

View File

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

View File

@ -584,11 +584,6 @@ void CGameHandler::init(StartInfo *si, Load::ProgressAccumulator & progressTrack
reinitScripting(); 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) 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 {// 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); events::PlayerGotTurn::defaultExecute(serverEventBus.get(), which);
turnTimerHandler->onPlayerGetTurn(which); turnTimerHandler->onPlayerGetTurn(which);
handleTimeEvents(which);
for (auto t : getPlayerState(which)->towns)
handleTownEvents(t);
} }
void CGameHandler::onPlayerTurnEnded(PlayerColor which) void CGameHandler::onPlayerTurnEnded(PlayerColor which)
@ -860,7 +860,6 @@ void CGameHandler::onNewTurn()
for (CGTownInstance *t : gs->map->towns) for (CGTownInstance *t : gs->map->towns)
{ {
PlayerColor player = t->tempOwner; PlayerColor player = t->tempOwner;
handleTownEvents(t, n);
if (newWeek) //first day of week if (newWeek) //first day of week
{ {
if (t->hasBuilt(BuildingSubID::PORTAL_OF_SUMMONING)) if (t->hasBuilt(BuildingSubID::PORTAL_OF_SUMMONING))
@ -1017,7 +1016,6 @@ void CGameHandler::onNewTurn()
checkVictoryLossConditionsForAll(); // check for map turn limit checkVictoryLossConditionsForAll(); // check for map turn limit
logGlobal->trace("Info about turn %d has been sent!", n.day); logGlobal->trace("Info about turn %d has been sent!", n.day);
handleTimeEvents();
//call objects //call objects
for (auto & elem : gs->map->objects) for (auto & elem : gs->map->objects)
{ {
@ -3406,97 +3404,58 @@ bool CGameHandler::queryReply(QueryID qid, std::optional<int32_t> answer, Player
return true; return true;
} }
void CGameHandler::handleTimeEvents() void CGameHandler::handleTimeEvents(PlayerColor color)
{ {
gs->map->events.sort(evntCmp); for (auto const & event : gs->map->events)
while(gs->map->events.size() && gs->map->events.front().firstOccurrence+1 == gs->day)
{ {
CMapEvent ev = gs->map->events.front(); if (!event.occursToday(gs->day))
continue;
for (int player = 0; player < PlayerColor::PLAYER_LIMIT_I; player++) if (!event.affectsPlayer(color, getPlayerState(color)->isHuman()))
{ continue;
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; InfoWindow iw;
iw.player = color; iw.player = color;
iw.text = ev.message; iw.text = event.message;
for (GameResID i : GameResID::ALL_RESOURCES()) //give resources
if (!event.resources.empty())
{ {
if (ev.resources[i]) //if resource is changed, we add it to the dialog giveResources(color, event.resources);
iw.components.emplace_back(ComponentType::RESOURCE, i, ev.resources[i]); for (GameResID i : GameResID::ALL_RESOURCES())
if (event.resources[i])
iw.components.emplace_back(ComponentType::RESOURCE, i, event.resources[i]);
} }
sendAndApply(&iw); //show dialog sendAndApply(&iw); //show dialog
} }
} //PLAYERS LOOP
if (ev.nextOccurrence)
{
gs->map->events.pop_front();
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
{
gs->map->events.pop_front();
}
}
//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); for (auto const & event : town->events)
while(town->events.size() && town->events.front().firstOccurrence == gs->day)
{ {
PlayerColor player = town->tempOwner; if (!event.occursToday(gs->day))
CCastleEvent ev = town->events.front(); continue;
const PlayerState * pinfo = getPlayerState(player, false);
PlayerColor player = town->getOwner();
if (!event.affectsPlayer(player, getPlayerState(player)->isHuman()))
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 // dialog
InfoWindow iw; InfoWindow iw;
iw.player = player; iw.player = player;
iw.text = ev.message; iw.text = event.message;
if (ev.resources.nonZero()) if (event.resources.nonZero())
{ {
TResources was = n.res[player]; giveResources(player, event.resources);
n.res[player] += ev.resources;
n.res[player].amax(0);
for (GameResID i : GameResID::ALL_RESOURCES()) 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 if (event.resources[i])
iw.components.emplace_back(ComponentType::RESOURCE, i, n.res.at(player)[i] - was[i]); iw.components.emplace_back(ComponentType::RESOURCE, i, event.resources[i]);
} }
for (auto & i : ev.buildings) for (auto & i : event.buildings)
{ {
// Only perform action if: // Only perform action if:
// 1. Building exists in town (don't attempt to build Lvl 5 guild in Fortress // 1. Building exists in town (don't attempt to build Lvl 5 guild in Fortress
@ -3509,45 +3468,23 @@ void CGameHandler::handleTownEvents(CGTownInstance * town, NewTurn &n)
} }
} }
if (!ev.creatures.empty() && !vstd::contains(n.cres, town->id)) if (!event.creatures.empty())
{ {
n.cres[town->id].tid = town->id; SetAvailableCreatures sac;
n.cres[town->id].creatures = town->creatures; sac.tid = town->id;
} sac.creatures = town->creatures;
auto & sac = n.cres[town->id];
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); sac.creatures[i].first += event.creatures.at(i);
iw.components.emplace_back(ComponentType::CREATURE, town->creatures.at(i).second.back(), ev.creatures.at(i)); iw.components.emplace_back(ComponentType::CREATURE, town->creatures.at(i).second.back(), event.creatures.at(i));
}
} }
} }
sendAndApply(&iw); //show dialog 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());
}
}
//TODO send only if changed
UpdateCastleEvents uce;
uce.town = town->id;
uce.events = town->events;
sendAndApply(&uce);
} }
bool CGameHandler::complain(const std::string &problem) bool CGameHandler::complain(const std::string &problem)

View File

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