1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-11-23 22:37:55 +02:00

battle only basic implementation

This commit is contained in:
Laserlicht
2025-10-18 21:43:05 +02:00
parent e81d5bc973
commit 4d37fe631b
21 changed files with 230 additions and 9 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 873 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 863 B

View File

@@ -0,0 +1,8 @@
{
"basepath" : "lobby/",
"images" :
[
{ "frame" : 0, "file" : "battle-normal.png"},
{ "frame" : 1, "file" : "battle-pressed.png"}
]
}

View File

@@ -138,6 +138,7 @@
"vcmi.lobby.deleteFile" : "Do you want to delete following file?",
"vcmi.lobby.deleteFolder" : "Do you want to delete following folder?",
"vcmi.lobby.deleteMode" : "Switch to delete mode and back",
"vcmi.lobby.battleOnlyMode" : "Battle only mode",
"vcmi.broadcast.failedLoadGame" : "Failed to load game",
"vcmi.broadcast.command" : "Use '!help' to list available commands",

View File

@@ -138,6 +138,7 @@
"vcmi.lobby.deleteFile" : "Möchtet Ihr folgende Datei löschen?",
"vcmi.lobby.deleteFolder" : "Möchtet Ihr folgenden Ordner löschen?",
"vcmi.lobby.deleteMode" : "In den Löschmodus wechseln und zurück",
"vcmi.lobby.battleOnlyMode" : "Nur Kämpfen Modus",
"vcmi.broadcast.failedLoadGame" : "Spiel konnte nicht geladen werden",
"vcmi.broadcast.command" : "Benutze '!help' um alle verfügbaren Befehle aufzulisten",

View File

