1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-11-28 08:48:48 +02:00

Merge remote-tracking branch 'vcmi/develop' into battle_improvements

This commit is contained in:
Ivan Savenko 2023-01-04 16:20:53 +02:00
commit d3ecd43aba
15 changed files with 515 additions and 93 deletions

3
.gitignore vendored
View File

@ -60,3 +60,6 @@ CMakeUserPresets.json
/AI/FuzzyLite.lib
/deps
.vs/
# CLion
.idea/

View File

@ -737,7 +737,30 @@ void BattleActionsController::activateStack()
{
const CStack * s = owner.stacksController->getActiveStack();
if(s)
{
possibleActions = getPossibleActionsForStack(s);
std::list<PossiblePlayerBattleAction> actionsToSelect;
if(!possibleActions.empty())
{
switch(possibleActions.front())
{
case PossiblePlayerBattleAction::SHOOT:
actionsToSelect.push_back(possibleActions.front());
actionsToSelect.push_back(PossiblePlayerBattleAction::ATTACK);
break;
case PossiblePlayerBattleAction::ATTACK_AND_RETURN:
actionsToSelect.push_back(possibleActions.front());
actionsToSelect.push_back(PossiblePlayerBattleAction::WALK_AND_ATTACK);
break;
case PossiblePlayerBattleAction::AIMED_SPELL_CREATURE:
actionsToSelect.push_back(possibleActions.front());
break;
}
}
owner.windowObject->setAlternativeActions(actionsToSelect);
}
}
bool BattleActionsController::spellcastingModeActive() const
@ -751,3 +774,18 @@ SpellID BattleActionsController::selectedSpell() const
return SpellID::NONE;
return SpellID(spellToCast->actionSubtype);
}
const std::vector<PossiblePlayerBattleAction> & BattleActionsController::getPossibleActions() const
{
return possibleActions;
}
void BattleActionsController::removePossibleAction(PossiblePlayerBattleAction action)
{
vstd::erase(possibleActions, action);
}
void BattleActionsController::pushFrontPossibleAction(PossiblePlayerBattleAction action)
{
possibleActions.insert(possibleActions.begin(), action);
}

View File

@ -92,5 +92,12 @@ public:
/// returns true if UI is currently in target selection mode
bool spellcastingModeActive() const;
/// methods to work with array of possible actions, needed to control special creatures abilities
const std::vector<PossiblePlayerBattleAction> & getPossibleActions() const;
void removePossibleAction(PossiblePlayerBattleAction);
/// inserts possible action in the beggining in order to prioritize it
void pushFrontPossibleAction(PossiblePlayerBattleAction);
};

View File

