mirror of
https://github.com/vcmi/vcmi.git
synced 2024-12-28 23:06:24 +02:00
Merge pull request #4327 from Laserlicht/handicap
Handicap / resource transfer
This commit is contained in:
commit
257fb8c70c
@ -317,7 +317,7 @@ void BuildAnalyzer::updateDailyIncome()
|
||||
|
||||
if(mine)
|
||||
{
|
||||
dailyIncome[mine->producedResource.getNum()] += mine->producedQuantity;
|
||||
dailyIncome[mine->producedResource.getNum()] += mine->getProducedQuantity();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -73,7 +73,11 @@
|
||||
"vcmi.lobby.sortDate" : "Sorts maps by change date",
|
||||
"vcmi.lobby.backToLobby" : "Return to lobby",
|
||||
"vcmi.lobby.author" : "Author",
|
||||
|
||||
"vcmi.lobby.handicap" : "Handicap",
|
||||
"vcmi.lobby.handicap.resource" : "Gives players appropriate resources to start with in addition to the normal starting resources. Negative values are allowed, but are limited to 0 in total (the player never starts with negative resources).",
|
||||
"vcmi.lobby.handicap.income" : "Changes the player's various incomes by the percentage. Is rounded up.",
|
||||
"vcmi.lobby.handicap.growth" : "Changes the growth rate of creatures in the towns owned by the player. Is rounded up.",
|
||||
|
||||
"vcmi.lobby.login.title" : "VCMI Online Lobby",
|
||||
"vcmi.lobby.login.username" : "Username:",
|
||||
"vcmi.lobby.login.connecting" : "Connecting...",
|
||||
|
@ -73,6 +73,10 @@
|
||||
"vcmi.lobby.sortDate" : "Ordnet Karten nach Änderungsdatum",
|
||||
"vcmi.lobby.backToLobby" : "Zur Lobby zurückkehren",
|
||||
"vcmi.lobby.author" : "Author",
|
||||
"vcmi.lobby.handicap" : "Handicap",
|
||||
"vcmi.lobby.handicap.resource" : "Gibt den Spielern entsprechende Ressourcen zum Start zusätzlich zu den normalen Startressourcen. Negative Werte sind erlaubt, werden aber insgesamt auf 0 begrenzt (der Spieler beginnt nie mit negativen Ressourcen).",
|
||||
"vcmi.lobby.handicap.income" : "Verändert die verschiedenen Einkommen des Spielers um den Prozentsatz. Wird aufgerundet.",
|
||||
"vcmi.lobby.handicap.growth" : "Verändert die Wachstumsrate der Kreaturen in den Städten, die der Spieler besitzt. Wird aufgerundet.",
|
||||
|
||||
"vcmi.lobby.login.title" : "VCMI Online Lobby",
|
||||
"vcmi.lobby.login.username" : "Benutzername:",
|
||||
|
@ -492,6 +492,14 @@ void CServerHandler::setPlayerName(PlayerColor color, const std::string & name)
|
||||
sendLobbyPack(lspn);
|
||||
}
|
||||
|
||||
void CServerHandler::setPlayerHandicap(PlayerColor color, Handicap handicap) const
|
||||
{
|
||||
LobbySetPlayerHandicap lsph;
|
||||
lsph.color = color;
|
||||
lsph.handicap = handicap;
|
||||
sendLobbyPack(lsph);
|
||||
}
|
||||
|
||||
void CServerHandler::setPlayerOption(ui8 what, int32_t value, PlayerColor player) const
|
||||
{
|
||||
LobbyChangePlayerOption lcpo;
|
||||
|
@ -82,6 +82,7 @@ public:
|
||||
virtual void setMapInfo(std::shared_ptr<CMapInfo> to, std::shared_ptr<CMapGenOptions> mapGenOpts = {}) const = 0;
|
||||
virtual void setPlayer(PlayerColor color) const = 0;
|
||||
virtual void setPlayerName(PlayerColor color, const std::string & name) const = 0;
|
||||
virtual void setPlayerHandicap(PlayerColor color, Handicap handicap) const = 0;
|
||||
virtual void setPlayerOption(ui8 what, int32_t value, PlayerColor player) const = 0;
|
||||
virtual void setDifficulty(int to) const = 0;
|
||||
virtual void setTurnTimerInfo(const TurnTimerInfo &) const = 0;
|
||||
@ -191,6 +192,7 @@ public:
|
||||
void setMapInfo(std::shared_ptr<CMapInfo> to, std::shared_ptr<CMapGenOptions> mapGenOpts = {}) const override;
|
||||
void setPlayer(PlayerColor color) const override;
|
||||
void setPlayerName(PlayerColor color, const std::string & name) const override;
|
||||
void setPlayerHandicap(PlayerColor color, Handicap handicap) const override;
|
||||
void setPlayerOption(ui8 what, int32_t value, PlayerColor player) const override;
|
||||
void setDifficulty(int to) const override;
|
||||
void setTurnTimerInfo(const TurnTimerInfo &) const override;
|
||||
|
@ -91,6 +91,7 @@ enum class EShortcut
|
||||
LOBBY_FLIP_COIN,
|
||||
LOBBY_RANDOM_TOWN,
|
||||
LOBBY_RANDOM_TOWN_VS,
|
||||
LOBBY_HANDICAP,
|
||||
|
||||
MAPS_SIZE_S,
|
||||
MAPS_SIZE_M,
|
||||
|
@ -288,6 +288,7 @@ EShortcut ShortcutHandler::findShortcut(const std::string & identifier ) const
|
||||
{"lobbyFlipCoin", EShortcut::LOBBY_FLIP_COIN },
|
||||
{"lobbyRandomTown", EShortcut::LOBBY_RANDOM_TOWN },
|
||||
{"lobbyRandomTownVs", EShortcut::LOBBY_RANDOM_TOWN_VS },
|
||||
{"lobbyHandicap", EShortcut::LOBBY_HANDICAP },
|
||||
{"mapsSizeS", EShortcut::MAPS_SIZE_S },
|
||||
{"mapsSizeM", EShortcut::MAPS_SIZE_M },
|
||||
{"mapsSizeL", EShortcut::MAPS_SIZE_L },
|
||||
|
@ -437,6 +437,13 @@ PvPBox::PvPBox(const Rect & rect)
|
||||
CSH->sendLobbyPack(lpa);
|
||||
}, EShortcut::LOBBY_RANDOM_TOWN_VS);
|
||||
buttonRandomTownVs->setTextOverlay(CGI->generaltexth->translate("vcmi.lobby.pvp.randomTownVs.hover"), EFonts::FONT_SMALL, Colors::WHITE);
|
||||
|
||||
buttonHandicap = std::make_shared<CButton>(Point(190, 81), AnimationPath::builtin("GSPBUT2.DEF"), CButton::tooltip("", CGI->generaltexth->translate("vcmi.lobby.handicap")), [](){
|
||||
if(!CSH->isHost())
|
||||
return;
|
||||
GH.windows().createAndPushWindow<OptionsTab::HandicapWindow>();
|
||||
}, EShortcut::LOBBY_HANDICAP);
|
||||
buttonHandicap->setTextOverlay(CGI->generaltexth->translate("vcmi.lobby.handicap"), EFonts::FONT_SMALL, Colors::WHITE);
|
||||
}
|
||||
|
||||
TownSelector::TownSelector(const Point & loc)
|
||||
|
@ -153,6 +153,7 @@ class PvPBox : public CIntObject
|
||||
std::shared_ptr<CButton> buttonFlipCoin;
|
||||
std::shared_ptr<CButton> buttonRandomTown;
|
||||
std::shared_ptr<CButton> buttonRandomTownVs;
|
||||
std::shared_ptr<CButton> buttonHandicap;
|
||||
public:
|
||||
PvPBox(const Rect & rect);
|
||||
};
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include "../widgets/ObjectLists.h"
|
||||
#include "../widgets/Slider.h"
|
||||
#include "../widgets/TextControls.h"
|
||||
#include "../widgets/GraphicalPrimitiveCanvas.h"
|
||||
#include "../windows/GUIClasses.h"
|
||||
#include "../windows/InfoWindows.h"
|
||||
#include "../windows/CHeroOverview.h"
|
||||
@ -793,6 +794,119 @@ void OptionsTab::SelectionWindow::showPopupWindow(const Point & cursorPosition)
|
||||
setElement(elem, false);
|
||||
}
|
||||
|
||||
OptionsTab::HandicapWindow::HandicapWindow()
|
||||
: CWindowObject(BORDERED)
|
||||
{
|
||||
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
|
||||
|
||||
addUsedEvents(LCLICK);
|
||||
|
||||
pos = Rect(0, 0, 660, 100 + SEL->getStartInfo()->playerInfos.size() * 30);
|
||||
|
||||
backgroundTexture = std::make_shared<FilledTexturePlayerColored>(ImagePath::builtin("DiBoxBck"), pos);
|
||||
backgroundTexture->setPlayerColor(PlayerColor(1));
|
||||
|
||||
labels.push_back(std::make_shared<CLabel>(pos.w / 2 + 8, 15, FONT_BIG, ETextAlignment::CENTER, Colors::YELLOW, CGI->generaltexth->translate("vcmi.lobby.handicap")));
|
||||
|
||||
enum Columns : int32_t
|
||||
{
|
||||
INCOME = 1000,
|
||||
GROWTH = 2000,
|
||||
};
|
||||
auto columns = std::vector<EGameResID>{EGameResID::GOLD, EGameResID::WOOD, EGameResID::MERCURY, EGameResID::ORE, EGameResID::SULFUR, EGameResID::CRYSTAL, EGameResID::GEMS, Columns::INCOME, Columns::GROWTH};
|
||||
|
||||
int i = 0;
|
||||
for(auto & pInfo : SEL->getStartInfo()->playerInfos)
|
||||
{
|
||||
PlayerColor player = pInfo.first;
|
||||
anim.push_back(std::make_shared<CAnimImage>(AnimationPath::builtin("ITGFLAGS"), player.getNum(), 0, 7, 57 + i * 30));
|
||||
for(int j = 0; j < columns.size(); j++)
|
||||
{
|
||||
bool isIncome = int(columns[j]) == Columns::INCOME;
|
||||
bool isGrowth = int(columns[j]) == Columns::GROWTH;
|
||||
EGameResID resource = columns[j];
|
||||
|
||||
const PlayerSettings &ps = SEL->getStartInfo()->getIthPlayersSettings(player);
|
||||
|
||||
int xPos = 30 + j * 70;
|
||||
xPos += j > 0 ? 10 : 0; // Gold field is larger
|
||||
|
||||
if(i == 0)
|
||||
{
|
||||
if(isIncome)
|
||||
labels.push_back(std::make_shared<CLabel>(xPos, 35, FONT_SMALL, ETextAlignment::TOPLEFT, Colors::WHITE, CGI->generaltexth->translate("core.jktext.32")));
|
||||
else if(isGrowth)
|
||||
labels.push_back(std::make_shared<CLabel>(xPos, 35, FONT_SMALL, ETextAlignment::TOPLEFT, Colors::WHITE, CGI->generaltexth->translate("core.genrltxt.194")));
|
||||
else
|
||||
anim.push_back(std::make_shared<CAnimImage>(AnimationPath::builtin("SMALRES"), GameResID(resource), 0, 15 + xPos + (j == 0 ? 10 : 0), 35));
|
||||
}
|
||||
|
||||
auto area = Rect(xPos, 60 + i * 30, j == 0 ? 60 : 50, 16);
|
||||
textinputbackgrounds.push_back(std::make_shared<TransparentFilledRectangle>(area.resize(3), ColorRGBA(0,0,0,128), ColorRGBA(64,64,64,64)));
|
||||
textinputs[player][resource] = std::make_shared<CTextInput>(area, FONT_SMALL, ETextAlignment::CENTERLEFT, true);
|
||||
textinputs[player][resource]->setText(std::to_string(isIncome ? ps.handicap.percentIncome : (isGrowth ? ps.handicap.percentGrowth : ps.handicap.startBonus[resource])));
|
||||
textinputs[player][resource]->setCallback([this, player, resource, isIncome, isGrowth](const std::string & s){
|
||||
// text input processing: add/remove sign when pressing "-"; remove non digits; cut length; fill empty field with 0
|
||||
std::string tmp = s;
|
||||
bool negative = std::count_if( s.begin(), s.end(), []( char c ){ return c == '-'; }) == 1 && !isIncome && !isGrowth;
|
||||
tmp.erase(std::remove_if(tmp.begin(), tmp.end(), [](char c) { return !isdigit(c); }), tmp.end());
|
||||
int maxLength = isIncome || isGrowth ? 3 : (resource == EGameResID::GOLD ? 6 : 5);
|
||||
tmp = tmp.substr(0, maxLength);
|
||||
textinputs[player][resource]->setText(tmp.length() == 0 ? "0" : (negative ? "-" : "") + std::to_string(stoi(tmp)));
|
||||
});
|
||||
textinputs[player][resource]->setPopupCallback([isIncome, isGrowth](){
|
||||
// Help for the textinputs
|
||||
if(isIncome)
|
||||
CRClickPopup::createAndPush(CGI->generaltexth->translate("vcmi.lobby.handicap.income"));
|
||||
else if(isGrowth)
|
||||
CRClickPopup::createAndPush(CGI->generaltexth->translate("vcmi.lobby.handicap.growth"));
|
||||
else
|
||||
CRClickPopup::createAndPush(CGI->generaltexth->translate("vcmi.lobby.handicap.resource"));
|
||||
});
|
||||
if(isIncome || isGrowth)
|
||||
labels.push_back(std::make_shared<CLabel>(area.topRight().x, area.center().y, FONT_SMALL, ETextAlignment::CENTERRIGHT, Colors::WHITE, "%"));
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
buttons.push_back(std::make_shared<CButton>(Point(pos.w / 2 - 32, 60 + SEL->getStartInfo()->playerInfos.size() * 30), AnimationPath::builtin("MuBchck"), CButton::tooltip(), [this](){
|
||||
for (const auto& player : textinputs)
|
||||
{
|
||||
TResources resources = TResources();
|
||||
int income = 100;
|
||||
int growth = 100;
|
||||
for (const auto& resource : player.second)
|
||||
{
|
||||
bool isIncome = int(resource.first) == Columns::INCOME;
|
||||
bool isGrowth = int(resource.first) == Columns::GROWTH;
|
||||
if(isIncome)
|
||||
income = std::stoi(resource.second->getText());
|
||||
else if(isGrowth)
|
||||
growth = std::stoi(resource.second->getText());
|
||||
else
|
||||
resources[resource.first] = std::stoi(resource.second->getText());
|
||||
}
|
||||
CSH->setPlayerHandicap(player.first, Handicap{resources, income, growth});
|
||||
}
|
||||
|
||||
close();
|
||||
}, EShortcut::GLOBAL_RETURN));
|
||||
|
||||
updateShadow();
|
||||
center();
|
||||
}
|
||||
|
||||
bool OptionsTab::HandicapWindow::receiveEvent(const Point & position, int eventType) const
|
||||
{
|
||||
return true; // capture click also outside of window
|
||||
}
|
||||
|
||||
void OptionsTab::HandicapWindow::clickReleased(const Point & cursorPosition)
|
||||
{
|
||||
if(!pos.isInside(cursorPosition)) // make it possible to close window by touching/clicking outside of window
|
||||
close();
|
||||
}
|
||||
|
||||
OptionsTab::SelectedBox::SelectedBox(Point position, PlayerSettings & playerSettings, SelType type)
|
||||
: Scrollable(LCLICK | SHOW_POPUP, position, Orientation::HORIZONTAL)
|
||||
, CPlayerSettingsHelper(playerSettings, type)
|
||||
@ -923,6 +1037,47 @@ OptionsTab::PlayerOptionsEntry::PlayerOptionsEntry(const PlayerSettings & S, con
|
||||
}
|
||||
labelWhoCanPlay = std::make_shared<CMultiLineLabel>(Rect(6, 23, 45, (int)graphics->fonts[EFonts::FONT_TINY]->getLineHeight()*2), EFonts::FONT_TINY, ETextAlignment::CENTER, Colors::WHITE, CGI->generaltexth->arraytxt[206 + whoCanPlay]);
|
||||
|
||||
auto hasHandicap = [this](){ return s->handicap.startBonus.empty() && s->handicap.percentIncome == 100 && s->handicap.percentGrowth == 100; };
|
||||
std::string labelHandicapText = hasHandicap() ? CGI->generaltexth->arraytxt[210] : MetaString::createFromTextID("vcmi.lobby.handicap").toString();
|
||||
labelHandicap = std::make_shared<CMultiLineLabel>(Rect(57, 24, 47, (int)graphics->fonts[EFonts::FONT_TINY]->getLineHeight()*2), EFonts::FONT_TINY, ETextAlignment::CENTER, Colors::WHITE, labelHandicapText);
|
||||
handicap = std::make_shared<LRClickableArea>(Rect(56, 24, 49, (int)graphics->fonts[EFonts::FONT_TINY]->getLineHeight()*2), [](){
|
||||
if(!CSH->isHost())
|
||||
return;
|
||||
|
||||
GH.windows().createAndPushWindow<HandicapWindow>();
|
||||
}, [this, hasHandicap](){
|
||||
if(hasHandicap())
|
||||
CRClickPopup::createAndPush(MetaString::createFromTextID("core.help.124.help").toString());
|
||||
else
|
||||
{
|
||||
auto str = MetaString::createFromTextID("vcmi.lobby.handicap");
|
||||
str.appendRawString(":\n");
|
||||
for(auto & res : EGameResID::ALL_RESOURCES())
|
||||
if(s->handicap.startBonus[res] != 0)
|
||||
{
|
||||
str.appendRawString("\n");
|
||||
str.appendName(res);
|
||||
str.appendRawString(": ");
|
||||
str.appendRawString(std::to_string(s->handicap.startBonus[res]));
|
||||
}
|
||||
if(s->handicap.percentIncome != 100)
|
||||
{
|
||||
str.appendRawString("\n");
|
||||
str.appendTextID("core.jktext.32");
|
||||
str.appendRawString(": ");
|
||||
str.appendRawString(std::to_string(s->handicap.percentIncome) + "%");
|
||||
}
|
||||
if(s->handicap.percentGrowth != 100)
|
||||
{
|
||||
str.appendRawString("\n");
|
||||
str.appendTextID("core.genrltxt.194");
|
||||
str.appendRawString(": ");
|
||||
str.appendRawString(std::to_string(s->handicap.percentGrowth) + "%");
|
||||
}
|
||||
CRClickPopup::createAndPush(str.toString());
|
||||
}
|
||||
});
|
||||
|
||||
if(SEL->screenType == ESelectionScreen::newGame)
|
||||
{
|
||||
buttonTownLeft = std::make_shared<CButton>(Point(107, 5), AnimationPath::builtin("ADOPLFA.DEF"), CGI->generaltexth->zelp[132], std::bind(&IServerAPI::setPlayerOption, CSH, LobbyChangePlayerOption::TOWN, -1, s->color));
|
||||
|
@ -27,8 +27,10 @@ class CComponentBox;
|
||||
class CTextBox;
|
||||
class CButton;
|
||||
class CSlider;
|
||||
class LRClickableArea;
|
||||
|
||||
class FilledTexturePlayerColored;
|
||||
class TransparentFilledRectangle;
|
||||
|
||||
/// The options tab which is shown at the map selection phase.
|
||||
class OptionsTab : public OptionsTabBase
|
||||
@ -51,6 +53,22 @@ public:
|
||||
BONUS
|
||||
};
|
||||
|
||||
class HandicapWindow : public CWindowObject
|
||||
{
|
||||
std::shared_ptr<FilledTexturePlayerColored> backgroundTexture;
|
||||
|
||||
std::vector<std::shared_ptr<CLabel>> labels;
|
||||
std::vector<std::shared_ptr<CAnimImage>> anim;
|
||||
std::vector<std::shared_ptr<TransparentFilledRectangle>> textinputbackgrounds;
|
||||
std::map<PlayerColor, std::map<EGameResID, std::shared_ptr<CTextInput>>> textinputs;
|
||||
std::vector<std::shared_ptr<CButton>> buttons;
|
||||
|
||||
bool receiveEvent(const Point & position, int eventType) const override;
|
||||
void clickReleased(const Point & cursorPosition) override;
|
||||
public:
|
||||
HandicapWindow();
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
struct CPlayerSettingsHelper
|
||||
@ -192,6 +210,8 @@ private:
|
||||
std::shared_ptr<SelectedBox> town;
|
||||
std::shared_ptr<SelectedBox> hero;
|
||||
std::shared_ptr<SelectedBox> bonus;
|
||||
std::shared_ptr<LRClickableArea> handicap;
|
||||
std::shared_ptr<CMultiLineLabel> labelHandicap;
|
||||
enum {HUMAN_OR_CPU, HUMAN, CPU} whoCanPlay;
|
||||
|
||||
PlayerOptionsEntry(const PlayerSettings & S, const OptionsTab & parentTab);
|
||||
|
@ -30,7 +30,7 @@ CTextInput::CTextInput(const Rect & Pos)
|
||||
pos.h = Pos.h;
|
||||
pos.w = Pos.w;
|
||||
|
||||
addUsedEvents(LCLICK | KEYBOARD | TEXTINPUT);
|
||||
addUsedEvents(LCLICK | SHOW_POPUP | KEYBOARD | TEXTINPUT);
|
||||
}
|
||||
|
||||
void CTextInput::createLabel(bool giveFocusToInput)
|
||||
@ -106,6 +106,11 @@ void CTextInput::setCallback(const TextEditedCallback & cb)
|
||||
onTextEdited = cb;
|
||||
}
|
||||
|
||||
void CTextInput::setPopupCallback(const std::function<void()> & cb)
|
||||
{
|
||||
callbackPopup = cb;
|
||||
}
|
||||
|
||||
void CTextInput::setFilterFilename()
|
||||
{
|
||||
assert(!onTextFiltering);
|
||||
@ -122,6 +127,12 @@ std::string CTextInput::getVisibleText() const
|
||||
return hasFocus() ? currentText + composedText + "_" : currentText;
|
||||
}
|
||||
|
||||
void CTextInput::showPopupWindow(const Point & cursorPosition)
|
||||
{
|
||||
if(callbackPopup)
|
||||
callbackPopup();
|
||||
}
|
||||
|
||||
void CTextInput::clickPressed(const Point & cursorPosition)
|
||||
{
|
||||
// attempt to give focus unconditionally, even if we already have it
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "../render/EFont.h"
|
||||
|
||||
#include "../../lib/filesystem/ResourcePath.h"
|
||||
#include "../../lib/FunctionList.h"
|
||||
|
||||
class CLabel;
|
||||
class IImage;
|
||||
@ -58,6 +59,7 @@ class CTextInput final : public CFocusable
|
||||
|
||||
TextEditedCallback onTextEdited;
|
||||
TextFilterCallback onTextFiltering;
|
||||
CFunctionList<void()> callbackPopup;
|
||||
|
||||
//Filter that will block all characters not allowed in filenames
|
||||
static void filenameFilter(std::string & text, const std::string & oldText);
|
||||
@ -74,6 +76,7 @@ class CTextInput final : public CFocusable
|
||||
void textEdited(const std::string & enteredText) final;
|
||||
void onFocusGot() final;
|
||||
void onFocusLost() final;
|
||||
void showPopupWindow(const Point & cursorPosition) final;
|
||||
|
||||
CTextInput(const Rect & Pos);
|
||||
public:
|
||||
@ -89,6 +92,9 @@ public:
|
||||
/// Set callback that will be called whenever player enters new text
|
||||
void setCallback(const TextEditedCallback & cb);
|
||||
|
||||
/// Set callback when player want to open popup
|
||||
void setPopupCallback(const std::function<void()> & cb);
|
||||
|
||||
/// Enables filtering entered text that ensures that text is valid filename (existing or not)
|
||||
void setFilterFilename();
|
||||
/// Enable filtering entered text that ensures that text is valid number in provided range [min, max]
|
||||
|
@ -37,6 +37,7 @@
|
||||
#include "../../lib/CHeroHandler.h"
|
||||
#include "../../lib/GameSettings.h"
|
||||
#include "../../lib/CSkillHandler.h"
|
||||
#include "../../lib/StartInfo.h"
|
||||
#include "../../lib/mapObjects/CGHeroInstance.h"
|
||||
#include "../../lib/mapObjects/CGTownInstance.h"
|
||||
#include "../../lib/mapObjects/MiscObjects.h"
|
||||
@ -586,15 +587,16 @@ void CKingdomInterface::generateMinesList(const std::vector<const CGObjectInstan
|
||||
minesCount[mine->producedResource]++;
|
||||
|
||||
if (mine->producedResource == EGameResID::GOLD)
|
||||
totalIncome += mine->producedQuantity;
|
||||
totalIncome += mine->getProducedQuantity();
|
||||
}
|
||||
}
|
||||
|
||||
//Heroes can produce gold as well - skill, specialty or arts
|
||||
std::vector<const CGHeroInstance*> heroes = LOCPLINT->cb->getHeroesInfo(true);
|
||||
auto * playerSettings = LOCPLINT->cb->getPlayerSettings(LOCPLINT->playerID);
|
||||
for(auto & hero : heroes)
|
||||
{
|
||||
totalIncome += hero->valOfBonuses(Selector::typeSubtype(BonusType::GENERATE_RESOURCE, BonusSubtypeID(GameResID(EGameResID::GOLD))));
|
||||
totalIncome += hero->valOfBonuses(Selector::typeSubtype(BonusType::GENERATE_RESOURCE, BonusSubtypeID(GameResID(EGameResID::GOLD)))) * playerSettings->handicap.percentIncome / 100;
|
||||
}
|
||||
|
||||
//Add town income of all towns
|
||||
@ -605,8 +607,8 @@ void CKingdomInterface::generateMinesList(const std::vector<const CGObjectInstan
|
||||
}
|
||||
|
||||
//if player has some modded boosts we want to show that as well
|
||||
totalIncome += LOCPLINT->cb->getPlayerState(LOCPLINT->playerID)->valOfBonuses(BonusType::RESOURCES_CONSTANT_BOOST, BonusSubtypeID(GameResID(EGameResID::GOLD)));
|
||||
totalIncome += LOCPLINT->cb->getPlayerState(LOCPLINT->playerID)->valOfBonuses(BonusType::RESOURCES_TOWN_MULTIPLYING_BOOST, BonusSubtypeID(GameResID(EGameResID::GOLD))) * towns.size();
|
||||
totalIncome += LOCPLINT->cb->getPlayerState(LOCPLINT->playerID)->valOfBonuses(BonusType::RESOURCES_CONSTANT_BOOST, BonusSubtypeID(GameResID(EGameResID::GOLD))) * playerSettings->handicap.percentIncome / 100;
|
||||
totalIncome += LOCPLINT->cb->getPlayerState(LOCPLINT->playerID)->valOfBonuses(BonusType::RESOURCES_TOWN_MULTIPLYING_BOOST, BonusSubtypeID(GameResID(EGameResID::GOLD))) * towns.size() * playerSettings->handicap.percentIncome / 100;
|
||||
|
||||
for(int i=0; i<7; i++)
|
||||
{
|
||||
|
@ -150,10 +150,11 @@
|
||||
"lobbyRandomMap": "R",
|
||||
"lobbyRandomTown": "T",
|
||||
"lobbyRandomTownVs": "V",
|
||||
"lobbyHandicap": "H",
|
||||
"lobbyReplayVideo": "R",
|
||||
"lobbySaveGame": [ "S", "Return", "Keypad Enter"],
|
||||
"lobbySelectScenario": "S",
|
||||
"lobbyToggleChat": "H",
|
||||
"lobbyToggleChat": "C",
|
||||
"lobbyTurnOptions": "T",
|
||||
"mainMenuBack": [ "B", "Escape" ],
|
||||
"mainMenuCampaign": "C",
|
||||
|
@ -25,7 +25,7 @@
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
PlayerSettings::PlayerSettings()
|
||||
: bonus(PlayerStartingBonus::RANDOM), color(0), handicap(NO_HANDICAP), compOnly(false)
|
||||
: bonus(PlayerStartingBonus::RANDOM), color(0), compOnly(false)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "ExtraOptionsInfo.h"
|
||||
#include "campaign/CampaignConstants.h"
|
||||
#include "serializer/Serializeable.h"
|
||||
#include "ResourceSet.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
@ -65,6 +66,20 @@ enum class PlayerStartingBonus : int8_t
|
||||
RESOURCE = 2
|
||||
};
|
||||
|
||||
struct DLL_LINKAGE Handicap {
|
||||
TResources startBonus = TResources();
|
||||
int percentIncome = 100;
|
||||
int percentGrowth = 100;
|
||||
|
||||
template <typename Handler>
|
||||
void serialize(Handler &h)
|
||||
{
|
||||
h & startBonus;
|
||||
h & percentIncome;
|
||||
h & percentGrowth;
|
||||
}
|
||||
};
|
||||
|
||||
/// Struct which describes the name, the color, the starting bonus of a player
|
||||
struct DLL_LINKAGE PlayerSettings
|
||||
{
|
||||
@ -77,8 +92,8 @@ struct DLL_LINKAGE PlayerSettings
|
||||
|
||||
std::string heroNameTextId;
|
||||
PlayerColor color; //from 0 -
|
||||
enum EHandicap {NO_HANDICAP, MILD, SEVERE};
|
||||
EHandicap handicap;//0-no, 1-mild, 2-severe
|
||||
|
||||
Handicap handicap;
|
||||
|
||||
std::string name;
|
||||
std::set<ui8> connectedPlayerIDs; //Empty - AI, or connectrd player ids
|
||||
@ -92,7 +107,14 @@ struct DLL_LINKAGE PlayerSettings
|
||||
h & heroNameTextId;
|
||||
h & bonus;
|
||||
h & color;
|
||||
h & handicap;
|
||||
if (h.version >= Handler::Version::PLAYER_HANDICAP)
|
||||
h & handicap;
|
||||
else
|
||||
{
|
||||
enum EHandicap {NO_HANDICAP, MILD, SEVERE};
|
||||
EHandicap handicapLegacy = NO_HANDICAP;
|
||||
h & handicapLegacy;
|
||||
}
|
||||
h & name;
|
||||
h & connectedPlayerIDs;
|
||||
h & compOnly;
|
||||
|
@ -387,10 +387,14 @@ void CGameState::initDifficulty()
|
||||
const JsonNode & difficultyAI(config["ai"][GameConstants::DIFFICULTY_NAMES[scenarioOps->difficulty]]);
|
||||
const JsonNode & difficultyHuman(config["human"][GameConstants::DIFFICULTY_NAMES[scenarioOps->difficulty]]);
|
||||
|
||||
auto setDifficulty = [](PlayerState & state, const JsonNode & json)
|
||||
auto setDifficulty = [this](PlayerState & state, const JsonNode & json)
|
||||
{
|
||||
//set starting resources
|
||||
state.resources = TResources(json["resources"]);
|
||||
|
||||
//handicap
|
||||
const PlayerSettings &ps = scenarioOps->getIthPlayersSettings(state.color);
|
||||
state.resources += ps.handicap.startBonus;
|
||||
|
||||
//set global bonuses
|
||||
for(auto & jsonBonus : json["globalBonuses"].Vector())
|
||||
@ -1614,7 +1618,7 @@ struct statsHLP
|
||||
}
|
||||
|
||||
// get total gold income
|
||||
static int getIncome(const PlayerState * ps)
|
||||
static int getIncome(const PlayerState * ps, int percentIncome)
|
||||
{
|
||||
int totalIncome = 0;
|
||||
const CGObjectInstance * heroOrTown = nullptr;
|
||||
@ -1622,7 +1626,7 @@ struct statsHLP
|
||||
//Heroes can produce gold as well - skill, specialty or arts
|
||||
for(const auto & h : ps->heroes)
|
||||
{
|
||||
totalIncome += h->valOfBonuses(Selector::typeSubtype(BonusType::GENERATE_RESOURCE, BonusSubtypeID(GameResID(GameResID::GOLD))));
|
||||
totalIncome += h->valOfBonuses(Selector::typeSubtype(BonusType::GENERATE_RESOURCE, BonusSubtypeID(GameResID(GameResID::GOLD)))) * percentIncome / 100;
|
||||
|
||||
if(!heroOrTown)
|
||||
heroOrTown = h;
|
||||
@ -1657,7 +1661,7 @@ struct statsHLP
|
||||
assert(mine);
|
||||
|
||||
if (mine->producedResource == EGameResID::GOLD)
|
||||
totalIncome += mine->producedQuantity;
|
||||
totalIncome += mine->getProducedQuantity();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1747,7 +1751,7 @@ void CGameState::obtainPlayersStats(SThievesGuildInfo & tgi, int level)
|
||||
}
|
||||
if(level >= 5) //income
|
||||
{
|
||||
FILL_FIELD(income, statsHLP::getIncome(&g->second))
|
||||
FILL_FIELD(income, statsHLP::getIncome(&g->second, scenarioOps->getIthPlayersSettings(g->second.color).handicap.percentIncome))
|
||||
}
|
||||
if(level >= 2) //best hero's stats
|
||||
{
|
||||
|
@ -137,6 +137,14 @@ GrowthInfo CGTownInstance::getGrowthInfo(int level) const
|
||||
const int base = creature->getGrowth();
|
||||
int castleBonus = 0;
|
||||
|
||||
if(tempOwner.isValidPlayer())
|
||||
{
|
||||
auto * playerSettings = cb->getPlayerSettings(tempOwner);
|
||||
ret.handicapPercentage = playerSettings->handicap.percentGrowth;
|
||||
}
|
||||
else
|
||||
ret.handicapPercentage = 100;
|
||||
|
||||
ret.entries.emplace_back(VLC->generaltexth->allTexts[590], base); // \n\nBasic growth %d"
|
||||
|
||||
if (hasBuilt(BuildingID::CASTLE))
|
||||
@ -215,6 +223,11 @@ TResources CGTownInstance::dailyIncome() const
|
||||
ret += p.second->produce;
|
||||
}
|
||||
}
|
||||
|
||||
auto playerSettings = cb->gameState()->scenarioOps->getIthPlayersSettings(getOwner());
|
||||
for(TResources::nziterator it(ret); it.valid(); it++)
|
||||
// always round up income - we don't want to always produce zero if handicap in use
|
||||
ret[it->resType] = (ret[it->resType] * playerSettings.handicap.percentIncome + 99) / 100;
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -1257,7 +1270,8 @@ int GrowthInfo::totalGrowth() const
|
||||
for(const Entry &entry : entries)
|
||||
ret += entry.count;
|
||||
|
||||
return ret;
|
||||
// always round up income - we don't want buildings to always produce zero if handicap in use
|
||||
return (ret * handicapPercentage + 99) / 100;
|
||||
}
|
||||
|
||||
void CGTownInstance::fillUpgradeInfo(UpgradeInfo & info, const CStackInstance &stack) const
|
||||
|
@ -41,6 +41,7 @@ struct DLL_LINKAGE GrowthInfo
|
||||
|
||||
std::vector<Entry> entries;
|
||||
int totalGrowth() const;
|
||||
int handicapPercentage;
|
||||
};
|
||||
|
||||
class DLL_LINKAGE CGTownInstance : public CGDwelling, public IShipyard, public IMarket, public INativeTerrainProvider, public ICreatureUpgrader
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "../gameState/CGameState.h"
|
||||
#include "../mapping/CMap.h"
|
||||
#include "../CPlayerState.h"
|
||||
#include "../StartInfo.h"
|
||||
#include "../serializer/JsonSerializeFormat.h"
|
||||
#include "../mapObjectConstructors/AObjectTypeHandler.h"
|
||||
#include "../mapObjectConstructors/CObjectClassesHandler.h"
|
||||
@ -103,7 +104,7 @@ void CGMine::newTurn(vstd::RNG & rand) const
|
||||
if (tempOwner == PlayerColor::NEUTRAL)
|
||||
return;
|
||||
|
||||
cb->giveResource(tempOwner, producedResource, producedQuantity);
|
||||
cb->giveResource(tempOwner, producedResource, getProducedQuantity());
|
||||
}
|
||||
|
||||
void CGMine::initObj(vstd::RNG & rand)
|
||||
@ -177,7 +178,7 @@ void CGMine::flagMine(const PlayerColor & player) const
|
||||
iw.type = EInfoWindowMode::AUTO;
|
||||
iw.text.appendTextID(TextIdentifier("core.mineevnt", producedResource.getNum()).get()); //not use subID, abandoned mines uses default mine texts
|
||||
iw.player = player;
|
||||
iw.components.emplace_back(ComponentType::RESOURCE_PER_DAY, producedResource, producedQuantity);
|
||||
iw.components.emplace_back(ComponentType::RESOURCE_PER_DAY, producedResource, getProducedQuantity());
|
||||
cb->showInfoDialog(&iw);
|
||||
}
|
||||
|
||||
@ -195,6 +196,13 @@ ui32 CGMine::defaultResProduction() const
|
||||
}
|
||||
}
|
||||
|
||||
ui32 CGMine::getProducedQuantity() const
|
||||
{
|
||||
auto * playerSettings = cb->getPlayerSettings(getOwner());
|
||||
// always round up income - we don't want mines to always produce zero if handicap in use
|
||||
return (producedQuantity * playerSettings->handicap.percentIncome + 99) / 100;
|
||||
}
|
||||
|
||||
void CGMine::battleFinished(const CGHeroInstance *hero, const BattleResult &result) const
|
||||
{
|
||||
if(result.winner == 0) //attacker won
|
||||
|
@ -181,6 +181,7 @@ public:
|
||||
h & abandonedMineResources;
|
||||
}
|
||||
ui32 defaultResProduction() const;
|
||||
ui32 getProducedQuantity() const;
|
||||
|
||||
protected:
|
||||
void serializeJsonOptions(JsonSerializeFormat & handler) override;
|
||||
|
@ -168,6 +168,7 @@ public:
|
||||
virtual void visitLobbyChangePlayerOption(LobbyChangePlayerOption & pack) {}
|
||||
virtual void visitLobbySetPlayer(LobbySetPlayer & pack) {}
|
||||
virtual void visitLobbySetPlayerName(LobbySetPlayerName & pack) {}
|
||||
virtual void visitLobbySetPlayerHandicap(LobbySetPlayerHandicap & pack) {}
|
||||
virtual void visitLobbySetSimturns(LobbySetSimturns & pack) {}
|
||||
virtual void visitLobbySetTurnTime(LobbySetTurnTime & pack) {}
|
||||
virtual void visitLobbySetExtraOptions(LobbySetExtraOptions & pack) {}
|
||||
|
@ -790,6 +790,11 @@ void LobbySetPlayerName::visitTyped(ICPackVisitor & visitor)
|
||||
visitor.visitLobbySetPlayerName(*this);
|
||||
}
|
||||
|
||||
void LobbySetPlayerHandicap::visitTyped(ICPackVisitor & visitor)
|
||||
{
|
||||
visitor.visitLobbySetPlayerHandicap(*this);
|
||||
}
|
||||
|
||||
void LobbySetSimturns::visitTyped(ICPackVisitor & visitor)
|
||||
{
|
||||
visitor.visitLobbySetSimturns(*this);
|
||||
|
@ -285,6 +285,20 @@ struct DLL_LINKAGE LobbySetPlayerName : public CLobbyPackToServer
|
||||
}
|
||||
};
|
||||
|
||||
struct DLL_LINKAGE LobbySetPlayerHandicap : public CLobbyPackToServer
|
||||
{
|
||||
PlayerColor color = PlayerColor::CANNOT_DETERMINE;
|
||||
Handicap handicap = Handicap();
|
||||
|
||||
void visitTyped(ICPackVisitor & visitor) override;
|
||||
|
||||
template <typename Handler> void serialize(Handler &h)
|
||||
{
|
||||
h & color;
|
||||
h & handicap;
|
||||
}
|
||||
};
|
||||
|
||||
struct DLL_LINKAGE LobbySetSimturns : public CLobbyPackToServer
|
||||
{
|
||||
SimturnsInfo simturnsInfo;
|
||||
|
@ -55,6 +55,7 @@ void registerTypesLobbyPacks(Serializer &s)
|
||||
s.template registerType<CLobbyPackToServer, LobbySetCampaignBonus>();
|
||||
s.template registerType<CLobbyPackToServer, LobbySetPlayer>();
|
||||
s.template registerType<CLobbyPackToServer, LobbySetPlayerName>();
|
||||
s.template registerType<CLobbyPackToServer, LobbySetPlayerHandicap>();
|
||||
s.template registerType<CLobbyPackToServer, LobbySetTurnTime>();
|
||||
s.template registerType<CLobbyPackToServer, LobbySetSimturns>();
|
||||
s.template registerType<CLobbyPackToServer, LobbySetDifficulty>();
|
||||
|
@ -58,6 +58,7 @@ enum class ESerializationVersion : int32_t
|
||||
MAP_FORMAT_ADDITIONAL_INFOS, // 848 - serialize new infos in map format
|
||||
REMOVE_LIB_RNG, // 849 - removed random number generators from library classes
|
||||
HIGHSCORE_PARAMETERS, // 850 - saves parameter for campaign
|
||||
PLAYER_HANDICAP, // 851 - player handicap selection at game start
|
||||
|
||||
CURRENT = HIGHSCORE_PARAMETERS
|
||||
CURRENT = PLAYER_HANDICAP
|
||||
};
|
||||
|
@ -367,6 +367,11 @@ void MetaString::appendName(const CreatureID & id, TQuantity count)
|
||||
appendNamePlural(id);
|
||||
}
|
||||
|
||||
void MetaString::appendName(const GameResID& id)
|
||||
{
|
||||
appendTextID(TextIdentifier("core.restypes", id.getNum()).get());
|
||||
}
|
||||
|
||||
void MetaString::appendNameSingular(const CreatureID & id)
|
||||
{
|
||||
appendTextID(id.toEntity(VLC)->getNameSingularTextID());
|
||||
|
@ -80,6 +80,7 @@ public:
|
||||
void appendName(const SpellID& id);
|
||||
void appendName(const PlayerColor& id);
|
||||
void appendName(const CreatureID & id, TQuantity count);
|
||||
void appendName(const GameResID& id);
|
||||
void appendNameSingular(const CreatureID & id);
|
||||
void appendNamePlural(const CreatureID & id);
|
||||
void appendEOL();
|
||||
|
@ -760,6 +760,8 @@ void CGameHandler::onNewTurn()
|
||||
continue;
|
||||
|
||||
assert(elem.first.isValidPlayer());//illegal player number!
|
||||
|
||||
auto playerSettings = gameState()->scenarioOps->getIthPlayersSettings(elem.first);
|
||||
|
||||
std::pair<PlayerColor, si32> playerGold(elem.first, elem.second.resources[EGameResID::GOLD]);
|
||||
hadGold.insert(playerGold);
|
||||
@ -773,8 +775,8 @@ void CGameHandler::onNewTurn()
|
||||
{
|
||||
for (GameResID k = GameResID::WOOD; k < GameResID::COUNT; k++)
|
||||
{
|
||||
n.res[elem.first][k] += elem.second.valOfBonuses(BonusType::RESOURCES_CONSTANT_BOOST, BonusSubtypeID(k));
|
||||
n.res[elem.first][k] += elem.second.valOfBonuses(BonusType::RESOURCES_TOWN_MULTIPLYING_BOOST, BonusSubtypeID(k)) * elem.second.towns.size();
|
||||
n.res[elem.first][k] += elem.second.valOfBonuses(BonusType::RESOURCES_CONSTANT_BOOST, BonusSubtypeID(k)) * playerSettings.handicap.percentIncome / 100;
|
||||
n.res[elem.first][k] += elem.second.valOfBonuses(BonusType::RESOURCES_TOWN_MULTIPLYING_BOOST, BonusSubtypeID(k)) * elem.second.towns.size() * playerSettings.handicap.percentIncome / 100;
|
||||
}
|
||||
|
||||
if(newWeek) //weekly crystal generation if 1 or more crystal dragons in any hero army or town garrison
|
||||
@ -806,7 +808,7 @@ void CGameHandler::onNewTurn()
|
||||
}
|
||||
}
|
||||
if(hasCrystalGenCreature)
|
||||
n.res[elem.first][EGameResID::CRYSTAL] += 3;
|
||||
n.res[elem.first][EGameResID::CRYSTAL] += 3 * playerSettings.handicap.percentIncome / 100;
|
||||
}
|
||||
}
|
||||
|
||||
@ -828,7 +830,7 @@ void CGameHandler::onNewTurn()
|
||||
{
|
||||
for (GameResID k = GameResID::WOOD; k < GameResID::COUNT; k++)
|
||||
{
|
||||
n.res[elem.first][k] += h->valOfBonuses(BonusType::GENERATE_RESOURCE, BonusSubtypeID(k));
|
||||
n.res[elem.first][k] += h->valOfBonuses(BonusType::GENERATE_RESOURCE, BonusSubtypeID(k)) * playerSettings.handicap.percentIncome / 100;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -624,8 +624,6 @@ void CVCMIServer::updateStartInfoOnMapChange(std::shared_ptr<CMapInfo> mapInfo,
|
||||
pset.heroNameTextId = pinfo.mainCustomHeroNameTextId;
|
||||
pset.heroPortrait = pinfo.mainCustomHeroPortrait;
|
||||
}
|
||||
|
||||
pset.handicap = PlayerSettings::NO_HANDICAP;
|
||||
}
|
||||
|
||||
if(mi->isRandomMap && mapGenOpts)
|
||||
@ -765,6 +763,60 @@ void CVCMIServer::setPlayerName(PlayerColor color, std::string name)
|
||||
setPlayerConnectedId(player, nameID);
|
||||
}
|
||||
|
||||
void CVCMIServer::setPlayerHandicap(PlayerColor color, Handicap handicap)
|
||||
{
|
||||
if(color == PlayerColor::CANNOT_DETERMINE)
|
||||
return;
|
||||
|
||||
si->playerInfos[color].handicap = handicap;
|
||||
|
||||
int humanPlayer = 0;
|
||||
for (const auto & pi : si->playerInfos)
|
||||
if(pi.second.isControlledByHuman())
|
||||
humanPlayer++;
|
||||
|
||||
if(humanPlayer < 2) // Singleplayer
|
||||
return;
|
||||
|
||||
MetaString str;
|
||||
str.appendTextID("vcmi.lobby.handicap");
|
||||
str.appendRawString(" ");
|
||||
str.appendName(color);
|
||||
str.appendRawString(":");
|
||||
|
||||
if(handicap.startBonus.empty() && handicap.percentIncome == 100 && handicap.percentGrowth == 100)
|
||||
{
|
||||
str.appendRawString(" ");
|
||||
str.appendTextID("core.genrltxt.523");
|
||||
announceTxt(str);
|
||||
return;
|
||||
}
|
||||
|
||||
for(auto & res : EGameResID::ALL_RESOURCES())
|
||||
if(handicap.startBonus[res] != 0)
|
||||
{
|
||||
str.appendRawString(" ");
|
||||
str.appendName(res);
|
||||
str.appendRawString(":");
|
||||
str.appendRawString(std::to_string(handicap.startBonus[res]));
|
||||
}
|
||||
if(handicap.percentIncome != 100)
|
||||
{
|
||||
str.appendRawString(" ");
|
||||
str.appendTextID("core.jktext.32");
|
||||
str.appendRawString(":");
|
||||
str.appendRawString(std::to_string(handicap.percentIncome) + "%");
|
||||
}
|
||||
if(handicap.percentGrowth != 100)
|
||||
{
|
||||
str.appendRawString(" ");
|
||||
str.appendTextID("core.genrltxt.194");
|
||||
str.appendRawString(":");
|
||||
str.appendRawString(std::to_string(handicap.percentGrowth) + "%");
|
||||
}
|
||||
announceTxt(str);
|
||||
}
|
||||
|
||||
void CVCMIServer::optionNextCastle(PlayerColor player, int dir)
|
||||
{
|
||||
PlayerSettings & s = si->playerInfos[player];
|
||||
@ -1011,6 +1063,39 @@ void CVCMIServer::multiplayerWelcomeMessage()
|
||||
|
||||
gh->playerMessages->broadcastSystemMessage("Use '!help' to list available commands");
|
||||
|
||||
for (const auto & pi : si->playerInfos)
|
||||
if(!pi.second.handicap.startBonus.empty() || pi.second.handicap.percentIncome != 100 || pi.second.handicap.percentGrowth != 100)
|
||||
{
|
||||
MetaString str;
|
||||
str.appendTextID("vcmi.lobby.handicap");
|
||||
str.appendRawString(" ");
|
||||
str.appendName(pi.first);
|
||||
str.appendRawString(":");
|
||||
for(auto & res : EGameResID::ALL_RESOURCES())
|
||||
if(pi.second.handicap.startBonus[res] != 0)
|
||||
{
|
||||
str.appendRawString(" ");
|
||||
str.appendName(res);
|
||||
str.appendRawString(":");
|
||||
str.appendRawString(std::to_string(pi.second.handicap.startBonus[res]));
|
||||
}
|
||||
if(pi.second.handicap.percentIncome != 100)
|
||||
{
|
||||
str.appendRawString(" ");
|
||||
str.appendTextID("core.jktext.32");
|
||||
str.appendRawString(":");
|
||||
str.appendRawString(std::to_string(pi.second.handicap.percentIncome) + "%");
|
||||
}
|
||||
if(pi.second.handicap.percentGrowth != 100)
|
||||
{
|
||||
str.appendRawString(" ");
|
||||
str.appendTextID("core.genrltxt.194");
|
||||
str.appendRawString(":");
|
||||
str.appendRawString(std::to_string(pi.second.handicap.percentGrowth) + "%");
|
||||
}
|
||||
gh->playerMessages->broadcastSystemMessage(str);
|
||||
}
|
||||
|
||||
std::vector<std::string> optionIds;
|
||||
if(si->extraOptionsInfo.cheatsAllowed)
|
||||
optionIds.emplace_back("vcmi.optionsTab.cheatAllowed.hover");
|
||||
|
@ -117,6 +117,7 @@ public:
|
||||
// Work with LobbyInfo
|
||||
void setPlayer(PlayerColor clickedColor);
|
||||
void setPlayerName(PlayerColor player, std::string name);
|
||||
void setPlayerHandicap(PlayerColor player, Handicap handicap);
|
||||
void optionNextHero(PlayerColor player, int dir); //dir == -1 or +1
|
||||
void optionSetHero(PlayerColor player, HeroTypeID id);
|
||||
HeroTypeID nextAllowedHero(PlayerColor player, HeroTypeID id, int direction);
|
||||
|
@ -89,6 +89,7 @@ public:
|
||||
void visitLobbyChangePlayerOption(LobbyChangePlayerOption & pack) override;
|
||||
void visitLobbySetPlayer(LobbySetPlayer & pack) override;
|
||||
void visitLobbySetPlayerName(LobbySetPlayerName & pack) override;
|
||||
void visitLobbySetPlayerHandicap(LobbySetPlayerHandicap & pack) override;
|
||||
void visitLobbySetTurnTime(LobbySetTurnTime & pack) override;
|
||||
void visitLobbySetExtraOptions(LobbySetExtraOptions & pack) override;
|
||||
void visitLobbySetSimturns(LobbySetSimturns & pack) override;
|
||||
|
@ -346,6 +346,12 @@ void ApplyOnServerNetPackVisitor::visitLobbySetPlayerName(LobbySetPlayerName & p
|
||||
result = true;
|
||||
}
|
||||
|
||||
void ApplyOnServerNetPackVisitor::visitLobbySetPlayerHandicap(LobbySetPlayerHandicap & pack)
|
||||
{
|
||||
srv.setPlayerHandicap(pack.color, pack.handicap);
|
||||
result = true;
|
||||
}
|
||||
|
||||
void ApplyOnServerNetPackVisitor::visitLobbySetSimturns(LobbySetSimturns & pack)
|
||||
{
|
||||
srv.si->simturnsInfo = pack.simturnsInfo;
|
||||
|
@ -175,8 +175,6 @@ public:
|
||||
pset.heroNameTextId = pinfo.mainCustomHeroNameTextId;
|
||||
pset.heroPortrait = HeroTypeID(pinfo.mainCustomHeroPortrait);
|
||||
}
|
||||
|
||||
pset.handicap = PlayerSettings::NO_HANDICAP;
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user