mirror of
https://github.com/vcmi/vcmi.git
synced 2025-05-13 22:06:58 +02:00
Client: implement spectator mode via command-line options
If running with --spectate/-s CPlayerInterface will appear even without human players. Following command-line options also available: --spectate-ignore-hero --spectate-hero-speed=N --spectate-battle-speed=N --spectate-skip-battle --spectate-skip-battle-result Boolean options can also be changed in runtime via client console: set spectate-ignore-hero on / off Spectator mode also: - Work with --onlyAI option when starting game or loading saves. - Allow to use any cheat codes. - Give recon on towns and heroes.
This commit is contained in:
parent
d95c74941b
commit
18161d3688
@ -1,3 +1,8 @@
|
|||||||
|
0.99 -> 1.00
|
||||||
|
|
||||||
|
GENERAL:
|
||||||
|
* Spectator mode was implemented through command-line options
|
||||||
|
|
||||||
0.98 -> 0.99
|
0.98 -> 0.99
|
||||||
|
|
||||||
GENERAL:
|
GENERAL:
|
||||||
|
@ -123,7 +123,7 @@ void startTestMap(const std::string &mapname)
|
|||||||
PlayerSettings &pset = si.playerInfos[PlayerColor(i)];
|
PlayerSettings &pset = si.playerInfos[PlayerColor(i)];
|
||||||
pset.color = PlayerColor(i);
|
pset.color = PlayerColor(i);
|
||||||
pset.name = CGI->generaltexth->allTexts[468];//Computer
|
pset.name = CGI->generaltexth->allTexts[468];//Computer
|
||||||
pset.playerID = i;
|
pset.playerID = PlayerSettings::PLAYER_AI;
|
||||||
pset.compOnly = true;
|
pset.compOnly = true;
|
||||||
pset.castle = 0;
|
pset.castle = 0;
|
||||||
pset.hero = -1;
|
pset.hero = -1;
|
||||||
@ -263,6 +263,12 @@ int main(int argc, char** argv)
|
|||||||
("battle,b", po::value<std::string>(), "runs game in duel mode (battle-only")
|
("battle,b", po::value<std::string>(), "runs game in duel mode (battle-only")
|
||||||
("start", po::value<bfs::path>(), "starts game from saved StartInfo file")
|
("start", po::value<bfs::path>(), "starts game from saved StartInfo file")
|
||||||
("testmap", po::value<std::string>(), "")
|
("testmap", po::value<std::string>(), "")
|
||||||
|
("spectate,s", "enable spectator interface for AI-only games")
|
||||||
|
("spectate-ignore-hero", "wont follow heroes on adventure map")
|
||||||
|
("spectate-hero-speed", po::value<int>(), "hero movement speed on adventure map")
|
||||||
|
("spectate-battle-speed", po::value<int>(), "battle animation speed for spectator")
|
||||||
|
("spectate-skip-battle", "skip battles in spectator view")
|
||||||
|
("spectate-skip-battle-result", "skip battle result window")
|
||||||
("onlyAI", "runs without human player, all players will be default AI")
|
("onlyAI", "runs without human player, all players will be default AI")
|
||||||
("headless", "runs without GUI, implies --onlyAI")
|
("headless", "runs without GUI, implies --onlyAI")
|
||||||
("ai", po::value<std::vector<std::string>>(), "AI to be used for the player, can be specified several times for the consecutive players")
|
("ai", po::value<std::vector<std::string>>(), "AI to be used for the player, can be specified several times for the consecutive players")
|
||||||
@ -515,18 +521,34 @@ int main(int argc, char** argv)
|
|||||||
if(vm.count("testmap"))
|
if(vm.count("testmap"))
|
||||||
testmap = vm["testmap"].as<std::string>();
|
testmap = vm["testmap"].as<std::string>();
|
||||||
|
|
||||||
|
session["spectate"].Bool() = vm.count("spectate");
|
||||||
|
if(session["spectate"].Bool())
|
||||||
|
{
|
||||||
|
session["spectate-ignore-hero"].Bool() = vm.count("spectate-ignore-hero");
|
||||||
|
session["spectate-skip-battle"].Bool() = vm.count("spectate-skip-battle");
|
||||||
|
session["spectate-skip-battle-result"].Bool() = vm.count("spectate-skip-battle-result");
|
||||||
|
if(vm.count("spectate-hero-speed"))
|
||||||
|
session["spectate-hero-speed"].Integer() = vm["spectate-hero-speed"].as<int>();
|
||||||
|
if(vm.count("spectate-battle-speed"))
|
||||||
|
session["spectate-battle-speed"].Float() = vm["spectate-battle-speed"].as<int>();
|
||||||
|
}
|
||||||
if(!testmap.empty())
|
if(!testmap.empty())
|
||||||
|
{
|
||||||
startTestMap(testmap);
|
startTestMap(testmap);
|
||||||
else if(!fileToStartFrom.empty() && bfs::exists(fileToStartFrom))
|
}
|
||||||
startGameFromFile(fileToStartFrom); //ommit pregame and start the game using settings from file
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if(!fileToStartFrom.empty())
|
if(!fileToStartFrom.empty() && bfs::exists(fileToStartFrom))
|
||||||
|
startGameFromFile(fileToStartFrom); //ommit pregame and start the game using settings from file
|
||||||
|
else
|
||||||
{
|
{
|
||||||
logGlobal->warnStream() << "Warning: cannot find given file to start from (" << fileToStartFrom
|
if(!fileToStartFrom.empty())
|
||||||
<< "). Falling back to main menu.";
|
{
|
||||||
|
logGlobal->warnStream() << "Warning: cannot find given file to start from (" << fileToStartFrom
|
||||||
|
<< "). Falling back to main menu.";
|
||||||
|
}
|
||||||
|
GH.curInt = CGPreGame::create(); //will set CGP pointer to itself
|
||||||
}
|
}
|
||||||
GH.curInt = CGPreGame::create(); //will set CGP pointer to itself
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -296,6 +296,8 @@ void CMessage::drawIWindow(CInfoWindow * ret, std::string text, PlayerColor play
|
|||||||
|
|
||||||
void CMessage::drawBorder(PlayerColor playerColor, SDL_Surface * ret, int w, int h, int x, int y)
|
void CMessage::drawBorder(PlayerColor playerColor, SDL_Surface * ret, int w, int h, int x, int y)
|
||||||
{
|
{
|
||||||
|
if(playerColor.isSpectator())
|
||||||
|
playerColor = PlayerColor(1);
|
||||||
std::vector<const IImage*> &box = piecesOfBox.at(playerColor.getNum());
|
std::vector<const IImage*> &box = piecesOfBox.at(playerColor.getNum());
|
||||||
|
|
||||||
// Note: this code assumes that the corner dimensions are all the same.
|
// Note: this code assumes that the corner dimensions are all the same.
|
||||||
|
@ -245,6 +245,9 @@ void CPlayerInterface::heroMoved(const TryMoveHero & details)
|
|||||||
if (LOCPLINT != this)
|
if (LOCPLINT != this)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if(settings["session"]["spectate"].Bool() && settings["session"]["spectate-ignore-hero"].Bool())
|
||||||
|
return;
|
||||||
|
|
||||||
const CGHeroInstance * hero = cb->getHero(details.id); //object representing this hero
|
const CGHeroInstance * hero = cb->getHero(details.id); //object representing this hero
|
||||||
int3 hp = details.start;
|
int3 hp = details.start;
|
||||||
|
|
||||||
@ -321,7 +324,12 @@ void CPlayerInterface::heroMoved(const TryMoveHero & details)
|
|||||||
}
|
}
|
||||||
|
|
||||||
ui32 speed;
|
ui32 speed;
|
||||||
if (makingTurn) // our turn, our hero moves
|
if(settings["session"]["spectate"].Bool())
|
||||||
|
{
|
||||||
|
if(!settings["session"]["spectate-hero-speed"].isNull())
|
||||||
|
speed = settings["session"]["spectate-hero-speed"].Integer();
|
||||||
|
}
|
||||||
|
else if (makingTurn) // our turn, our hero moves
|
||||||
speed = settings["adventure"]["heroSpeed"].Float();
|
speed = settings["adventure"]["heroSpeed"].Float();
|
||||||
else
|
else
|
||||||
speed = settings["adventure"]["enemySpeed"].Float();
|
speed = settings["adventure"]["enemySpeed"].Float();
|
||||||
@ -334,7 +342,6 @@ void CPlayerInterface::heroMoved(const TryMoveHero & details)
|
|||||||
return; // no animation
|
return; // no animation
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
adventureInt->centerOn(hero); //actualizing screen pos
|
adventureInt->centerOn(hero); //actualizing screen pos
|
||||||
adventureInt->minimap.redraw();
|
adventureInt->minimap.redraw();
|
||||||
adventureInt->heroList.redraw();
|
adventureInt->heroList.redraw();
|
||||||
@ -471,6 +478,14 @@ int3 CPlayerInterface::repairScreenPos(int3 pos)
|
|||||||
pos.y = CGI->mh->sizes.y - adventureInt->terrain.tilesh + CGI->mh->frameH;
|
pos.y = CGI->mh->sizes.y - adventureInt->terrain.tilesh + CGI->mh->frameH;
|
||||||
return pos;
|
return pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CPlayerInterface::activateForSpectator()
|
||||||
|
{
|
||||||
|
adventureInt->state = CAdvMapInt::INGAME;
|
||||||
|
adventureInt->activate();
|
||||||
|
adventureInt->minimap.activate();
|
||||||
|
}
|
||||||
|
|
||||||
void CPlayerInterface::heroPrimarySkillChanged(const CGHeroInstance * hero, int which, si64 val)
|
void CPlayerInterface::heroPrimarySkillChanged(const CGHeroInstance * hero, int which, si64 val)
|
||||||
{
|
{
|
||||||
EVENT_HANDLER_CALLED_BY_CLIENT;
|
EVENT_HANDLER_CALLED_BY_CLIENT;
|
||||||
@ -1637,7 +1652,8 @@ void CPlayerInterface::update()
|
|||||||
}
|
}
|
||||||
|
|
||||||
//in some conditions we may receive calls before selection is initialized - we must ignore them
|
//in some conditions we may receive calls before selection is initialized - we must ignore them
|
||||||
if (adventureInt && !adventureInt->selection && GH.topInt() == adventureInt)
|
if(adventureInt && GH.topInt() == adventureInt
|
||||||
|
&& (!adventureInt->selection && !settings["session"]["spectate"].Bool()))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -2131,7 +2147,7 @@ void CPlayerInterface::gameOver(PlayerColor player, const EVictoryLossCheckResul
|
|||||||
|
|
||||||
--howManyPeople;
|
--howManyPeople;
|
||||||
|
|
||||||
if (howManyPeople == 0) //all human players eliminated
|
if(howManyPeople == 0 && !settings["session"]["spectate"].Bool()) //all human players eliminated
|
||||||
{
|
{
|
||||||
if (adventureInt)
|
if (adventureInt)
|
||||||
{
|
{
|
||||||
@ -2152,7 +2168,7 @@ void CPlayerInterface::gameOver(PlayerColor player, const EVictoryLossCheckResul
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (howManyPeople == 0) //all human players eliminated
|
if(howManyPeople == 0 && !settings["session"]["spectate"].Bool()) //all human players eliminated
|
||||||
{
|
{
|
||||||
requestReturningToMainMenu();
|
requestReturningToMainMenu();
|
||||||
}
|
}
|
||||||
|
@ -236,6 +236,7 @@ public:
|
|||||||
void updateInfo(const CGObjectInstance * specific);
|
void updateInfo(const CGObjectInstance * specific);
|
||||||
void init(std::shared_ptr<CCallback> CB) override;
|
void init(std::shared_ptr<CCallback> CB) override;
|
||||||
int3 repairScreenPos(int3 pos); //returns position closest to pos we can center screen on
|
int3 repairScreenPos(int3 pos); //returns position closest to pos we can center screen on
|
||||||
|
void activateForSpectator(); // TODO: spectator probably need own player interface class
|
||||||
|
|
||||||
// show dialogs
|
// show dialogs
|
||||||
void showInfoDialog(const std::string &text, CComponent * component);
|
void showInfoDialog(const std::string &text, CComponent * component);
|
||||||
|
@ -487,6 +487,10 @@ void CClient::newGame( CConnection *con, StartInfo *si )
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
if(settings["session"]["spectate"].Bool())
|
||||||
|
{
|
||||||
|
installNewPlayerInterface(std::make_shared<CPlayerInterface>(PlayerColor::SPECTATOR), PlayerColor::SPECTATOR, true);
|
||||||
|
}
|
||||||
loadNeutralBattleAI();
|
loadNeutralBattleAI();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -638,9 +642,6 @@ void CClient::serialize(BinaryDeserializer & h, const int version, const std::se
|
|||||||
nInt->human = isHuman;
|
nInt->human = isHuman;
|
||||||
nInt->playerID = pid;
|
nInt->playerID = pid;
|
||||||
|
|
||||||
if(playerIDs.count(pid))
|
|
||||||
installNewPlayerInterface(nInt, pid);
|
|
||||||
|
|
||||||
nInt->loadGame(h, version);
|
nInt->loadGame(h, version);
|
||||||
if(settings["session"]["onlyai"].Bool() && isHuman)
|
if(settings["session"]["onlyai"].Bool() && isHuman)
|
||||||
{
|
{
|
||||||
@ -654,6 +655,20 @@ void CClient::serialize(BinaryDeserializer & h, const int version, const std::se
|
|||||||
installNewPlayerInterface(nInt, pid);
|
installNewPlayerInterface(nInt, pid);
|
||||||
GH.totalRedraw();
|
GH.totalRedraw();
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(playerIDs.count(pid))
|
||||||
|
installNewPlayerInterface(nInt, pid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(settings["session"]["spectate"].Bool())
|
||||||
|
{
|
||||||
|
removeGUI();
|
||||||
|
auto p = std::make_shared<CPlayerInterface>(PlayerColor::SPECTATOR);
|
||||||
|
installNewPlayerInterface(p, PlayerColor::SPECTATOR, true);
|
||||||
|
GH.curInt = p.get();
|
||||||
|
LOCPLINT->activateForSpectator();
|
||||||
|
GH.totalRedraw();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(playerIDs.count(PlayerColor::NEUTRAL))
|
if(playerIDs.count(PlayerColor::NEUTRAL))
|
||||||
@ -759,15 +774,29 @@ void CClient::battleStarted(const BattleInfo * info)
|
|||||||
def = std::dynamic_pointer_cast<CPlayerInterface>( playerint[rightSide.color] );
|
def = std::dynamic_pointer_cast<CPlayerInterface>( playerint[rightSide.color] );
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!settings["session"]["headless"].Bool()
|
if(!settings["session"]["headless"].Bool())
|
||||||
&& (!!att || !!def || gs->scenarioOps->mode == StartInfo::DUEL))
|
|
||||||
{
|
{
|
||||||
boost::unique_lock<boost::recursive_mutex> un(*CPlayerInterface::pim);
|
if(!!att || !!def || gs->scenarioOps->mode == StartInfo::DUEL)
|
||||||
auto bi = new CBattleInterface(leftSide.armyObject, rightSide.armyObject, leftSide.hero, rightSide.hero,
|
{
|
||||||
Rect((screen->w - 800)/2,
|
boost::unique_lock<boost::recursive_mutex> un(*CPlayerInterface::pim);
|
||||||
(screen->h - 600)/2, 800, 600), att, def);
|
auto bi = new CBattleInterface(leftSide.armyObject, rightSide.armyObject, leftSide.hero, rightSide.hero,
|
||||||
|
Rect((screen->w - 800)/2,
|
||||||
|
(screen->h - 600)/2, 800, 600), att, def);
|
||||||
|
|
||||||
GH.pushInt(bi);
|
GH.pushInt(bi);
|
||||||
|
}
|
||||||
|
else if(settings["session"]["spectate"].Bool() && !settings["session"]["spectate-skip-battle"].Bool())
|
||||||
|
{
|
||||||
|
//TODO: This certainly need improvement
|
||||||
|
auto spectratorInt = std::dynamic_pointer_cast<CPlayerInterface>(playerint[PlayerColor::SPECTATOR]);
|
||||||
|
spectratorInt->cb->setBattle(info);
|
||||||
|
boost::unique_lock<boost::recursive_mutex> un(*CPlayerInterface::pim);
|
||||||
|
auto bi = new CBattleInterface(leftSide.armyObject, rightSide.armyObject, leftSide.hero, rightSide.hero,
|
||||||
|
Rect((screen->w - 800)/2,
|
||||||
|
(screen->h - 600)/2, 800, 600), att, def, spectratorInt);
|
||||||
|
|
||||||
|
GH.pushInt(bi);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto callBattleStart = [&](PlayerColor color, ui8 side){
|
auto callBattleStart = [&](PlayerColor color, ui8 side){
|
||||||
@ -778,6 +807,8 @@ void CClient::battleStarted(const BattleInfo * info)
|
|||||||
callBattleStart(leftSide.color, 0);
|
callBattleStart(leftSide.color, 0);
|
||||||
callBattleStart(rightSide.color, 1);
|
callBattleStart(rightSide.color, 1);
|
||||||
callBattleStart(PlayerColor::UNFLAGGABLE, 1);
|
callBattleStart(PlayerColor::UNFLAGGABLE, 1);
|
||||||
|
if(settings["session"]["spectate"].Bool() && !settings["session"]["spectate-skip-battle"].Bool())
|
||||||
|
callBattleStart(PlayerColor::SPECTATOR, 1);
|
||||||
|
|
||||||
if(info->tacticDistance && vstd::contains(battleints,info->sides[info->tacticsSide].color))
|
if(info->tacticDistance && vstd::contains(battleints,info->sides[info->tacticsSide].color))
|
||||||
{
|
{
|
||||||
@ -790,6 +821,9 @@ void CClient::battleFinished()
|
|||||||
for(auto & side : gs->curB->sides)
|
for(auto & side : gs->curB->sides)
|
||||||
if(battleCallbacks.count(side.color))
|
if(battleCallbacks.count(side.color))
|
||||||
battleCallbacks[side.color]->setBattle(nullptr);
|
battleCallbacks[side.color]->setBattle(nullptr);
|
||||||
|
|
||||||
|
if(settings["session"]["spectate"].Bool() && !settings["session"]["spectate-skip-battle"].Bool())
|
||||||
|
battleCallbacks[PlayerColor::SPECTATOR]->setBattle(nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CClient::loadNeutralBattleAI()
|
void CClient::loadNeutralBattleAI()
|
||||||
@ -887,7 +921,7 @@ void CClient::campaignMapFinished( std::shared_ptr<CCampaignState> camp )
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CClient::installNewPlayerInterface(std::shared_ptr<CGameInterface> gameInterface, boost::optional<PlayerColor> color)
|
void CClient::installNewPlayerInterface(std::shared_ptr<CGameInterface> gameInterface, boost::optional<PlayerColor> color, bool battlecb)
|
||||||
{
|
{
|
||||||
boost::unique_lock<boost::recursive_mutex> un(*CPlayerInterface::pim);
|
boost::unique_lock<boost::recursive_mutex> un(*CPlayerInterface::pim);
|
||||||
PlayerColor colorUsed = color.get_value_or(PlayerColor::UNFLAGGABLE);
|
PlayerColor colorUsed = color.get_value_or(PlayerColor::UNFLAGGABLE);
|
||||||
@ -903,7 +937,7 @@ void CClient::installNewPlayerInterface(std::shared_ptr<CGameInterface> gameInte
|
|||||||
battleCallbacks[colorUsed] = cb;
|
battleCallbacks[colorUsed] = cb;
|
||||||
gameInterface->init(cb);
|
gameInterface->init(cb);
|
||||||
|
|
||||||
installNewBattleInterface(gameInterface, color, false);
|
installNewBattleInterface(gameInterface, color, battlecb);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CClient::installNewBattleInterface(std::shared_ptr<CBattleGameInterface> battleInterface, boost::optional<PlayerColor> color, bool needCallback /*= true*/)
|
void CClient::installNewBattleInterface(std::shared_ptr<CBattleGameInterface> battleInterface, boost::optional<PlayerColor> color, bool needCallback /*= true*/)
|
||||||
|
@ -148,7 +148,7 @@ public:
|
|||||||
void newGame(CConnection *con, StartInfo *si); //con - connection to server
|
void newGame(CConnection *con, StartInfo *si); //con - connection to server
|
||||||
|
|
||||||
void loadNeutralBattleAI();
|
void loadNeutralBattleAI();
|
||||||
void installNewPlayerInterface(std::shared_ptr<CGameInterface> gameInterface, boost::optional<PlayerColor> color);
|
void installNewPlayerInterface(std::shared_ptr<CGameInterface> gameInterface, boost::optional<PlayerColor> color, bool battlecb = false);
|
||||||
void installNewBattleInterface(std::shared_ptr<CBattleGameInterface> battleInterface, boost::optional<PlayerColor> color, bool needCallback = true);
|
void installNewBattleInterface(std::shared_ptr<CBattleGameInterface> battleInterface, boost::optional<PlayerColor> color, bool needCallback = true);
|
||||||
std::string aiNameForPlayer(const PlayerSettings &ps, bool battleAI); //empty means no AI -> human
|
std::string aiNameForPlayer(const PlayerSettings &ps, bool battleAI); //empty means no AI -> human
|
||||||
std::string aiNameForPlayer(bool battleAI);
|
std::string aiNameForPlayer(bool battleAI);
|
||||||
|
@ -96,6 +96,10 @@
|
|||||||
#define BATTLE_INTERFACE_CALL_IF_PRESENT_FOR_BOTH_SIDES(function,...) \
|
#define BATTLE_INTERFACE_CALL_IF_PRESENT_FOR_BOTH_SIDES(function,...) \
|
||||||
CALL_ONLY_THAT_BATTLE_INTERFACE(GS(cl)->curB->sides[0].color, function, __VA_ARGS__) \
|
CALL_ONLY_THAT_BATTLE_INTERFACE(GS(cl)->curB->sides[0].color, function, __VA_ARGS__) \
|
||||||
CALL_ONLY_THAT_BATTLE_INTERFACE(GS(cl)->curB->sides[1].color, function, __VA_ARGS__) \
|
CALL_ONLY_THAT_BATTLE_INTERFACE(GS(cl)->curB->sides[1].color, function, __VA_ARGS__) \
|
||||||
|
if(settings["session"]["spectate"].Bool() && !settings["session"]["spectate-skip-battle"].Bool()) \
|
||||||
|
{ \
|
||||||
|
CALL_ONLY_THAT_BATTLE_INTERFACE(PlayerColor::SPECTATOR, function, __VA_ARGS__) \
|
||||||
|
} \
|
||||||
BATTLE_INTERFACE_CALL_RECEIVERS(function, __VA_ARGS__)
|
BATTLE_INTERFACE_CALL_RECEIVERS(function, __VA_ARGS__)
|
||||||
/*
|
/*
|
||||||
* NetPacksClient.cpp, part of VCMI engine
|
* NetPacksClient.cpp, part of VCMI engine
|
||||||
@ -358,12 +362,12 @@ void TryMoveHero::applyFirstCl(CClient *cl)
|
|||||||
//check if playerint will have the knowledge about movement - if not, directly update maphandler
|
//check if playerint will have the knowledge about movement - if not, directly update maphandler
|
||||||
for(auto i=cl->playerint.begin(); i!=cl->playerint.end(); i++)
|
for(auto i=cl->playerint.begin(); i!=cl->playerint.end(); i++)
|
||||||
{
|
{
|
||||||
if(i->first >= PlayerColor::PLAYER_LIMIT)
|
auto ps = GS(cl)->getPlayer(i->first);
|
||||||
continue;
|
if(ps && (GS(cl)->isVisible(start - int3(1, 0, 0), i->first) || GS(cl)->isVisible(end - int3(1, 0, 0), i->first)))
|
||||||
TeamState *t = GS(cl)->getPlayerTeam(i->first);
|
{
|
||||||
if((t->fogOfWarMap[start.x-1][start.y][start.z] || t->fogOfWarMap[end.x-1][end.y][end.z])
|
if(ps->human)
|
||||||
&& GS(cl)->getPlayer(i->first)->human)
|
humanKnows = true;
|
||||||
humanKnows = true;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!CGI->mh)
|
if(!CGI->mh)
|
||||||
@ -399,9 +403,8 @@ void TryMoveHero::applyCl(CClient *cl)
|
|||||||
//notify interfaces about move
|
//notify interfaces about move
|
||||||
for(auto i=cl->playerint.begin(); i!=cl->playerint.end(); i++)
|
for(auto i=cl->playerint.begin(); i!=cl->playerint.end(); i++)
|
||||||
{
|
{
|
||||||
if(i->first >= PlayerColor::PLAYER_LIMIT) continue;
|
if(GS(cl)->isVisible(start - int3(1, 0, 0), i->first)
|
||||||
TeamState *t = GS(cl)->getPlayerTeam(i->first);
|
|| GS(cl)->isVisible(end - int3(1, 0, 0), i->first))
|
||||||
if(t->fogOfWarMap[start.x-1][start.y][start.z] || t->fogOfWarMap[end.x-1][end.y][end.z])
|
|
||||||
{
|
{
|
||||||
i->second->heroMoved(*this);
|
i->second->heroMoved(*this);
|
||||||
}
|
}
|
||||||
@ -592,6 +595,8 @@ void BattleStart::applyFirstCl(CClient *cl)
|
|||||||
info->tile, info->sides[0].hero, info->sides[1].hero);
|
info->tile, info->sides[0].hero, info->sides[1].hero);
|
||||||
CALL_ONLY_THAT_BATTLE_INTERFACE(info->sides[1].color, battleStartBefore, info->sides[0].armyObject, info->sides[1].armyObject,
|
CALL_ONLY_THAT_BATTLE_INTERFACE(info->sides[1].color, battleStartBefore, info->sides[0].armyObject, info->sides[1].armyObject,
|
||||||
info->tile, info->sides[0].hero, info->sides[1].hero);
|
info->tile, info->sides[0].hero, info->sides[1].hero);
|
||||||
|
CALL_ONLY_THAT_BATTLE_INTERFACE(PlayerColor::SPECTATOR, battleStartBefore, info->sides[0].armyObject, info->sides[1].armyObject,
|
||||||
|
info->tile, info->sides[0].hero, info->sides[1].hero);
|
||||||
BATTLE_INTERFACE_CALL_RECEIVERS(battleStartBefore, info->sides[0].armyObject, info->sides[1].armyObject,
|
BATTLE_INTERFACE_CALL_RECEIVERS(battleStartBefore, info->sides[0].armyObject, info->sides[1].armyObject,
|
||||||
info->tile, info->sides[0].hero, info->sides[1].hero);
|
info->tile, info->sides[0].hero, info->sides[1].hero);
|
||||||
}
|
}
|
||||||
@ -711,7 +716,7 @@ void BattleResultsApplied::applyCl(CClient *cl)
|
|||||||
{
|
{
|
||||||
INTERFACE_CALL_IF_PRESENT(player1, battleResultsApplied);
|
INTERFACE_CALL_IF_PRESENT(player1, battleResultsApplied);
|
||||||
INTERFACE_CALL_IF_PRESENT(player2, battleResultsApplied);
|
INTERFACE_CALL_IF_PRESENT(player2, battleResultsApplied);
|
||||||
INTERFACE_CALL_IF_PRESENT(PlayerColor::UNFLAGGABLE, battleResultsApplied);
|
INTERFACE_CALL_IF_PRESENT(PlayerColor::SPECTATOR, battleResultsApplied);
|
||||||
if(GS(cl)->initialOpts->mode == StartInfo::DUEL)
|
if(GS(cl)->initialOpts->mode == StartInfo::DUEL)
|
||||||
{
|
{
|
||||||
handleQuit();
|
handleQuit();
|
||||||
@ -812,7 +817,10 @@ void PlayerMessage::applyCl(CClient *cl)
|
|||||||
logNetwork->debugStream() << "Player "<< player <<" sends a message: " << text;
|
logNetwork->debugStream() << "Player "<< player <<" sends a message: " << text;
|
||||||
|
|
||||||
std::ostringstream str;
|
std::ostringstream str;
|
||||||
str << cl->getPlayer(player)->nodeName() <<": " << text;
|
if(player.isSpectator())
|
||||||
|
str << "Spectator: " << text;
|
||||||
|
else
|
||||||
|
str << cl->getPlayer(player)->nodeName() <<": " << text;
|
||||||
if(LOCPLINT)
|
if(LOCPLINT)
|
||||||
LOCPLINT->cingconsole->print(str.str());
|
LOCPLINT->cingconsole->print(str.str());
|
||||||
}
|
}
|
||||||
|
@ -95,7 +95,7 @@ void CBattleInterface::addNewAnim(CBattleAnimation *anim)
|
|||||||
CBattleInterface::CBattleInterface(const CCreatureSet *army1, const CCreatureSet *army2,
|
CBattleInterface::CBattleInterface(const CCreatureSet *army1, const CCreatureSet *army2,
|
||||||
const CGHeroInstance *hero1, const CGHeroInstance *hero2,
|
const CGHeroInstance *hero1, const CGHeroInstance *hero2,
|
||||||
const SDL_Rect & myRect,
|
const SDL_Rect & myRect,
|
||||||
std::shared_ptr<CPlayerInterface> att, std::shared_ptr<CPlayerInterface> defen)
|
std::shared_ptr<CPlayerInterface> att, std::shared_ptr<CPlayerInterface> defen, std::shared_ptr<CPlayerInterface> spectatorInt)
|
||||||
: background(nullptr), queue(nullptr), attackingHeroInstance(hero1), defendingHeroInstance(hero2), animCount(0),
|
: background(nullptr), queue(nullptr), attackingHeroInstance(hero1), defendingHeroInstance(hero2), animCount(0),
|
||||||
activeStack(nullptr), mouseHoveredStack(nullptr), stackToActivate(nullptr), selectedStack(nullptr), previouslyHoveredHex(-1),
|
activeStack(nullptr), mouseHoveredStack(nullptr), stackToActivate(nullptr), selectedStack(nullptr), previouslyHoveredHex(-1),
|
||||||
currentlyHoveredHex(-1), attackingHex(-1), stackCanCastSpell(false), creatureCasting(false), spellDestSelectMode(false), spellToCast(nullptr), sp(nullptr),
|
currentlyHoveredHex(-1), attackingHex(-1), stackCanCastSpell(false), creatureCasting(false), spellDestSelectMode(false), spellToCast(nullptr), sp(nullptr),
|
||||||
@ -105,12 +105,15 @@ CBattleInterface::CBattleInterface(const CCreatureSet *army1, const CCreatureSet
|
|||||||
{
|
{
|
||||||
OBJ_CONSTRUCTION;
|
OBJ_CONSTRUCTION;
|
||||||
|
|
||||||
if (!curInt)
|
if(spectatorInt)
|
||||||
|
curInt = spectatorInt;
|
||||||
|
else if(!curInt)
|
||||||
{
|
{
|
||||||
//May happen when we are defending during network MP game -> attacker interface is just not present
|
//May happen when we are defending during network MP game -> attacker interface is just not present
|
||||||
curInt = defenderInt;
|
curInt = defenderInt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
animsAreDisplayed.setn(false);
|
animsAreDisplayed.setn(false);
|
||||||
pos = myRect;
|
pos = myRect;
|
||||||
strongInterest = true;
|
strongInterest = true;
|
||||||
@ -377,6 +380,8 @@ CBattleInterface::CBattleInterface(const CCreatureSet *army1, const CCreatureSet
|
|||||||
currentAction = INVALID;
|
currentAction = INVALID;
|
||||||
selectedAction = INVALID;
|
selectedAction = INVALID;
|
||||||
addUsedEvents(RCLICK | MOVE | KEYBOARD);
|
addUsedEvents(RCLICK | MOVE | KEYBOARD);
|
||||||
|
|
||||||
|
blockUI(settings["session"]["spectate"].Bool());
|
||||||
}
|
}
|
||||||
|
|
||||||
CBattleInterface::~CBattleInterface()
|
CBattleInterface::~CBattleInterface()
|
||||||
@ -1246,6 +1251,11 @@ void CBattleInterface::battleFinished(const BattleResult& br)
|
|||||||
void CBattleInterface::displayBattleFinished()
|
void CBattleInterface::displayBattleFinished()
|
||||||
{
|
{
|
||||||
CCS->curh->changeGraphic(ECursor::ADVENTURE,0);
|
CCS->curh->changeGraphic(ECursor::ADVENTURE,0);
|
||||||
|
if(settings["session"]["spectate"].Bool() && settings["session"]["spectate-skip-battle-result"].Bool())
|
||||||
|
{
|
||||||
|
GH.popIntTotally(this);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
SDL_Rect temp_rect = genRect(561, 470, (screen->w - 800)/2 + 165, (screen->h - 600)/2 + 19);
|
SDL_Rect temp_rect = genRect(561, 470, (screen->w - 800)/2 + 165, (screen->h - 600)/2 + 19);
|
||||||
resWindow = new CBattleResultWindow(*bresult, temp_rect, *this->curInt);
|
resWindow = new CBattleResultWindow(*bresult, temp_rect, *this->curInt);
|
||||||
@ -1531,6 +1541,9 @@ void CBattleInterface::setAnimSpeed(int set)
|
|||||||
|
|
||||||
int CBattleInterface::getAnimSpeed() const
|
int CBattleInterface::getAnimSpeed() const
|
||||||
{
|
{
|
||||||
|
if(settings["session"]["spectate"].Bool() && !settings["session"]["spectate-battle-speed"].isNull())
|
||||||
|
return vstd::round(settings["session"]["spectate-battle-speed"].Float() *100);
|
||||||
|
|
||||||
return vstd::round(settings["battle"]["animationSpeed"].Float() *100);
|
return vstd::round(settings["battle"]["animationSpeed"].Float() *100);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -269,7 +269,7 @@ public:
|
|||||||
ui32 animIDhelper; //for giving IDs for animations
|
ui32 animIDhelper; //for giving IDs for animations
|
||||||
static CondSh<bool> animsAreDisplayed; //for waiting with the end of battle for end of anims
|
static CondSh<bool> animsAreDisplayed; //for waiting with the end of battle for end of anims
|
||||||
|
|
||||||
CBattleInterface(const CCreatureSet *army1, const CCreatureSet *army2, const CGHeroInstance *hero1, const CGHeroInstance *hero2, const SDL_Rect & myRect, std::shared_ptr<CPlayerInterface> att, std::shared_ptr<CPlayerInterface> defen); //c-tor
|
CBattleInterface(const CCreatureSet *army1, const CCreatureSet *army2, const CGHeroInstance *hero1, const CGHeroInstance *hero2, const SDL_Rect & myRect, std::shared_ptr<CPlayerInterface> att, std::shared_ptr<CPlayerInterface> defen, std::shared_ptr<CPlayerInterface> spectatorInt = nullptr); //c-tor
|
||||||
virtual ~CBattleInterface(); //d-tor
|
virtual ~CBattleInterface(); //d-tor
|
||||||
|
|
||||||
//std::vector<TimeInterested*> timeinterested; //animation handling
|
//std::vector<TimeInterested*> timeinterested; //animation handling
|
||||||
|
@ -211,8 +211,7 @@ void CBattleHero::clickRight(tribool down, bool previousState)
|
|||||||
windowPosition.y = myOwner->pos.y + 135;
|
windowPosition.y = myOwner->pos.y + 135;
|
||||||
|
|
||||||
InfoAboutHero targetHero;
|
InfoAboutHero targetHero;
|
||||||
|
if(down && (myOwner->myTurn || settings["session"]["spectate"].Bool()))
|
||||||
if (down && myOwner->myTurn)
|
|
||||||
{
|
{
|
||||||
auto h = flip ? myOwner->defendingHeroInstance : myOwner->attackingHeroInstance;
|
auto h = flip ? myOwner->defendingHeroInstance : myOwner->attackingHeroInstance;
|
||||||
targetHero.initFromHero(h, InfoAboutHero::EInfoLevel::INBATTLE);
|
targetHero.initFromHero(h, InfoAboutHero::EInfoLevel::INBATTLE);
|
||||||
|
@ -361,7 +361,8 @@ void CGuiHandler::simpleRedraw()
|
|||||||
//update only top interface and draw background
|
//update only top interface and draw background
|
||||||
if(objsToBlit.size() > 1)
|
if(objsToBlit.size() > 1)
|
||||||
blitAt(screen2,0,0,screen); //blit background
|
blitAt(screen2,0,0,screen); //blit background
|
||||||
objsToBlit.back()->show(screen); //blit active interface/window
|
if(!objsToBlit.empty())
|
||||||
|
objsToBlit.back()->show(screen); //blit active interface/window
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGuiHandler::handleMoveInterested(const SDL_MouseMotionEvent & motion)
|
void CGuiHandler::handleMoveInterested(const SDL_MouseMotionEvent & motion)
|
||||||
|
@ -54,7 +54,7 @@ struct NeighborTilesInfo
|
|||||||
if ( dx + pos.x < 0 || dx + pos.x >= sizes.x
|
if ( dx + pos.x < 0 || dx + pos.x >= sizes.x
|
||||||
|| dy + pos.y < 0 || dy + pos.y >= sizes.y)
|
|| dy + pos.y < 0 || dy + pos.y >= sizes.y)
|
||||||
return false;
|
return false;
|
||||||
return visibilityMap[dx+pos.x][dy+pos.y][pos.z];
|
return settings["session"]["spectate"].Bool() ? true : visibilityMap[dx+pos.x][dy+pos.y][pos.z];
|
||||||
};
|
};
|
||||||
d7 = getTile(-1, -1); //789
|
d7 = getTile(-1, -1); //789
|
||||||
d8 = getTile( 0, -1); //456
|
d8 = getTile( 0, -1); //456
|
||||||
@ -563,7 +563,7 @@ void CMapHandler::CMapWorldViewBlitter::drawTileOverlay(SDL_Surface * targetSurf
|
|||||||
const CGObjectInstance * obj = object.obj;
|
const CGObjectInstance * obj = object.obj;
|
||||||
|
|
||||||
const bool sameLevel = obj->pos.z == pos.z;
|
const bool sameLevel = obj->pos.z == pos.z;
|
||||||
const bool isVisible = (*info->visibilityMap)[pos.x][pos.y][pos.z];
|
const bool isVisible = settings["session"]["spectate"].Bool() ? true : (*info->visibilityMap)[pos.x][pos.y][pos.z];
|
||||||
const bool isVisitable = obj->visitableAt(pos.x, pos.y);
|
const bool isVisitable = obj->visitableAt(pos.x, pos.y);
|
||||||
|
|
||||||
if(sameLevel && isVisible && isVisitable)
|
if(sameLevel && isVisible && isVisitable)
|
||||||
@ -895,7 +895,7 @@ void CMapHandler::CMapBlitter::blit(SDL_Surface * targetSurf, const MapDrawingIn
|
|||||||
{
|
{
|
||||||
const TerrainTile2 & tile = parent->ttiles[pos.x][pos.y][pos.z];
|
const TerrainTile2 & tile = parent->ttiles[pos.x][pos.y][pos.z];
|
||||||
|
|
||||||
if (!(*info->visibilityMap)[pos.x][pos.y][topTile.z] && !info->showAllTerrain)
|
if(!settings["session"]["spectate"].Bool() && !(*info->visibilityMap)[pos.x][pos.y][topTile.z] && !info->showAllTerrain)
|
||||||
drawFow(targetSurf);
|
drawFow(targetSurf);
|
||||||
|
|
||||||
// overlay needs to be drawn over fow, because of artifacts-aura-like spells
|
// overlay needs to be drawn over fow, because of artifacts-aura-like spells
|
||||||
@ -1099,6 +1099,9 @@ bool CMapHandler::CMapBlitter::canDrawObject(const CGObjectInstance * obj) const
|
|||||||
|
|
||||||
bool CMapHandler::CMapBlitter::canDrawCurrentTile() const
|
bool CMapHandler::CMapBlitter::canDrawCurrentTile() const
|
||||||
{
|
{
|
||||||
|
if(settings["session"]["spectate"].Bool())
|
||||||
|
return true;
|
||||||
|
|
||||||
const NeighborTilesInfo neighbors(pos, parent->sizes, *info->visibilityMap);
|
const NeighborTilesInfo neighbors(pos, parent->sizes, *info->visibilityMap);
|
||||||
return !neighbors.areAllHidden();
|
return !neighbors.areAllHidden();
|
||||||
}
|
}
|
||||||
|
@ -1021,7 +1021,12 @@ void CAdvMapInt::show(SDL_Surface * to)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
for(int i = 0; i < 4; i++)
|
for(int i = 0; i < 4; i++)
|
||||||
gems[i]->setFrame(LOCPLINT->playerID.getNum());
|
{
|
||||||
|
if(settings["session"]["spectate"].Bool())
|
||||||
|
gems[i]->setFrame(PlayerColor(1).getNum());
|
||||||
|
else
|
||||||
|
gems[i]->setFrame(LOCPLINT->playerID.getNum());
|
||||||
|
}
|
||||||
if(updateScreen)
|
if(updateScreen)
|
||||||
{
|
{
|
||||||
int3 betterPos = LOCPLINT->repairScreenPos(position);
|
int3 betterPos = LOCPLINT->repairScreenPos(position);
|
||||||
@ -1481,7 +1486,8 @@ void CAdvMapInt::setPlayer(PlayerColor Player)
|
|||||||
void CAdvMapInt::startTurn()
|
void CAdvMapInt::startTurn()
|
||||||
{
|
{
|
||||||
state = INGAME;
|
state = INGAME;
|
||||||
if(LOCPLINT->cb->getCurrentPlayer() == LOCPLINT->playerID)
|
if(LOCPLINT->cb->getCurrentPlayer() == LOCPLINT->playerID
|
||||||
|
|| settings["session"]["spectate"].Bool())
|
||||||
{
|
{
|
||||||
adjustActiveness(false);
|
adjustActiveness(false);
|
||||||
minimap.setAIRadar(false);
|
minimap.setAIRadar(false);
|
||||||
@ -1490,6 +1496,9 @@ void CAdvMapInt::startTurn()
|
|||||||
|
|
||||||
void CAdvMapInt::endingTurn()
|
void CAdvMapInt::endingTurn()
|
||||||
{
|
{
|
||||||
|
if(settings["session"]["spectate"].Bool())
|
||||||
|
return;
|
||||||
|
|
||||||
if(LOCPLINT->cingconsole->active)
|
if(LOCPLINT->cingconsole->active)
|
||||||
LOCPLINT->cingconsole->deactivate();
|
LOCPLINT->cingconsole->deactivate();
|
||||||
LOCPLINT->makingTurn = false;
|
LOCPLINT->makingTurn = false;
|
||||||
@ -1817,6 +1826,9 @@ const IShipyard * CAdvMapInt::ourInaccessibleShipyard(const CGObjectInstance *ob
|
|||||||
|
|
||||||
void CAdvMapInt::aiTurnStarted()
|
void CAdvMapInt::aiTurnStarted()
|
||||||
{
|
{
|
||||||
|
if(settings["session"]["spectate"].Bool())
|
||||||
|
return;
|
||||||
|
|
||||||
adjustActiveness(true);
|
adjustActiveness(true);
|
||||||
CCS->musich->playMusicFromSet("enemy-turn", true);
|
CCS->musich->playMusicFromSet("enemy-turn", true);
|
||||||
adventureInt->minimap.setAIRadar(true);
|
adventureInt->minimap.setAIRadar(true);
|
||||||
|
@ -225,9 +225,13 @@ void CWindowObject::setShadow(bool on)
|
|||||||
|
|
||||||
void CWindowObject::showAll(SDL_Surface *to)
|
void CWindowObject::showAll(SDL_Surface *to)
|
||||||
{
|
{
|
||||||
|
auto color = LOCPLINT ? LOCPLINT->playerID : PlayerColor(1);
|
||||||
|
if(settings["session"]["spectate"].Bool())
|
||||||
|
color = PlayerColor(1); // TODO: Spectator shouldn't need special code for UI colors
|
||||||
|
|
||||||
CIntObject::showAll(to);
|
CIntObject::showAll(to);
|
||||||
if ((options & BORDERED) && (pos.h != to->h || pos.w != to->w))
|
if ((options & BORDERED) && (pos.h != to->h || pos.w != to->w))
|
||||||
CMessage::drawBorder(LOCPLINT ? LOCPLINT->playerID : PlayerColor(1), to, pos.w+28, pos.h+29, pos.x-14, pos.y-15);
|
CMessage::drawBorder(color, to, pos.w+28, pos.h+29, pos.x-14, pos.y-15);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CWindowObject::close()
|
void CWindowObject::close()
|
||||||
|
@ -335,6 +335,8 @@ void CRClickPopup::close()
|
|||||||
void CRClickPopup::createAndPush(const std::string &txt, const CInfoWindow::TCompsInfo &comps)
|
void CRClickPopup::createAndPush(const std::string &txt, const CInfoWindow::TCompsInfo &comps)
|
||||||
{
|
{
|
||||||
PlayerColor player = LOCPLINT ? LOCPLINT->playerID : PlayerColor(1); //if no player, then use blue
|
PlayerColor player = LOCPLINT ? LOCPLINT->playerID : PlayerColor(1); //if no player, then use blue
|
||||||
|
if(settings["session"]["spectate"].Bool())//TODO: there must be better way to implement this
|
||||||
|
player = PlayerColor(1);
|
||||||
|
|
||||||
CSimpleWindow * temp = new CInfoWindow(txt, player, comps);
|
CSimpleWindow * temp = new CInfoWindow(txt, player, comps);
|
||||||
temp->center(Point(GH.current->motion)); //center on mouse
|
temp->center(Point(GH.current->motion)); //center on mouse
|
||||||
|
@ -237,7 +237,7 @@ const CGTownInstance * CBattleInfoEssentials::battleGetDefendedTown() const
|
|||||||
BattlePerspective::BattlePerspective CBattleInfoEssentials::battleGetMySide() const
|
BattlePerspective::BattlePerspective CBattleInfoEssentials::battleGetMySide() const
|
||||||
{
|
{
|
||||||
RETURN_IF_NOT_BATTLE(BattlePerspective::INVALID);
|
RETURN_IF_NOT_BATTLE(BattlePerspective::INVALID);
|
||||||
if(!player)
|
if(!player || player.get().isSpectator())
|
||||||
return BattlePerspective::ALL_KNOWING;
|
return BattlePerspective::ALL_KNOWING;
|
||||||
if(*player == getBattle()->sides[0].color)
|
if(*player == getBattle()->sides[0].color)
|
||||||
return BattlePerspective::LEFT_SIDE;
|
return BattlePerspective::LEFT_SIDE;
|
||||||
|
@ -595,7 +595,7 @@ const CMapHeader * CGameInfoCallback::getMapHeader() const
|
|||||||
|
|
||||||
bool CGameInfoCallback::hasAccess(boost::optional<PlayerColor> playerId) const
|
bool CGameInfoCallback::hasAccess(boost::optional<PlayerColor> playerId) const
|
||||||
{
|
{
|
||||||
return !player || gs->getPlayerRelations( *playerId, *player ) != PlayerRelations::ENEMIES;
|
return !player || player.get().isSpectator() || gs->getPlayerRelations( *playerId, *player ) != PlayerRelations::ENEMIES;
|
||||||
}
|
}
|
||||||
|
|
||||||
EPlayerStatus::EStatus CGameInfoCallback::getPlayerStatus(PlayerColor player, bool verbose) const
|
EPlayerStatus::EStatus CGameInfoCallback::getPlayerStatus(PlayerColor player, bool verbose) const
|
||||||
|
@ -2208,6 +2208,9 @@ bool CGameState::isVisible(int3 pos, PlayerColor player)
|
|||||||
{
|
{
|
||||||
if(player == PlayerColor::NEUTRAL)
|
if(player == PlayerColor::NEUTRAL)
|
||||||
return false;
|
return false;
|
||||||
|
if(player.isSpectator())
|
||||||
|
return true;
|
||||||
|
|
||||||
return getPlayerTeam(player)->fogOfWarMap[pos.x][pos.y][pos.z];
|
return getPlayerTeam(player)->fogOfWarMap[pos.x][pos.y][pos.z];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,6 +29,7 @@ const SlotID SlotID::SUMMONED_SLOT_PLACEHOLDER = SlotID(-3);
|
|||||||
const SlotID SlotID::WAR_MACHINES_SLOT = SlotID(-4);
|
const SlotID SlotID::WAR_MACHINES_SLOT = SlotID(-4);
|
||||||
const SlotID SlotID::ARROW_TOWERS_SLOT = SlotID(-5);
|
const SlotID SlotID::ARROW_TOWERS_SLOT = SlotID(-5);
|
||||||
|
|
||||||
|
const PlayerColor PlayerColor::SPECTATOR = PlayerColor(252);
|
||||||
const PlayerColor PlayerColor::CANNOT_DETERMINE = PlayerColor(253);
|
const PlayerColor PlayerColor::CANNOT_DETERMINE = PlayerColor(253);
|
||||||
const PlayerColor PlayerColor::UNFLAGGABLE = PlayerColor(254);
|
const PlayerColor PlayerColor::UNFLAGGABLE = PlayerColor(254);
|
||||||
const PlayerColor PlayerColor::NEUTRAL = PlayerColor(255);
|
const PlayerColor PlayerColor::NEUTRAL = PlayerColor(255);
|
||||||
@ -72,6 +73,11 @@ bool PlayerColor::isValidPlayer() const
|
|||||||
return num < PLAYER_LIMIT_I;
|
return num < PLAYER_LIMIT_I;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool PlayerColor::isSpectator() const
|
||||||
|
{
|
||||||
|
return num == 252;
|
||||||
|
}
|
||||||
|
|
||||||
std::string PlayerColor::getStr(bool L10n) const
|
std::string PlayerColor::getStr(bool L10n) const
|
||||||
{
|
{
|
||||||
std::string ret = "unnamed";
|
std::string ret = "unnamed";
|
||||||
|
@ -257,12 +257,14 @@ class PlayerColor : public BaseForID<PlayerColor, ui8>
|
|||||||
PLAYER_LIMIT_I = 8
|
PLAYER_LIMIT_I = 8
|
||||||
};
|
};
|
||||||
|
|
||||||
|
DLL_LINKAGE static const PlayerColor SPECTATOR; //252
|
||||||
DLL_LINKAGE static const PlayerColor CANNOT_DETERMINE; //253
|
DLL_LINKAGE static const PlayerColor CANNOT_DETERMINE; //253
|
||||||
DLL_LINKAGE static const PlayerColor UNFLAGGABLE; //254 - neutral objects (pandora, banks)
|
DLL_LINKAGE static const PlayerColor UNFLAGGABLE; //254 - neutral objects (pandora, banks)
|
||||||
DLL_LINKAGE static const PlayerColor NEUTRAL; //255
|
DLL_LINKAGE static const PlayerColor NEUTRAL; //255
|
||||||
DLL_LINKAGE static const PlayerColor PLAYER_LIMIT; //player limit per map
|
DLL_LINKAGE static const PlayerColor PLAYER_LIMIT; //player limit per map
|
||||||
|
|
||||||
DLL_LINKAGE bool isValidPlayer() const; //valid means < PLAYER_LIMIT (especially non-neutral)
|
DLL_LINKAGE bool isValidPlayer() const; //valid means < PLAYER_LIMIT (especially non-neutral)
|
||||||
|
DLL_LINKAGE bool isSpectator() const;
|
||||||
|
|
||||||
DLL_LINKAGE std::string getStr(bool L10n = false) const;
|
DLL_LINKAGE std::string getStr(bool L10n = false) const;
|
||||||
DLL_LINKAGE std::string getStrCap(bool L10n = false) const;
|
DLL_LINKAGE std::string getStrCap(bool L10n = false) const;
|
||||||
|
@ -1842,6 +1842,9 @@ DLL_LINKAGE void BattleSetStackProperty::applyGs(CGameState *gs)
|
|||||||
|
|
||||||
DLL_LINKAGE void PlayerCheated::applyGs(CGameState *gs)
|
DLL_LINKAGE void PlayerCheated::applyGs(CGameState *gs)
|
||||||
{
|
{
|
||||||
|
if(!player.isValidPlayer())
|
||||||
|
return;
|
||||||
|
|
||||||
gs->getPlayer(player)->enteredLosingCheatCode = losingCheatCode;
|
gs->getPlayer(player)->enteredLosingCheatCode = losingCheatCode;
|
||||||
gs->getPlayer(player)->enteredWinningCheatCode = winningCheatCode;
|
gs->getPlayer(player)->enteredWinningCheatCode = winningCheatCode;
|
||||||
}
|
}
|
||||||
|
@ -1878,7 +1878,8 @@ void CGameHandler::run(bool resume)
|
|||||||
sbuffer << color << " ";
|
sbuffer << color << " ";
|
||||||
{
|
{
|
||||||
boost::unique_lock<boost::recursive_mutex> lock(gsm);
|
boost::unique_lock<boost::recursive_mutex> lock(gsm);
|
||||||
connections[color] = cc;
|
if(!color.isSpectator()) // there can be more than one spectator
|
||||||
|
connections[color] = cc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
logGlobal->info(sbuffer.str());
|
logGlobal->info(sbuffer.str());
|
||||||
@ -4430,7 +4431,9 @@ void CGameHandler::playerMessage(PlayerColor player, const std::string &message,
|
|||||||
{
|
{
|
||||||
SystemMessage temp_message(VLC->generaltexth->allTexts.at(260));
|
SystemMessage temp_message(VLC->generaltexth->allTexts.at(260));
|
||||||
sendAndApply(&temp_message);
|
sendAndApply(&temp_message);
|
||||||
checkVictoryLossConditionsForPlayer(player);//Player enter win code or got required art\creature
|
|
||||||
|
if(!player.isSpectator())
|
||||||
|
checkVictoryLossConditionsForPlayer(player);//Player enter win code or got required art\creature
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -283,8 +283,11 @@ bool CastAdvSpell::applyGh( CGameHandler *gh )
|
|||||||
|
|
||||||
bool PlayerMessage::applyGh( CGameHandler *gh )
|
bool PlayerMessage::applyGh( CGameHandler *gh )
|
||||||
{
|
{
|
||||||
ERROR_IF_NOT(player);
|
if(!player.isSpectator()) // TODO: clearly not a great way to verify permissions
|
||||||
if(gh->getPlayerAt(c) != player) ERROR_AND_RETURN;
|
{
|
||||||
|
ERROR_IF_NOT(player);
|
||||||
|
if(gh->getPlayerAt(c) != player) ERROR_AND_RETURN;
|
||||||
|
}
|
||||||
gh->playerMessage(player,text, currObj);
|
gh->playerMessage(player,text, currObj);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user