@ -23,6 +23,7 @@
#include "../gui/Canvas.h"
#include "../gui/CCursorHandler.h"
#include "../gui/CGuiHandler.h"
#include "../gui/CAnimation.h"
#include "../windows/CSpellWindow.h"
#include "../widgets/AdventureMapClasses.h"
#include "../widgets/Buttons.h"
@ -33,6 +34,7 @@
#include "../../lib/mapObjects/CGHeroInstance.h"
#include "../../lib/CStack.h"
#include "../../lib/CConfigHandler.h"
#include "../../lib/filesystem/ResourceID.h"
BattleWindow::BattleWindow(BattleInterface & owner):
owner(owner)
@ -42,31 +44,27 @@ BattleWindow::BattleWindow(BattleInterface & owner):
pos.h = 600;
pos = center();
// create background for panel/ribbon at the bottom
menuTactics = std::make_shared<CPicture>("COPLACBR.BMP", 0, 556);
menuBattle = std::make_shared<CPicture>("CBAR.BMP", 0, 556);
menuTactics->colorize(owner.curInt->playerID);
menuBattle->colorize(owner.curInt->playerID);
REGISTER_BUILDER("battleConsole", &BattleWindow::buildBattleConsole);
const JsonNode config(ResourceID("config/widgets/BattleWindow.json"));
addCallback("options", std::bind(&BattleWindow::bOptionsf, this));
addCallback("surrender", std::bind(&BattleWindow::bSurrenderf, this));
addCallback("flee", std::bind(&BattleWindow::bFleef, this));
addCallback("autofight", std::bind(&BattleWindow::bAutofightf, this));
addCallback("spellbook", std::bind(&BattleWindow::bSpellf, this));
addCallback("wait", std::bind(&BattleWindow::bWaitf, this));
addCallback("defence", std::bind(&BattleWindow::bDefencef, this));
addCallback("consoleUp", std::bind(&BattleWindow::bConsoleUpf, this));
addCallback("consoleDown", std::bind(&BattleWindow::bConsoleDownf, this));
addCallback("tacticNext", std::bind(&BattleWindow::bTacticNextStack, this));
addCallback("tacticEnd", std::bind(&BattleWindow::bTacticPhaseEnd, this));
addCallback("alternativeAction", std::bind(&BattleWindow::bSwitchActionf, this));
build(config);
console = widget<BattleConsole>("console");
//preparing buttons and console
bOptions = std::make_shared<CButton> (Point( 3, 5 + 556), "icm003.def", CGI->generaltexth->zelp[381], std::bind(&BattleWindow::bOptionsf,this), SDLK_o);
bSurrender = std::make_shared<CButton> (Point( 54, 5 + 556), "icm001.def", CGI->generaltexth->zelp[379], std::bind(&BattleWindow::bSurrenderf,this), SDLK_s);
bFlee = std::make_shared<CButton> (Point(105, 5 + 556), "icm002.def", CGI->generaltexth->zelp[380], std::bind(&BattleWindow::bFleef,this), SDLK_r);
bAutofight = std::make_shared<CButton> (Point(158, 5 + 556), "icm004.def", CGI->generaltexth->zelp[382], std::bind(&BattleWindow::bAutofightf,this), SDLK_a);
bSpell = std::make_shared<CButton> (Point(645, 5 + 556), "icm005.def", CGI->generaltexth->zelp[385], std::bind(&BattleWindow::bSpellf,this), SDLK_c);
bWait = std::make_shared<CButton> (Point(696, 5 + 556), "icm006.def", CGI->generaltexth->zelp[386], std::bind(&BattleWindow::bWaitf,this), SDLK_w);
bDefence = std::make_shared<CButton> (Point(747, 5 + 556), "icm007.def", CGI->generaltexth->zelp[387], std::bind(&BattleWindow::bDefencef,this), SDLK_d);
bConsoleUp = std::make_shared<CButton> (Point(624, 5 + 556), "ComSlide.def", std::make_pair("", ""), std::bind(&BattleWindow::bConsoleUpf,this), SDLK_UP);
bConsoleDown = std::make_shared<CButton>(Point(624, 24 + 556), "ComSlide.def", std::make_pair("", ""), std::bind(&BattleWindow::bConsoleDownf,this), SDLK_DOWN);
bDefence->assignedKeys.insert(SDLK_SPACE);
bConsoleUp->setImageOrder(0, 1, 0, 0);
bConsoleDown->setImageOrder(2, 3, 2, 2);
btactNext = std::make_shared<CButton>(Point(213, 4 + 556), "icm011.def", std::make_pair("", ""), [&]() { bTacticNextStack();}, SDLK_SPACE);
btactEnd = std::make_shared<CButton>(Point(419, 4 + 556), "icm012.def", std::make_pair("", ""), [&](){ bTacticPhaseEnd();}, SDLK_RETURN);
console = std::make_shared<BattleConsole>(menuBattle, Point(211, 4 + 556), Point(211, 4), Point(406,38));
GH.statusbar = console;
owner.console = console;
@ -104,6 +102,14 @@ BattleWindow::~BattleWindow()
CPlayerInterface::battleInt = nullptr;
}
std::shared_ptr<BattleConsole> BattleWindow::buildBattleConsole(const JsonNode & config) const
{
auto rect = readRect(config["rect"]);
auto offset = readPosition(config["imagePosition"]);
auto background = widget<CPicture>("menuBattle");
return std::make_shared<BattleConsole>(background, rect.topLeft(), offset, rect.dimensions() );
}
void BattleWindow::hideQueue()
{
Settings showQueue = settings.write["battle"]["showQueue"];
@ -181,22 +187,34 @@ void BattleWindow::clickRight(tribool down, bool previousState)
void BattleWindow::tacticPhaseStarted()
{
auto menuBattle = widget<CIntObject>("menuBattle");
auto console = widget<CIntObject>("console");
auto menuTactics = widget<CIntObject>("menuTactics");
auto tacticNext = widget<CIntObject>("tacticNext");
auto tacticEnd = widget<CIntObject>("tacticEnd");
menuBattle->disable();
console->disable();
menuTactics->enable();
btactNext->enable();
btactEnd->enable();
tacticNext->enable();
tacticEnd->enable();
}
void BattleWindow::tacticPhaseEnded()
{
auto menuBattle = widget<CIntObject>("menuBattle");
auto console = widget<CIntObject>("console");
auto menuTactics = widget<CIntObject>("menuTactics");
auto tacticNext = widget<CIntObject>("tacticNext");
auto tacticEnd = widget<CIntObject>("tacticEnd");
menuBattle->enable();
console->enable();
menuTactics->disable();
btactNext->disable();
btactEnd->disable();
tacticNext->disable();
tacticEnd->disable();
}
void BattleWindow::bOptionsf()
@ -277,6 +295,57 @@ void BattleWindow::reallySurrender()
}
}
void BattleWindow::showAlternativeActionIcon(PossiblePlayerBattleAction action)
{
auto w = widget<CButton>("alternativeAction");
if(!w)
return;
std::string iconName = variables["actionIconDefault"].String();
switch(action)
{
case PossiblePlayerBattleAction::ATTACK:
iconName = variables["actionIconAttack"].String();
break;
case PossiblePlayerBattleAction::SHOOT:
iconName = variables["actionIconShoot"].String();
break;
case PossiblePlayerBattleAction::AIMED_SPELL_CREATURE:
iconName = variables["actionIconSpell"].String();
break;
//TODO: figure out purpose of this icon
//case PossiblePlayerBattleAction::???:
//iconName = variables["actionIconWalk"].String();
//break;
case PossiblePlayerBattleAction::ATTACK_AND_RETURN:
iconName = variables["actionIconReturn"].String();
break;
case PossiblePlayerBattleAction::WALK_AND_ATTACK:
iconName = variables["actionIconNoReturn"].String();
break;
}
auto anim = std::make_shared<CAnimation>(iconName);
w->setImage(anim, false);
}
void BattleWindow::setAlternativeActions(const std::list<PossiblePlayerBattleAction> & actions)
{
alternativeActions = actions;
defaultAction = PossiblePlayerBattleAction::INVALID;
if(alternativeActions.size() > 1)
defaultAction = alternativeActions.back();
if(!alternativeActions.empty())
showAlternativeActionIcon(alternativeActions.front());
else
showAlternativeActionIcon(defaultAction);
}
void BattleWindow::bAutofightf()
{
if (owner.actionsController->spellcastingModeActive())
@ -346,6 +415,33 @@ void BattleWindow::bSpellf()
}
}
void BattleWindow::bSwitchActionf()
{
if(alternativeActions.empty())
return;
if(alternativeActions.front() == defaultAction)
{
alternativeActions.push_back(alternativeActions.front());
alternativeActions.pop_front();
}
auto actions = owner.actionsController->getPossibleActions();
if(!actions.empty() && actions.front() == alternativeActions.front())
{
owner.actionsController->removePossibleAction(alternativeActions.front());
showAlternativeActionIcon(defaultAction);
}
else
{
owner.actionsController->pushFrontPossibleAction(alternativeActions.front());
showAlternativeActionIcon(alternativeActions.front());
}
alternativeActions.push_back(alternativeActions.front());
alternativeActions.pop_front();
}
void BattleWindow::bWaitf()
{
if (owner.actionsController->spellcastingModeActive())
@ -405,29 +501,43 @@ void BattleWindow::blockUI(bool on)
bool canWait = owner.stacksController->getActiveStack() ? !owner.stacksController->getActiveStack()->waitedThisTurn : false;
bOptions->block(on);
bFlee->block(on || !owner.curInt->cb->battleCanFlee());
bSurrender->block(on || owner.curInt->cb->battleGetSurrenderCost() < 0);
if(auto w = widget<CButton>("options"))
w->block(on);
if(auto w = widget<CButton>("flee"))
w->block(on || !owner.curInt->cb->battleCanFlee());
if(auto w = widget<CButton>("surrender"))
w->block(on || owner.curInt->cb->battleGetSurrenderCost() < 0);
if(auto w = widget<CButton>("cast"))
w->block(on || owner.tacticsMode || !canCastSpells);
if(auto w = widget<CButton>("wait"))
w->block(on || owner.tacticsMode || !canWait);
if(auto w = widget<CButton>("defence"))
w->block(on || owner.tacticsMode);
if(auto w = widget<CButton>("alternativeAction"))
w->block(on || owner.tacticsMode);
// block only if during enemy turn and auto-fight is off
// otherwise - crash on accessing non-exisiting active stack
bAutofight->block(!owner.curInt->isAutoFightOn && !owner.stacksController->getActiveStack());
if(auto w = widget<CButton>("options"))
w->block(!owner.curInt->isAutoFightOn && !owner.stacksController->getActiveStack());
if (owner.tacticsMode && btactEnd && btactNext)
auto btactEnd = widget<CButton>("tacticEnd");
auto btactNext = widget<CButton>("tacticNext");
if(owner.tacticsMode && btactEnd && btactNext)
{
btactNext->block(on);
btactEnd->block(on);
}
else
{
bConsoleUp->block(on);
bConsoleDown->block(on);
auto bConsoleUp = widget<CButton>("consoleUp");
auto bConsoleDown = widget<CButton>("consoleDown");
if(bConsoleUp && bConsoleDown)
{
bConsoleUp->block(on);
bConsoleDown->block(on);
}
}
bSpell->block(on || owner.tacticsMode || !canCastSpells);
bWait->block(on || owner.tacticsMode || !canWait);
bDefence->block(on || owner.tacticsMode);
}
void BattleWindow::showAll(SDL_Surface *to)
@ -443,3 +553,10 @@ void BattleWindow::show(SDL_Surface *to)
CIntObject::show(to);
LOCPLINT->cingconsole->show(to);
}
void BattleWindow::close()
{
if(GH.topInt().get() != this)
logGlobal->error("Only top interface must be closed");
GH.popInts(1);
}