@@ -52,6 +52,7 @@ set(vcmiclientcommon_SRCS
gui/ShortcutHandler.cpp
gui/WindowHandler.cpp
lobby/BattleOnlyMode.cpp
lobby/CBonusSelection.cpp
lobby/CCampaignInfoScreen.cpp
lobby/CLobbyScreen.cpp
@@ -262,6 +263,7 @@ set(vcmiclientcommon_HEADERS
gui/TextAlignment.h
gui/WindowHandler.h
lobby/BattleOnlyMode.h
lobby/CBonusSelection.h
lobby/CCampaignInfoScreen.h
lobby/CLobbyScreen.h

View File

@@ -97,6 +97,7 @@
#include "../lib/mapObjects/MiscObjects.h"
#include "../lib/mapObjects/ObjectTemplate.h"
#include "../lib/mapping/CMap.h"
#include "../lib/mapping/CMapHeader.h"
#include "../lib/networkPacks/PacksForClient.h"
@@ -658,7 +659,7 @@ void CPlayerInterface::battleStart(const BattleID & battleID, const CCreatureSet
{
EVENT_HANDLER_CALLED_BY_CLIENT;
bool useQuickCombat = settings["adventure"]["quickCombat"].Bool();
bool useQuickCombat = settings["adventure"]["quickCombat"].Bool() || GAME->map().getMap()->battleOnly;
bool forceQuickCombat = settings["adventure"]["forceQuickCombat"].Bool();
if ((replayAllowed && useQuickCombat) || forceQuickCombat)

View File

@@ -15,6 +15,7 @@
#include "windows/GUIClasses.h"
#include "windows/CCastleInterface.h"
#include "mapView/mapHandler.h"
#include "mainmenu/CMainMenu.h"
#include "adventureMap/AdventureMapInterface.h"
#include "adventureMap/CInGameConsole.h"
#include "battle/BattleInterface.h"
@@ -409,7 +410,13 @@ void ApplyClientNetPackVisitor::visitPlayerEndsGame(PlayerEndsGame & pack)
adventureInt.reset();
}
GAME->server().showHighScoresAndEndGameplay(pack.player, pack.victoryLossCheckResult.victory(), pack.statistic);
if(!pack.silentEnd)
GAME->server().showHighScoresAndEndGameplay(pack.player, pack.victoryLossCheckResult.victory(), pack.statistic);
else
{
GAME->server().endGameplay();
GAME->mainmenu()->menu->switchToTab("main");
}
}
// In auto testing pack.mode we always close client if red pack.player won or lose

View File

@@ -0,0 +1,124 @@
/*
* BattleOnlyMode.cpp, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#include "StdInc.h"
#include "BattleOnlyMode.h"
#include "../CServerHandler.h"
#include "../GameEngine.h"
#include "../GameInstance.h"
#include "../gui/Shortcut.h"
#include "../gui/WindowHandler.h"
#include "../widgets/Buttons.h"
#include "../widgets/Images.h"
#include "../../lib/gameState/CGameState.h"
#include "../../lib/StartInfo.h"
#include "../../lib/VCMIDirs.h"
#include "../../lib/CRandomGenerator.h"
#include "../../lib/callback/EditorCallback.h"
#include "../../lib/mapObjects/CGHeroInstance.h"
#include "../../lib/mapObjectConstructors/AObjectTypeHandler.h"
#include "../../lib/mapObjectConstructors/CObjectClassesHandler.h"
#include "../../lib/mapping/CMap.h"
#include "../../lib/mapping/CMapInfo.h"
#include "../../lib/mapping/CMapEditManager.h"
#include "../../lib/mapping/CMapService.h"
#include "../../lib/mapping/MapFormat.h"
#include "../../lib/filesystem/Filesystem.h"
void BattleOnlyMode::openBattleWindow()
{
ENGINE->windows().createAndPushWindow<BattleOnlyModeWindow>();
}
BattleOnlyModeWindow::BattleOnlyModeWindow()
: CWindowObject(BORDERED)
{
OBJECT_CONSTRUCTION;
pos.w = 640;
pos.h = 480;
updateShadow();
center();
backgroundTexture = std::make_shared<FilledTexturePlayerColored>(Rect(0, 0, pos.w, pos.h));
backgroundTexture->setPlayerColor(PlayerColor(1));
buttonOk = std::make_shared<CButton>(Point(183, 388), AnimationPath::builtin("MuBchck"), CButton::tooltip(), [this](){ startBattle(); }, EShortcut::GLOBAL_ACCEPT);
buttonAbort = std::make_shared<CButton>(Point(250, 388), AnimationPath::builtin("MuBcanc"), CButton::tooltip(), [this](){ close(); }, EShortcut::GLOBAL_CANCEL);
}
void BattleOnlyModeWindow::startBattle()
{
auto map = std::make_unique<CMap>(nullptr);
map->version = EMapFormat::VCMI;
map->creationDateTime = std::time(nullptr);
map->width = 4;
map->height = 3;
map->mapLevels = 1;
map->battleOnly = true;
map->initTerrain();
map->getEditManager()->clearTerrain(&CRandomGenerator::getDefault());
//auto terrain = LIBRARY->terrainTypeHandler->objects;
map->getEditManager()->getTerrainSelection().selectAll();
map->getEditManager()->drawTerrain(TerrainId::GRASS, 0, &CRandomGenerator::getDefault());
map->players[0].canComputerPlay = true;
map->players[0].canHumanPlay = true;
map->players[1] = map->players[0];
auto cb = std::make_unique<EditorCallback>(map.get());
auto knownHeroes = LIBRARY->objtypeh->knownSubObjects(Obj::HERO);
auto addHero = [&](MapObjectSubID heroType, PlayerColor color, const int3 & position)
{
auto it = knownHeroes.find(heroType);
if (it == knownHeroes.end())
return; // unknown hero type, nothing to add
auto firstHeroType = *it;
auto factory = LIBRARY->objtypeh->getHandlerFor(Obj::HERO, firstHeroType);
auto templates = factory->getTemplates();
auto obj = std::dynamic_pointer_cast<CGHeroInstance>(factory->create(cb.get(), templates.front()));
obj->setOwner(color);
obj->pos = position;
/*if(heroType == MapObjectSubID(1))
{*/
obj->pushPrimSkill(PrimarySkill::ATTACK, 100);
obj->getArmy()->setCreature(SlotID(0), CreatureID(1), 10000);
//}
map->getEditManager()->insertObject(obj);
};
addHero(MapObjectSubID(1), PlayerColor(0), int3(2, 1, 0));
addHero(MapObjectSubID(2), PlayerColor(1), int3(3, 1, 0));
auto path = VCMIDirs::get().userDataPath() / "Maps";
boost::filesystem::create_directories(path);
const std::string fileName = "BattleOnlyMode.vmap";
const auto fullPath = path / fileName;
CMapService mapService;
mapService.saveMap(map, fullPath);
CResourceHandler::get()->updateFilteredFiles([&](const std::string & mount) { return true; });
auto mapInfo = std::make_shared<CMapInfo>();
mapInfo->mapInit("Maps/BattleOnlyMode");
GAME->server().setMapInfo(mapInfo);
ExtraOptionsInfo extraOptions;
extraOptions.unlimitedReplay = true;
GAME->server().setExtraOptionsInfo(extraOptions);
GAME->server().sendStartGame();
}