View File

@ -10,6 +10,8 @@
#pragma once
#include "../gui/CIntObject.h"
#include "../gui/InterfaceObjectConfigurable.h"
#include "../../lib/battle/CBattleInfoCallback.h"
VCMI_LIB_NAMESPACE_BEGIN
class CStack;
@ -23,29 +25,13 @@ class BattleRenderer;
class StackQueue;
/// GUI object that handles functionality of panel at the bottom of combat screen
class BattleWindow : public WindowBase
class BattleWindow : public InterfaceObjectConfigurable
{
BattleInterface & owner;
std::shared_ptr<StackQueue> queue;
std::shared_ptr<BattleConsole> console;
std::shared_ptr<CPicture> menuTactics;
std::shared_ptr<CPicture> menuBattle;
std::shared_ptr<CButton> bOptions;
std::shared_ptr<CButton> bSurrender;
std::shared_ptr<CButton> bFlee;
std::shared_ptr<CButton> bAutofight;
std::shared_ptr<CButton> bSpell;
std::shared_ptr<CButton> bWait;
std::shared_ptr<CButton> bDefence;
std::shared_ptr<CButton> bConsoleUp;
std::shared_ptr<CButton> bConsoleDown;
std::shared_ptr<CButton> btactNext;
std::shared_ptr<CButton> btactEnd;
/// button press handling functions
void bOptionsf();
void bSurrenderf();
@ -53,6 +39,7 @@ class BattleWindow : public WindowBase
void bAutofightf();
void bSpellf();
void bWaitf();
void bSwitchActionf();
void bDefencef();
void bConsoleUpf();
void bConsoleDownf();
@ -62,17 +49,24 @@ class BattleWindow : public WindowBase
/// functions for handling actions after they were confirmed by popup window
void reallyFlee();
void reallySurrender();
/// management of alternative actions
std::list<PossiblePlayerBattleAction> alternativeActions;
PossiblePlayerBattleAction defaultAction;
void showAlternativeActionIcon(PossiblePlayerBattleAction);
/// Toggle StackQueue visibility
void hideQueue();
void showQueue();
std::shared_ptr<BattleConsole> buildBattleConsole(const JsonNode &) const;
public:
BattleWindow(BattleInterface & owner );
~BattleWindow();
/// Closes window once battle finished (explicit declaration to move into public visibility)
using WindowBase::close;
/// Closes window once battle finished
void close();
/// block all UI elements when player is not allowed to act, e.g. during enemy turn
void blockUI(bool on);
@ -92,5 +86,9 @@ public:
/// Toggle UI to displaying battle log in place of tactics UI
void tacticPhaseEnded();
/// Set possible alternative options. If more than 1 - the last will be considered as default option
void setAlternativeActions(const std::list<PossiblePlayerBattleAction> &);
};

View File

@ -25,11 +25,20 @@
#include "../../lib/CGeneralTextHandler.h"
static std::map<std::string, int> KeycodeMap{
{"up", SDLK_UP},
{"down", SDLK_DOWN},
{"left", SDLK_LEFT},
{"right", SDLK_RIGHT},
{"space", SDLK_SPACE},
{"enter", SDLK_RETURN}
};
InterfaceObjectConfigurable::InterfaceObjectConfigurable(const JsonNode & config, int used, Point offset):
InterfaceObjectConfigurable(used, offset)
{
init(config);
build(config);
}
InterfaceObjectConfigurable::InterfaceObjectConfigurable(int used, Point offset):
@ -57,19 +66,32 @@ void InterfaceObjectConfigurable::addCallback(const std::string & callbackName,
callbacks[callbackName] = callback;
}
void InterfaceObjectConfigurable::init(const JsonNode &config)
void InterfaceObjectConfigurable::deleteWidget(const std::string & name)
{
auto iter = widgets.find(name);
if(iter != widgets.end())
widgets.erase(iter);
}
void InterfaceObjectConfigurable::build(const JsonNode &config)
{
OBJ_CONSTRUCTION;
logGlobal->debug("Building configurable interface object");
for(auto & item : config["variables"].Struct())
auto * items = &config;
if(config.getType() == JsonNode::JsonType::DATA_STRUCT)
{
logGlobal->debug("Read variable named %s", item.first);
variables[item.first] = item.second;
for(auto & item : config["variables"].Struct())
{
logGlobal->debug("Read variable named %s", item.first);
variables[item.first] = item.second;
}
items = &config["items"];
}
int unnamedObjectId = 0;
const std::string unnamedObjectPrefix = "__widget_";
for(const auto & item : config["items"].Vector())
for(const auto & item : items->Vector())
{
std::string name = item["name"].isNull()
? unnamedObjectPrefix + std::to_string(unnamedObjectId++)
@ -210,6 +232,23 @@ std::pair<std::string, std::string> InterfaceObjectConfigurable::readHintText(co
return result;
}
int InterfaceObjectConfigurable::readKeycode(const JsonNode & config) const
{
logGlobal->debug("Reading keycode");
if(config.getType() == JsonNode::JsonType::DATA_INTEGER)
return config.Integer();
if(config.getType() == JsonNode::JsonType::DATA_STRING)
{
auto s = config.String();
if(s.size() == 1) //keyboard symbol
return s[0];
return KeycodeMap[s];
}
return 0;
}
std::shared_ptr<CPicture> InterfaceObjectConfigurable::buildPicture(const JsonNode & config) const
{
logGlobal->debug("Building widget CPicture");
@ -289,8 +328,24 @@ std::shared_ptr<CButton> InterfaceObjectConfigurable::buildButton(const JsonNode
button->addOverlay(buildWidget(item));
}
}
if(!config["imageOrder"].isNull())
{
auto imgOrder = config["imageOrder"].Vector();
assert(imgOrder.size() >= 4);
button->setImageOrder(imgOrder[0].Integer(), imgOrder[1].Integer(), imgOrder[2].Integer(), imgOrder[3].Integer());
}
if(!config["callback"].isNull())
button->addCallback(std::bind(callbacks.at(config["callback"].String()), 0));
if(!config["hotkey"].isNull())
{
if(config["hotkey"].getType() == JsonNode::JsonType::DATA_VECTOR)
{
for(auto k : config["hotkey"].Vector())
button->assignedKeys.insert(readKeycode(k));
}
else
button->assignedKeys.insert(readKeycode(config["hotkey"]));
}
return button;
}

View File

@ -39,7 +39,7 @@ protected:
void registerBuilder(const std::string &, BuilderFunction);
//must be called after adding callbacks
void init(const JsonNode & config);
void build(const JsonNode & config);
void addCallback(const std::string & callbackName, std::function<void(int)> callback);
JsonNode variables;
@ -52,6 +52,8 @@ protected:
return nullptr;
return std::dynamic_pointer_cast<T>(iter->second);
}
void deleteWidget(const std::string & name);
//basic serializers
Point readPosition(const JsonNode &) const;
@ -61,6 +63,7 @@ protected:
EFonts readFont(const JsonNode &) const;
std::string readText(const JsonNode &) const;
std::pair<std::string, std::string> readHintText(const JsonNode &) const;
int readKeycode(const JsonNode &) const;
//basic widgets
std::shared_ptr<CPicture> buildPicture(const JsonNode &) const;
@ -79,6 +82,7 @@ protected:
private:
int unnamedObjectId = 0;
std::map<std::string, BuilderFunction> builders;
std::map<std::string, std::shared_ptr<CIntObject>> widgets;
std::map<std::string, std::function<void(int)>> callbacks;