View File

@@ -0,0 +1,32 @@
/*
* BattleOnlyMode.h, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#pragma once
#include "../windows/CWindowObject.h"
class FilledTexturePlayerColored;
class CButton;
class BattleOnlyMode
{
public:
static void openBattleWindow();
};
class BattleOnlyModeWindow : public CWindowObject
{
private:
std::shared_ptr<FilledTexturePlayerColored> backgroundTexture;
std::shared_ptr<CButton> buttonOk;
std::shared_ptr<CButton> buttonAbort;
void startBattle();
public:
BattleOnlyModeWindow();
};

View File

@@ -12,6 +12,7 @@
#include "SelectionTab.h"
#include "CSelectionBase.h"
#include "CLobbyScreen.h"
#include "BattleOnlyMode.h"
#include "../CPlayerInterface.h"
#include "../CServerHandler.h"
@@ -241,6 +242,15 @@ SelectionTab::SelectionTab(ESelectionScreen Type)
sortByDate->setOverlay(std::make_shared<CPicture>(ImagePath::builtin("lobby/selectionTabSortDate")));
buttonsSortBy.push_back(sortByDate);
bool isMultiplayer = GAME->server().loadMode == ELoadMode::MULTI;
if(tabType == ESelectionScreen::newGame && !isMultiplayer)
{
buttonBattleOnlyMode = std::make_shared<CButton>(Point(23, 18), AnimationPath::builtin("lobby/battleButton"), CButton::tooltip("", LIBRARY->generaltexth->translate("vcmi.lobby.battleOnlyMode")), [this, tabTitle, tabTitleDelete](){
BattleOnlyMode::openBattleWindow();
});
}
if(tabType == ESelectionScreen::loadGame || tabType == ESelectionScreen::newGame)
{
buttonDeleteMode = std::make_shared<CButton>(Point(367, 18), AnimationPath::builtin("lobby/deleteButton"), CButton::tooltip("", LIBRARY->generaltexth->translate("vcmi.lobby.deleteMode")), [this, tabTitle, tabTitleDelete](){
@@ -325,6 +335,7 @@ void SelectionTab::toggleMode()
inputName->disable();
auto files = getFiles("Maps/", EResType::MAP);
files.erase(ResourcePath("Maps/Tutorial.tut", EResType::MAP));
files.erase(ResourcePath("Maps/BattleOnlyMode.vmap", EResType::MAP));
parseMaps(files);
break;
}

View File

@@ -128,6 +128,8 @@ private:
std::shared_ptr<CButton> buttonDeleteMode;
bool deleteMode;
std::shared_ptr<CButton> buttonBattleOnlyMode;
bool enableUiEnhancements;
std::shared_ptr<CButton> buttonCampaignSet;

View File

@@ -129,6 +129,7 @@ CMapHeader::CMapHeader()
, defeatIconIndex(0)
, howManyTeams(0)
, areAnyPlayers(false)
, battleOnly(false)
{
setupEvents();
allowedHeroes = LIBRARY->heroh->getDefaultAllowed();

View File

@@ -272,6 +272,8 @@ public:
bool areAnyPlayers; /// Unused. True if there are any playable players on the map.
bool battleOnly; /// Battle only mode
/// "main quests" of the map that describe victory and loss conditions
std::vector<TriggeredEvent> triggeredEvents;
@@ -316,6 +318,8 @@ public:
h & levelLimit;
h & areAnyPlayers;
if (h.version >= Handler::Version::BATTLE_ONLY)
h & battleOnly;
h & players;
h & howManyTeams;
h & allowedHeroes;

View File

@@ -120,14 +120,14 @@ void CTerrainSelection::setSelection(const std::vector<int3> & vec)
void CTerrainSelection::selectAll()
{
selectRange(MapRect(int3(0, 0, 0), getMap()->width, getMap()->height));
selectRange(MapRect(int3(0, 0, 1), getMap()->width, getMap()->height));
for(int i = 0; i < getMap()->mapLevels; i++)
selectRange(MapRect(int3(0, 0, i), getMap()->width, getMap()->height));
}
void CTerrainSelection::clearSelection()
{
deselectRange(MapRect(int3(0, 0, 0), getMap()->width, getMap()->height));
deselectRange(MapRect(int3(0, 0, 1), getMap()->width, getMap()->height));
for(int i = 0; i < getMap()->mapLevels; i++)
deselectRange(MapRect(int3(0, 0, i), getMap()->width, getMap()->height));
}
CObjectSelection::CObjectSelection(CMap * map) : CMapSelection(map)

View File

@@ -870,6 +870,8 @@ void CMapLoaderJson::readHeader(const bool complete)
readTeams(handler);
//TODO: check mods
mapHeader->battleOnly = header["battleOnly"].Bool();
if(complete)
readOptions(handler);
@@ -1223,6 +1225,8 @@ void CMapSaverJson::writeHeader()
writeTeams(handler);
header["battleOnly"].Bool() = mapHeader->battleOnly;
writeOptions(handler);
writeTranslations();

View File

@@ -94,7 +94,7 @@ void CIdentifierStorage::requestIdentifier(const ObjectCallback & callback) cons
checkIdentifier(callback.type);
checkIdentifier(callback.name);
assert(!callback.localScope.empty());
//assert(!callback.localScope.empty());
if (state != ELoadingState::FINISHED) // enqueue request if loading is still in progress
scheduledRequests.push_back(callback);
@@ -126,7 +126,7 @@ CIdentifierStorage::ObjectCallback CIdentifierStorage::ObjectCallback::fromNameW
CIdentifierStorage::ObjectCallback CIdentifierStorage::ObjectCallback::fromNameAndType(const std::string & scope, const std::string & type, const std::string & fullName, const std::function<void(si32)> & callback, bool optional, bool bypassDependenciesCheck, bool caseSensitive)
{
assert(!scope.empty());
//assert(!scope.empty());
auto scopeAndFullName = vstd::splitStringToPair(fullName, ':');
auto typeAndName = vstd::splitStringToPair(scopeAndFullName.second, '.');

View File

@@ -524,6 +524,7 @@ struct DLL_LINKAGE PlayerEndsGame : public CPackForClient
PlayerColor player;
EVictoryLossCheckResult victoryLossCheckResult;
StatisticDataSet statistic;
bool silentEnd = false;
void visitTyped(ICPackVisitor & visitor) override;
@@ -532,6 +533,7 @@ struct DLL_LINKAGE PlayerEndsGame : public CPackForClient
h & player;
h & victoryLossCheckResult;
h & statistic;
h & silentEnd;
}
};

View File

@@ -50,8 +50,9 @@ enum class ESerializationVersion : int32_t
BONUS_HIDDEN, // hidden bonus
MORE_MAP_LAYERS, // more map layers
CONFIGURABLE_RESOURCES, // configurable resources
BATTLE_ONLY, // battle only mode
CURRENT = CONFIGURABLE_RESOURCES,
CURRENT = BATTLE_ONLY,
};
static_assert(ESerializationVersion::MINIMAL <= ESerializationVersion::CURRENT, "Invalid serialization version definition!");

View File

@@ -3478,6 +3478,16 @@ void CGameHandler::checkVictoryLossConditionsForPlayer(PlayerColor player)
if(!p || p->status != EPlayerStatus::INGAME) return;
if(gameState().getMap().battleOnly)
{
PlayerEndsGame peg;
peg.player = player;
peg.silentEnd = true;
sendAndApply(peg);
gameServer().setState(EServerState::SHUTDOWN);
return;
}
auto victoryLossCheckResult = gameState().checkForVictoryAndLoss(player);
if (victoryLossCheckResult.victory() || victoryLossCheckResult.loss())

View File

@@ -19,6 +19,7 @@
#include "../../lib/CPlayerState.h"
#include "../../lib/mapping/CMap.h"
#include "../../lib/mapObjects/CGObjectInstance.h"
#include "../../lib/mapObjects/CGHeroInstance.h"
#include "../../lib/gameState/CGameState.h"
#include "../../lib/pathfinder/CPathfinder.h"
#include "../../lib/pathfinder/PathfinderOptions.h"
@@ -364,6 +365,15 @@ bool TurnOrderProcessor::onPlayerEndsTurn(PlayerColor which)
void TurnOrderProcessor::onGameStarted()
{
if(gameHandler->gameInfo().getMapHeader()->battleOnly)
{
auto heroes = gameHandler->gameState().getMap().getObjects<CGHeroInstance>();
auto hero1 = heroes.at(0);
auto hero2 = heroes.at(1);
gameHandler->startBattle(hero1, hero2);
return;
}
if (actingPlayers.empty())
blockedContacts = computeContactStatus();