View File

@ -119,7 +119,7 @@ RandomMapTab::RandomMapTab():
}
init(config);
build(config);
updateMapInfoByHost();
}
@ -338,7 +338,7 @@ TemplatesDropBox::ListItem::ListItem(const JsonNode & config, TemplatesDropBox &
{
OBJ_CONSTRUCTION;
init(config);
build(config);
if(auto w = widget<CPicture>("hoverImage"))
{
@ -412,7 +412,7 @@ TemplatesDropBox::TemplatesDropBox(RandomMapTab & randomMapTab, int3 size):
OBJ_CONSTRUCTION;
pos = randomMapTab.pos;
init(config);
build(config);
if(auto w = widget<CSlider>("slider"))
{
@ -522,7 +522,7 @@ TeamAlignmentsWidget::TeamAlignmentsWidget(RandomMapTab & randomMapTab):
GH.popInt(GH.topInt());
});
init(config);
build(config);
center(pos);

View File

@ -0,0 +1,135 @@
{
"items":
[
{
"type": "picture",
"name": "menuTactics",
"position": {"x": 0, "y": 556},
"image": "COPLACBR.bmp"
},
{
"type": "picture",
"name": "menuBattle",
"position": {"x": 0, "y": 556},
"image": "CBAR.bmp"
},
{
"type": "button",
"name": "options",
"position": {"x": 3, "y": 561},
"image": "icm003",
"zelp": 381,
"callback": "options",
"hotkey": "o"
},
{
"type": "button",
"name": "surrender",
"position": {"x": 54, "y": 561},
"image": "icm001",
"zelp": 379,
"callback": "surrender",
"hotkey": "s"
},
{
"type": "button",
"name": "flee",
"position": {"x": 105, "y": 561},
"image": "icm002",
"zelp": 380,
"callback": "flee",
"hotkey": "r"
},
{
"type": "button",
"name": "autofight",
"position": {"x": 157, "y": 561},
"image": "icm004",
"zelp": 382,
"callback": "autofight",
"hotkey": "a"
},
{
"type": "button",
"name": "cast",
"position": {"x": 645, "y": 561},
"image": "icm005",
"zelp": 385,
"callback": "spellbook",
"hotkey": "c"
},
{
"type": "button",
"name": "wait",
"position": {"x": 696, "y": 561},
"image": "icm006",
"zelp": 386,
"callback": "wait",
"hotkey": "w"
},
{
"type": "button",
"name": "defence",
"position": {"x": 747, "y": 561},
"image": "icm007",
"zelp": 387,
"callback": "defence",
"hotkey": ["d", "space"]
},
{
"type": "button",
"name": "consoleUp",
"position": {"x": 624, "y": 561},
"image": "ComSlide",
"zelp": "",
"callback": "consoleUp",
"imageOrder": [0, 1, 0, 0],
"hotkey": "up"
},
{
"type": "button",
"name": "consoleDown",
"position": {"x": 624, "y": 580},
"image": "ComSlide",
"zelp": "",
"callback": "consoleDown",
"imageOrder": [2, 3, 2, 2],
"hotkey": "down"
},
{
"type": "battleConsole",
"name": "console",
"imagePosition" : { "x" : 211, "y" : 4 },
"rect": {"x": 211, "y": 561, "w": 406, "h": 38}
},
{
"type": "button",
"name": "tacticNext",
"position": {"x": 213, "y": 561},
"image": "icm011",
"zelp": "",
"callback": "tacticNext",
"hotkey": "space"
},
{
"type": "button",
"name": "tacticEnd",
"position": {"x": 419, "y": 561},
"image": "icm012",
"zelp": "",
"callback": "tacticEnd",
"hotkey": "enter"
}
]
}

2
debian/control vendored
View File

@ -2,7 +2,7 @@ Source: vcmi
Section: games
Priority: optional
Maintainer: Ivan Savenko <saven.ivan@gmail.com>
Build-Depends: debhelper (>= 8), cmake, libsdl2-dev, libsdl2-image-dev, libsdl2-ttf-dev, libsdl2-mixer-dev, zlib1g-dev, libavformat-dev, libswscale-dev, libboost-dev (>=1.48), libboost-program-options-dev (>=1.48), libboost-filesystem-dev (>=1.48), libboost-system-dev (>=1.48), libboost-locale-dev (>=1.48), libboost-thread-dev (>=1.48), qtbase5-dev, libtbb-dev, libfuzzylite-dev, libminizip-dev, libluajit-5.1-dev
Build-Depends: debhelper (>= 8), cmake, libsdl2-dev, libsdl2-image-dev, libsdl2-ttf-dev, libsdl2-mixer-dev, zlib1g-dev, libavformat-dev, libswscale-dev, libboost-dev (>=1.48), libboost-program-options-dev (>=1.48), libboost-filesystem-dev (>=1.48), libboost-system-dev (>=1.48), libboost-locale-dev (>=1.48), libboost-thread-dev (>=1.48), qtbase5-dev, libtbb2-dev, libfuzzylite-dev, libminizip-dev, libluajit-5.1-dev
Standards-Version: 3.9.1
Homepage: http://vcmi.eu
Vcs-Git: git://github.com/vcmi/vcmi.git

View File

@ -67,7 +67,7 @@ void CPrivilegedInfoCallback::getTilesInRange(std::unordered_set<int3, ShashInt3
return;
}
if(radious == CBuilding::HEIGHT_SKYSHIP) //reveal entire map
getAllTiles (tiles, player, -1, 0);
getAllTiles (tiles, player, -1, MapTerrainFilterMode::NONE);
else
{
const TeamState * team = !player ? nullptr : gs->getPlayerTeam(*player);
@ -91,15 +91,13 @@ void CPrivilegedInfoCallback::getTilesInRange(std::unordered_set<int3, ShashInt3
}
}
void CPrivilegedInfoCallback::getAllTiles(std::unordered_set<int3, ShashInt3> & tiles, boost::optional<PlayerColor> Player, int level, int surface) const
void CPrivilegedInfoCallback::getAllTiles(std::unordered_set<int3, ShashInt3> & tiles, boost::optional<PlayerColor> Player, int level, MapTerrainFilterMode tileFilterMode) const
{
if(!!Player && *Player >= PlayerColor::PLAYER_LIMIT)
{
logGlobal->error("Illegal call to getAllTiles !");
return;
}
bool water = surface == 0 || surface == 2,
land = surface == 0 || surface == 1;
std::vector<int> floors;
if(level == -1)
@ -112,16 +110,35 @@ void CPrivilegedInfoCallback::getAllTiles(std::unordered_set<int3, ShashInt3> &
else
floors.push_back(level);
for (auto zd : floors)
for(auto zd: floors)
{
for (int xd = 0; xd < gs->map->width; xd++)
for(int xd = 0; xd < gs->map->width; xd++)
{
for (int yd = 0; yd < gs->map->height; yd++)
for(int yd = 0; yd < gs->map->height; yd++)
{
if ((getTile (int3 (xd,yd,zd))->terType->isWater() && water)
|| (getTile (int3 (xd,yd,zd))->terType->isLand() && land))
tiles.insert(int3(xd,yd,zd));
bool isTileEligible = false;
switch(tileFilterMode)
{
case MapTerrainFilterMode::NONE:
isTileEligible = true;
break;
case MapTerrainFilterMode::WATER:
isTileEligible = getTile(int3(xd, yd, zd))->terType->isWater();
break;
case MapTerrainFilterMode::LAND:
isTileEligible = getTile(int3(xd, yd, zd))->terType->isLand();
break;
case MapTerrainFilterMode::LAND_CARTOGRAPHER:
isTileEligible = getTile(int3(xd, yd, zd))->terType->isSurfaceCartographerCompatible();
break;
case MapTerrainFilterMode::UNDERGROUND_CARTOGRAPHER:
isTileEligible = getTile(int3(xd, yd, zd))->terType->isUndergroundCartographerCompatible();
break;
}
if(isTileEligible)
tiles.insert(int3(xd, yd, zd));
}
}
}

View File

@ -40,11 +40,31 @@ namespace scripting
class DLL_LINKAGE CPrivilegedInfoCallback : public CGameInfoCallback
{
public:
CGameState * gameState();
void getFreeTiles (std::vector<int3> &tiles) const; //used for random spawns
void getTilesInRange(std::unordered_set<int3, ShashInt3> &tiles, int3 pos, int radious, boost::optional<PlayerColor> player = boost::optional<PlayerColor>(), int mode = 0, int3::EDistanceFormula formula = int3::DIST_2D) const; //mode 1 - only unrevealed tiles; mode 0 - all, mode -1 - only revealed
void getAllTiles (std::unordered_set<int3, ShashInt3> &tiles, boost::optional<PlayerColor> player = boost::optional<PlayerColor>(), int level=-1, int surface=0) const; //returns all tiles on given level (-1 - both levels, otherwise number of level); surface: 0 - land and water, 1 - only land, 2 - only water
void pickAllowedArtsSet(std::vector<const CArtifact*> &out, CRandomGenerator & rand); //gives 3 treasures, 3 minors, 1 major -> used by Black Market and Artifact Merchant
enum class MapTerrainFilterMode
{
NONE = 0,
LAND = 1,
WATER = 2,
LAND_CARTOGRAPHER = 3,
UNDERGROUND_CARTOGRAPHER = 4
};
CGameState *gameState();
//used for random spawns
void getFreeTiles(std::vector<int3> &tiles) const;
//mode 1 - only unrevealed tiles; mode 0 - all, mode -1 - only revealed
void getTilesInRange(std::unordered_set<int3, ShashInt3> &tiles, int3 pos, int radious,
boost::optional<PlayerColor> player = boost::optional<PlayerColor>(), int mode = 0,
int3::EDistanceFormula formula = int3::DIST_2D) const;
//returns all tiles on given level (-1 - both levels, otherwise number of level)
void getAllTiles(std::unordered_set<int3, ShashInt3> &tiles, boost::optional<PlayerColor> player = boost::optional<PlayerColor>(),
int level = -1, MapTerrainFilterMode tileFilterMode = MapTerrainFilterMode::NONE) const;
void pickAllowedArtsSet(std::vector<const CArtifact *> &out,
CRandomGenerator &rand); //gives 3 treasures, 3 minors, 1 major -> used by Black Market and Artifact Merchant
void getAllowedSpells(std::vector<SpellID> &out, ui16 level);
template<typename Saver>

View File

@ -440,6 +440,16 @@ bool TerrainType::isUnderground() const
return passabilityType & PassabilityType::SUBTERRANEAN;
}
bool TerrainType::isSurfaceCartographerCompatible() const
{
return isSurface();
}
bool TerrainType::isUndergroundCartographerCompatible() const
{
return isLand() && isPassable() && !isSurface();
}
bool TerrainType::isTransitionRequired() const
{
return transitionRequired;

View File

@ -60,6 +60,8 @@ public:
bool isSurface() const;
bool isUnderground() const;
bool isTransitionRequired() const;
bool isSurfaceCartographerCompatible() const;
bool isUndergroundCartographerCompatible() const;
operator std::string() const;

View File

@ -2068,18 +2068,34 @@ void CCartographer::onHeroVisit( const CGHeroInstance * h ) const
void CCartographer::blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const
{
if (answer) //if hero wants to buy map
if(answer) //if hero wants to buy map
{
cb->giveResource (hero->tempOwner, Res::GOLD, -1000);
cb->giveResource(hero->tempOwner, Res::GOLD, -1000);
FoWChange fw;
fw.mode = 1;
fw.player = hero->tempOwner;
//subIDs of different types of cartographers:
//water = 0; land = 1; underground = 2;
cb->getAllTiles (fw.tiles, hero->tempOwner, subID - 1, !subID + 1); //reveal appropriate tiles
cb->sendAndApply (&fw);
cb->setObjProperty (id, CCartographer::OBJPROP_VISITED, hero->tempOwner.getNum());
IGameCallback::MapTerrainFilterMode tileFilterMode = IGameCallback::MapTerrainFilterMode::NONE;
switch(subID)
{
case 0:
tileFilterMode = CPrivilegedInfoCallback::MapTerrainFilterMode::WATER;
break;
case 1:
tileFilterMode = CPrivilegedInfoCallback::MapTerrainFilterMode::LAND_CARTOGRAPHER;
break;
case 2:
tileFilterMode = CPrivilegedInfoCallback::MapTerrainFilterMode::UNDERGROUND_CARTOGRAPHER;
break;
}
cb->getAllTiles(fw.tiles, hero->tempOwner, -1, tileFilterMode); //reveal appropriate tiles
cb->sendAndApply(&fw);
cb->setObjProperty(id, CCartographer::OBJPROP_VISITED, hero->tempOwner.getNum());
}
}