From ecaa9f5d0bfa96a68d886e58e05a7cf8b64d1b4f Mon Sep 17 00:00:00 2001 From: AlexVinS Date: Sat, 17 Mar 2018 17:58:30 +0300 Subject: [PATCH] Entities redesign and a few ERM features * Made most Handlers derived from CHandlerBase and moved service API there. * Declared existing Entity APIs. * Added basic script context caching * Started Lua script module * Started Lua spell effect API * Started script state persistence * Started battle info callback binding * CommitPackage removed * Extracted spells::Caster to own header; Expanded Spell API. * implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C * !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented * Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key) * Re-enabled VERM macros. * !?GM0 added * !?TM implemented * Added !!MF:N * Started !?OB, !!BM, !!HE, !!OW, !!UN * Added basic support of w-variables * Added support for ERM indirect variables * Made !?FU regular trigger * !!re (ERA loop receiver) implemented * Fixed ERM receivers with zero args. --- .travis.yml | 2 +- AI/BattleAI/BattleAI.cbp | 5 +- AI/BattleAI/BattleAI.cpp | 108 +- AI/BattleAI/BattleAI.h | 5 +- AI/BattleAI/PossibleSpellcast.h | 3 +- AI/BattleAI/StackWithBonuses.cpp | 131 +- AI/BattleAI/StackWithBonuses.h | 86 +- AI/EmptyAI/CEmptyAI.cpp | 12 +- AI/EmptyAI/CEmptyAI.h | 5 +- AI/EmptyAI/EmptyAI.cbp | 5 +- AI/StupidAI/StupidAI.cbp | 14 +- AI/StupidAI/StupidAI.cpp | 25 +- AI/StupidAI/StupidAI.h | 8 +- AI/VCAI/AIUtility.cpp | 2 +- AI/VCAI/FuzzyHelper.cpp | 2 + AI/VCAI/Goals/AbstractGoal.cpp | 2 +- AI/VCAI/Goals/AdventureSpellCast.cpp | 14 +- AI/VCAI/Goals/GatherArmy.cpp | 2 +- AI/VCAI/Goals/GatherTroops.cpp | 6 +- AI/VCAI/MapObjectsEvaluator.cpp | 6 +- AI/VCAI/Pathfinding/AIPathfinder.cpp | 2 +- AI/VCAI/VCAI.cbp | 5 +- AI/VCAI/VCAI.cpp | 9 +- AI/VCAI/VCAI.h | 2 +- CCallback.cpp | 16 +- CCallback.h | 6 +- CI/linux/before_install.sh | 1 + CI/mac/before_install.sh | 2 +- CI/mxe/before_install.sh | 11 +- CMakeLists.txt | 23 +- Global.h | 18 - Mods/vcmi/Data/s/std.verm | 40 - Mods/vcmi/Data/s/testy.erm | 14 - client/CGameInfo.cpp | 60 +- client/CGameInfo.h | 25 +- client/CMT.cpp | 54 +- client/CMusicHandler.cpp | 2 - client/CMusicHandler.h | 1 - client/CPlayerInterface.cpp | 68 +- client/CPlayerInterface.h | 13 +- client/CServerHandler.cpp | 2 + client/Client.cpp | 203 +- client/Client.h | 88 +- client/Graphics.cpp | 96 +- client/Graphics.h | 5 +- client/NetPacksClient.cpp | 46 +- client/VCMI_client.cbp | 9 +- client/battle/CBattleAnimations.cpp | 9 +- client/battle/CBattleInterface.cpp | 166 +- client/battle/CBattleInterface.h | 13 +- client/battle/CBattleInterfaceClasses.cpp | 6 +- client/gui/CGuiHandler.cpp | 10 +- client/lobby/CBonusSelection.cpp | 20 +- client/lobby/OptionsTab.cpp | 43 +- client/widgets/CArtifactHolder.cpp | 56 +- client/widgets/CComponent.cpp | 24 +- client/widgets/CGarrisonInt.cpp | 30 +- client/widgets/CGarrisonInt.h | 2 +- client/widgets/MiscWidgets.cpp | 8 +- client/windows/CCastleInterface.cpp | 48 +- client/windows/CCreatureWindow.cpp | 14 +- client/windows/CHeroWindow.h | 1 - client/windows/CKingdomInterface.cpp | 6 +- client/windows/CSpellWindow.cpp | 16 +- client/windows/CTradeWindow.cpp | 26 +- client/windows/GUIClasses.cpp | 10 +- cmake_modules/FindLuaJIT.cmake | 50 + config/filesystem.json | 4 + config/schemas/mod.json | 7 +- config/schemas/script.json | 22 + include/vcmi/Artifact.h | 27 + include/vcmi/ArtifactService.h | 21 + include/vcmi/Creature.h | 45 + include/vcmi/CreatureService.h | 21 + include/vcmi/Entity.h | 42 + include/vcmi/EntityService.h | 32 + include/vcmi/Environment.h | 36 + include/vcmi/Faction.h | 21 + include/vcmi/FactionService.h | 21 + include/vcmi/HeroClass.h | 22 + include/vcmi/HeroClassService.h | 21 + include/vcmi/HeroType.h | 22 + include/vcmi/HeroTypeService.h | 21 + include/vcmi/Metatype.h | 30 + include/vcmi/Player.h | 30 + include/vcmi/ServerCallback.h | 44 + include/vcmi/Services.h | 57 + include/vcmi/Skill.h | 22 + include/vcmi/SkillService.h | 21 + include/vcmi/Team.h | 15 + include/vcmi/events/AdventureEvents.h | 13 + include/vcmi/events/ApplyDamage.h | 45 + include/vcmi/events/BattleEvents.h | 13 + include/vcmi/events/Event.h | 29 + include/vcmi/events/EventBus.h | 44 + include/vcmi/events/GameResumed.h | 35 + include/vcmi/events/GenericEvents.h | 15 + include/vcmi/events/ObjectVisitEnded.h | 40 + include/vcmi/events/ObjectVisitStarted.h | 43 + include/vcmi/events/PlayerGotTurn.h | 41 + include/vcmi/events/SubscriptionRegistry.h | 166 + include/vcmi/events/TurnStarted.h | 33 + include/vcmi/scripting/Service.h | 80 + include/vcmi/spells/Caster.h | 68 + include/vcmi/spells/Magic.h | 69 + include/vcmi/spells/Service.h | 26 + include/vcmi/spells/Spell.h | 56 + include/vstd/StringUtils.h | 1 + lib/CArtHandler.cpp | 275 +- lib/CArtHandler.h | 51 +- lib/CCreatureHandler.cpp | 482 +- lib/CCreatureHandler.h | 63 +- lib/CCreatureSet.cpp | 23 +- lib/CCreatureSet.h | 3 + lib/CGameInfoCallback.cpp | 66 +- lib/CGameInfoCallback.h | 132 +- lib/CGameInterface.cpp | 30 +- lib/CGameInterface.h | 30 +- lib/CGameState.cpp | 208 +- lib/CGameState.h | 13 +- lib/CGameStateFwd.h | 1 + lib/CHeroHandler.cpp | 247 +- lib/CHeroHandler.h | 86 +- lib/CMakeLists.txt | 50 +- lib/CModHandler.cpp | 21 +- lib/CPathfinder.cpp | 4 +- lib/CPlayerState.cpp | 69 + lib/CPlayerState.h | 13 +- lib/CScriptingModule.cpp | 32 + lib/CScriptingModule.h | 41 +- lib/CSkillHandler.cpp | 50 +- lib/CSkillHandler.h | 23 +- lib/CStack.cpp | 6 +- lib/CStack.h | 2 +- lib/CThreadHelper.h | 65 +- lib/CTownHandler.cpp | 140 +- lib/CTownHandler.h | 47 +- lib/GameConstants.cpp | 94 +- lib/GameConstants.h | 86 +- lib/HeroBonus.cpp | 50 +- lib/HeroBonus.h | 11 +- lib/IGameCallback.cpp | 37 +- lib/IGameCallback.h | 27 +- lib/IGameEventsReceiver.h | 5 +- lib/IHandlerBase.h | 88 +- lib/JsonNode.cpp | 21 +- lib/NetPacks.h | 80 +- lib/NetPacksBase.h | 32 +- lib/NetPacksLib.cpp | 67 +- lib/ScriptHandler.cpp | 303 ++ lib/ScriptHandler.h | 133 + lib/VCMI_Lib.cpp | 101 +- lib/VCMI_Lib.h | 45 +- lib/VCMI_lib.cbp | 89 +- lib/abilities/Ability.h | 18 + lib/battle/BattleInfo.cpp | 26 +- lib/battle/BattleInfo.h | 5 +- lib/battle/CBattleInfoCallback.cpp | 107 +- lib/battle/CBattleInfoCallback.h | 29 +- lib/battle/CBattleInfoEssentials.h | 30 +- lib/battle/CCallbackBase.h | 1 + lib/battle/CObstacleInstance.cpp | 2 +- lib/battle/CPlayerBattleCallback.h | 2 - lib/battle/CUnitState.cpp | 28 +- lib/battle/CUnitState.h | 14 +- lib/battle/IBattleInfoCallback.h | 60 + lib/battle/IBattleState.h | 2 +- lib/battle/SideInBattle.h | 2 +- lib/battle/Unit.h | 3 +- lib/events/ApplyDamage.cpp | 62 + lib/events/ApplyDamage.h | 39 + lib/events/GameResumed.cpp | 39 + lib/events/GameResumed.h | 26 + lib/events/ObjectVisitEnded.cpp | 55 + lib/events/ObjectVisitEnded.h | 34 + lib/events/ObjectVisitStarted.cpp | 67 + lib/events/ObjectVisitStarted.h | 38 + lib/events/PlayerGotTurn.cpp | 61 + lib/events/PlayerGotTurn.h | 36 + lib/events/TurnStarted.cpp | 39 + lib/events/TurnStarted.h | 26 + lib/filesystem/Filesystem.cpp | 14 +- lib/filesystem/ResourceID.cpp | 4 +- lib/filesystem/ResourceID.h | 3 +- lib/mapObjects/CArmedInstance.cpp | 8 +- lib/mapObjects/CBank.cpp | 12 +- lib/mapObjects/CGHeroInstance.cpp | 102 +- lib/mapObjects/CGHeroInstance.h | 31 +- lib/mapObjects/CGMarket.cpp | 10 +- lib/mapObjects/CGPandoraBox.cpp | 10 +- lib/mapObjects/CGTownInstance.cpp | 145 +- lib/mapObjects/CGTownInstance.h | 7 + lib/mapObjects/CObjectClassesHandler.cpp | 12 +- lib/mapObjects/CObjectClassesHandler.h | 2 +- lib/mapObjects/CObjectHandler.cpp | 20 + lib/mapObjects/CObjectHandler.h | 17 +- lib/mapObjects/CQuest.cpp | 18 +- lib/mapObjects/CRewardableObject.cpp | 14 +- lib/mapObjects/CommonConstructors.cpp | 6 +- lib/mapObjects/JsonRandom.cpp | 10 +- lib/mapObjects/MiscObjects.cpp | 32 +- lib/mapping/CMap.cpp | 4 +- lib/mapping/MapFormatH3M.cpp | 16 +- lib/mapping/MapFormatJson.cpp | 28 +- lib/registerTypes/RegisterTypes.h | 4 +- lib/rmg/CMapGenOptions.cpp | 4 +- lib/rmg/CMapGenerator.cpp | 4 +- lib/rmg/CRmgTemplate.cpp | 18 +- lib/rmg/CRmgTemplateZone.cpp | 21 +- lib/rmg/CZonePlacer.cpp | 2 +- lib/serializer/CSerializer.cpp | 6 +- lib/serializer/CSerializer.h | 2 +- lib/serializer/JsonDeserializer.cpp | 4 +- lib/serializer/JsonSerializeFormat.cpp | 5 +- lib/serializer/JsonSerializeFormat.h | 3 +- lib/serializer/JsonSerializer.cpp | 2 +- lib/serializer/JsonTreeSerializer.h | 4 +- lib/serializer/JsonUpdater.cpp | 309 ++ lib/serializer/JsonUpdater.h | 44 + lib/spells/AbilityCaster.cpp | 12 +- lib/spells/AbilityCaster.h | 10 +- lib/spells/AdventureSpellMechanics.cpp | 104 +- lib/spells/AdventureSpellMechanics.h | 34 +- lib/spells/BattleSpellMechanics.cpp | 129 +- lib/spells/BattleSpellMechanics.h | 12 +- lib/spells/BonusCaster.cpp | 4 +- lib/spells/BonusCaster.h | 2 +- lib/spells/CSpellHandler.cpp | 235 +- lib/spells/CSpellHandler.h | 161 +- lib/spells/ISpellMechanics.cpp | 138 +- lib/spells/ISpellMechanics.h | 102 +- lib/spells/Magic.h | 139 - lib/spells/Problem.h | 2 +- lib/spells/ProxyCaster.cpp | 14 +- lib/spells/ProxyCaster.h | 14 +- lib/spells/effects/Catapult.cpp | 20 +- lib/spells/effects/Catapult.h | 2 +- lib/spells/effects/Clone.cpp | 25 +- lib/spells/effects/Clone.h | 2 +- lib/spells/effects/Damage.cpp | 99 +- lib/spells/effects/Damage.h | 4 +- lib/spells/effects/Dispel.cpp | 130 +- lib/spells/effects/Dispel.h | 5 +- lib/spells/effects/Effect.cpp | 4 +- lib/spells/effects/Effect.h | 10 +- lib/spells/effects/Effects.cpp | 6 +- lib/spells/effects/Effects.h | 2 +- lib/spells/effects/Heal.cpp | 12 +- lib/spells/effects/Heal.h | 4 +- lib/spells/effects/Obstacle.cpp | 20 +- lib/spells/effects/Obstacle.h | 4 +- lib/spells/effects/Registry.cpp | 9 +- lib/spells/effects/Registry.h | 20 +- lib/spells/effects/RemoveObstacle.cpp | 8 +- lib/spells/effects/RemoveObstacle.h | 2 +- lib/spells/effects/Sacrifice.cpp | 8 +- lib/spells/effects/Sacrifice.h | 2 +- lib/spells/effects/Summon.cpp | 18 +- lib/spells/effects/Summon.h | 2 +- lib/spells/effects/Teleport.cpp | 36 +- lib/spells/effects/Teleport.h | 5 +- lib/spells/effects/Timed.cpp | 227 +- lib/spells/effects/Timed.h | 4 +- lib/spells/effects/UnitEffect.cpp | 10 +- lib/vstd/StringUtils.cpp | 18 + osx/CMakeLists.txt | 13 +- scripting/erm/CMakeLists.txt | 12 +- scripting/erm/ERM.cbp | 15 +- scripting/erm/ERMInterpreter.cpp | 4192 ++++++----------- scripting/erm/ERMInterpreter.h | 599 +-- scripting/erm/ERMParser.cpp | 111 +- scripting/erm/ERMParser.h | 52 +- scripting/erm/ERMScriptModule.cpp | 47 +- scripting/erm/ERMScriptModule.h | 14 +- scripting/lua/CMakeLists.txt | 57 + scripting/lua/Lua.cbp | 171 + scripting/lua/LuaCallWrapper.h | 319 ++ scripting/lua/LuaFunctor.h | 23 + scripting/lua/LuaReference.cpp | 44 + scripting/lua/LuaReference.h | 33 + scripting/lua/LuaScriptModule.cpp | 56 + scripting/lua/LuaScriptModule.h | 33 + scripting/lua/LuaScriptingContext.cpp | 607 +++ scripting/lua/LuaScriptingContext.h | 90 + scripting/lua/LuaSpellEffect.cpp | 186 + scripting/lua/LuaSpellEffect.h | 73 + scripting/lua/LuaStack.cpp | 253 + scripting/lua/LuaStack.h | 288 ++ scripting/lua/LuaWrapper.h | 266 ++ scripting/lua/StdInc.cpp | 2 + scripting/lua/StdInc.h | 10 + scripting/lua/api/Artifact.cpp | 48 + scripting/lua/api/Artifact.h | 32 + scripting/lua/api/BattleCb.cpp | 101 + scripting/lua/api/BattleCb.h | 37 + scripting/lua/api/BonusSystem.cpp | 232 + scripting/lua/api/BonusSystem.h | 75 + scripting/lua/api/Creature.cpp | 68 + scripting/lua/api/Creature.h | 33 + scripting/lua/api/Faction.cpp | 38 + scripting/lua/api/Faction.h | 31 + scripting/lua/api/GameCb.cpp | 43 + scripting/lua/api/GameCb.h | 34 + scripting/lua/api/HeroClass.cpp | 36 + scripting/lua/api/HeroClass.h | 32 + scripting/lua/api/HeroInstance.cpp | 35 + scripting/lua/api/HeroInstance.h | 35 + scripting/lua/api/HeroType.cpp | 38 + scripting/lua/api/HeroType.h | 32 + scripting/lua/api/ObjectInstance.cpp | 38 + scripting/lua/api/ObjectInstance.h | 35 + scripting/lua/api/Player.cpp | 42 + scripting/lua/api/Player.h | 33 + scripting/lua/api/Registry.cpp | 88 + scripting/lua/api/Registry.h | 110 + scripting/lua/api/ServerCb.cpp | 93 + scripting/lua/api/ServerCb.h | 37 + scripting/lua/api/Services.cpp | 137 + scripting/lua/api/Services.h | 97 + scripting/lua/api/Skill.cpp | 36 + scripting/lua/api/Skill.h | 32 + scripting/lua/api/Spell.cpp | 57 + scripting/lua/api/Spell.h | 31 + scripting/lua/api/StackInstance.cpp | 40 + scripting/lua/api/StackInstance.h | 35 + scripting/lua/api/battle/UnitProxy.cpp | 63 + scripting/lua/api/battle/UnitProxy.h | 38 + scripting/lua/api/events/AdventureEvents.cpp | 47 + scripting/lua/api/events/AdventureEvents.h | 41 + scripting/lua/api/events/BattleEvents.cpp | 67 + scripting/lua/api/events/BattleEvents.h | 38 + scripting/lua/api/events/EventBusProxy.cpp | 33 + scripting/lua/api/events/EventBusProxy.h | 34 + scripting/lua/api/events/GenericEvents.cpp | 94 + scripting/lua/api/events/GenericEvents.h | 54 + .../api/events/SubscriptionRegistryProxy.cpp | 28 + .../api/events/SubscriptionRegistryProxy.h | 131 + .../lua/api/netpacks/BattleLogMessage.cpp | 63 + scripting/lua/api/netpacks/BattleLogMessage.h | 35 + .../lua/api/netpacks/BattleStackMoved.cpp | 89 + scripting/lua/api/netpacks/BattleStackMoved.h | 39 + .../lua/api/netpacks/BattleUnitsChanged.cpp | 114 + .../lua/api/netpacks/BattleUnitsChanged.h | 38 + .../lua/api/netpacks/EntitiesChanged.cpp | 69 + scripting/lua/api/netpacks/EntitiesChanged.h | 36 + scripting/lua/api/netpacks/InfoWindow.cpp | 128 + scripting/lua/api/netpacks/InfoWindow.h | 36 + scripting/lua/api/netpacks/PackForClient.h | 39 + scripting/lua/api/netpacks/SetResources.cpp | 133 + scripting/lua/api/netpacks/SetResources.h | 41 + scripts/lib/Metatype.lua | 24 + scripts/lib/erm.lua | 252 + scripts/lib/erm/BM.lua | 104 + scripts/lib/erm/BU.lua | 130 + scripts/lib/erm/DO.lua | 55 + scripts/lib/erm/FU.lua | 44 + scripts/lib/erm/FU_T.lua | 15 + scripts/lib/erm/GM_T.lua | 21 + scripts/lib/erm/HE.lua | 181 + scripts/lib/erm/IF.lua | 85 + scripts/lib/erm/MA.lua | 233 + scripts/lib/erm/MF.lua | 36 + scripts/lib/erm/MF_T.lua | 15 + scripts/lib/erm/OW.lua | 109 + scripts/lib/erm/PI_T.lua | 5 + scripts/lib/erm/ReceiverBase.lua | 18 + scripts/lib/erm/TM.lua | 57 + scripts/lib/erm/TM_T.lua | 51 + scripts/lib/erm/TriggerBase.lua | 37 + scripts/lib/erm/UN.lua | 58 + scripts/lib/erm/VR.lua | 22 + scripts/lib/verm.lua | 375 ++ server/CGameHandler.cpp | 610 ++- server/CGameHandler.h | 58 +- server/CQuery.h | 2 - server/CVCMIServer.cpp | 10 +- server/NetPacksServer.cpp | 6 - server/VCMI_server.cbp | 7 +- test/CMakeLists.txt | 64 +- test/CVcmiTestConfig.cpp | 7 + test/JsonComparer.cpp | 28 +- test/Test.cbp | 108 +- test/battle/CBattleInfoCallbackTest.cpp | 23 +- test/battle/CUnitStateMagicTest.cpp | 9 +- test/battle/CUnitStateTest.cpp | 51 +- test/entity/CArtifactTest.cpp | 47 + test/entity/CCreatureTest.cpp | 173 + test/entity/CFactionTest.cpp | 85 + test/entity/CHeroClassTest.cpp | 39 + test/entity/CHeroTest.cpp | 52 + test/entity/CSkillTest.cpp | 58 + test/erm/ERMPersistenceTest.cpp | 102 + test/erm/ERM_BM.cpp | 99 + test/erm/ERM_BU.cpp | 314 ++ test/erm/ERM_FU.cpp | 73 + test/erm/ERM_GM_T.cpp | 56 + test/erm/ERM_MA.cpp | 293 ++ test/erm/ERM_MC.cpp | 76 + test/erm/ERM_MF.cpp | 90 + test/erm/ERM_OB_T.cpp | 65 + test/erm/ERM_TM_T.cpp | 82 + test/erm/ERM_UN.cpp | 128 + test/erm/ERM_VR.cpp | 79 + test/erm/ExamplesTest.cpp | 129 + test/events/ApplyDamageTest.cpp | 63 + test/events/EventBusTest.cpp | 157 + test/game/CGameStateTest.cpp | 132 +- test/mock/BattleFake.cpp | 105 + test/mock/BattleFake.h | 87 + test/mock/mock_Creature.h | 52 + test/mock/mock_CreatureService.h | 23 + test/mock/mock_Environment.h | 23 + test/mock/mock_IBattleInfoCallback.h | 43 + test/mock/mock_IGameCallback.cpp | 11 +- test/mock/mock_IGameCallback.h | 21 +- test/mock/mock_IGameEventCallback.h | 23 + test/mock/mock_IGameInfoCallback.h | 36 + test/mock/mock_ServerCallback.h | 32 + test/mock/mock_Services.h | 34 + test/mock/mock_battle_IBattleState.h | 1 + test/mock/mock_battle_Unit.h | 12 +- test/mock/mock_events_ApplyDamage.h | 30 + test/mock/mock_scripting_Context.h | 42 + test/mock/mock_scripting_Pool.h | 25 + test/mock/mock_scripting_Script.h | 29 + test/mock/mock_scripting_Service.h | 26 + test/mock/mock_spells_Mechanics.h | 18 +- test/mock/mock_spells_Problem.h | 8 +- test/mock/mock_spells_Spell.h | 25 +- test/mock/mock_spells_SpellService.h | 28 + test/mock/mock_spells_effects_Registry.h | 34 + test/mock/mock_vstd_CLoggerBase.h | 43 + test/netpacks/EntitiesChangedTest.cpp | 46 + test/netpacks/NetPackFixture.cpp | 31 + test/netpacks/NetPackFixture.h | 41 + test/scripting/LuaSandboxTest.cpp | 39 + test/scripting/LuaSpellEffectAPITest.cpp | 175 + test/scripting/LuaSpellEffectTest.cpp | 209 + test/scripting/PoolTest.cpp | 116 + test/scripting/ScriptFixture.cpp | 92 + test/scripting/ScriptFixture.h | 82 + test/serializer/JsonUpdaterTest.cpp | 205 + test/spells/CSpellTest.cpp | 49 + test/spells/effects/CatapultTest.cpp | 5 +- test/spells/effects/CloneTest.cpp | 25 +- test/spells/effects/DamageTest.cpp | 22 +- test/spells/effects/DispelTest.cpp | 94 +- test/spells/effects/EffectFixture.cpp | 136 +- test/spells/effects/EffectFixture.h | 88 +- test/spells/effects/HealTest.cpp | 6 +- test/spells/effects/SacrificeTest.cpp | 4 +- test/spells/effects/SummonTest.cpp | 39 +- test/spells/effects/TeleportTest.cpp | 16 +- test/spells/effects/TimedTest.cpp | 99 + test/testdata/erm/DO1.json | 16 + test/testdata/erm/DO1.verm | 13 + test/testdata/erm/DO2.json | 17 + test/testdata/erm/DO2.verm | 10 + test/testdata/erm/heroVar.json | 25 + test/testdata/erm/heroVar.verm | 19 + test/testdata/erm/indirectVar.json | 17 + test/testdata/erm/indirectVar.verm | 9 + test/testdata/erm/list-manipulation.json | 18 + test/testdata/erm/list-manipulation.verm | 17 + test/testdata/erm/re1.json | 27 + test/testdata/erm/re1.verm | 11 + test/testdata/erm/std.json | 29 + test/testdata/erm/std.verm | 77 + test/testdata/erm/testy.json | 27 + test/testdata/erm/testy.verm | 22 + test/testdata/lua/SandboxTest.lua | 21 + test/testdata/lua/SpellEffectAPIMoveUnit.lua | 22 + test/testdata/lua/SpellEffectAPITest.lua | 26 + test/vcai/mock_VCAI.h | 2 +- vcmi.workspace | 19 +- 475 files changed, 22491 insertions(+), 7123 deletions(-) delete mode 100644 Mods/vcmi/Data/s/std.verm delete mode 100644 Mods/vcmi/Data/s/testy.erm create mode 100644 cmake_modules/FindLuaJIT.cmake create mode 100644 config/schemas/script.json create mode 100644 include/vcmi/Artifact.h create mode 100644 include/vcmi/ArtifactService.h create mode 100644 include/vcmi/Creature.h create mode 100644 include/vcmi/CreatureService.h create mode 100644 include/vcmi/Entity.h create mode 100644 include/vcmi/EntityService.h create mode 100644 include/vcmi/Environment.h create mode 100644 include/vcmi/Faction.h create mode 100644 include/vcmi/FactionService.h create mode 100644 include/vcmi/HeroClass.h create mode 100644 include/vcmi/HeroClassService.h create mode 100644 include/vcmi/HeroType.h create mode 100644 include/vcmi/HeroTypeService.h create mode 100644 include/vcmi/Metatype.h create mode 100644 include/vcmi/Player.h create mode 100644 include/vcmi/ServerCallback.h create mode 100644 include/vcmi/Services.h create mode 100644 include/vcmi/Skill.h create mode 100644 include/vcmi/SkillService.h create mode 100644 include/vcmi/Team.h create mode 100644 include/vcmi/events/AdventureEvents.h create mode 100644 include/vcmi/events/ApplyDamage.h create mode 100644 include/vcmi/events/BattleEvents.h create mode 100644 include/vcmi/events/Event.h create mode 100644 include/vcmi/events/EventBus.h create mode 100644 include/vcmi/events/GameResumed.h create mode 100644 include/vcmi/events/GenericEvents.h create mode 100644 include/vcmi/events/ObjectVisitEnded.h create mode 100644 include/vcmi/events/ObjectVisitStarted.h create mode 100644 include/vcmi/events/PlayerGotTurn.h create mode 100644 include/vcmi/events/SubscriptionRegistry.h create mode 100644 include/vcmi/events/TurnStarted.h create mode 100644 include/vcmi/scripting/Service.h create mode 100644 include/vcmi/spells/Caster.h create mode 100644 include/vcmi/spells/Magic.h create mode 100644 include/vcmi/spells/Service.h create mode 100644 include/vcmi/spells/Spell.h create mode 100644 lib/CPlayerState.cpp create mode 100644 lib/CScriptingModule.cpp create mode 100644 lib/ScriptHandler.cpp create mode 100644 lib/ScriptHandler.h create mode 100644 lib/abilities/Ability.h create mode 100644 lib/battle/IBattleInfoCallback.h create mode 100644 lib/events/ApplyDamage.cpp create mode 100644 lib/events/ApplyDamage.h create mode 100644 lib/events/GameResumed.cpp create mode 100644 lib/events/GameResumed.h create mode 100644 lib/events/ObjectVisitEnded.cpp create mode 100644 lib/events/ObjectVisitEnded.h create mode 100644 lib/events/ObjectVisitStarted.cpp create mode 100644 lib/events/ObjectVisitStarted.h create mode 100644 lib/events/PlayerGotTurn.cpp create mode 100644 lib/events/PlayerGotTurn.h create mode 100644 lib/events/TurnStarted.cpp create mode 100644 lib/events/TurnStarted.h create mode 100644 lib/serializer/JsonUpdater.cpp create mode 100644 lib/serializer/JsonUpdater.h delete mode 100644 lib/spells/Magic.h create mode 100644 scripting/lua/CMakeLists.txt create mode 100644 scripting/lua/Lua.cbp create mode 100644 scripting/lua/LuaCallWrapper.h create mode 100644 scripting/lua/LuaFunctor.h create mode 100644 scripting/lua/LuaReference.cpp create mode 100644 scripting/lua/LuaReference.h create mode 100644 scripting/lua/LuaScriptModule.cpp create mode 100644 scripting/lua/LuaScriptModule.h create mode 100644 scripting/lua/LuaScriptingContext.cpp create mode 100644 scripting/lua/LuaScriptingContext.h create mode 100644 scripting/lua/LuaSpellEffect.cpp create mode 100644 scripting/lua/LuaSpellEffect.h create mode 100644 scripting/lua/LuaStack.cpp create mode 100644 scripting/lua/LuaStack.h create mode 100644 scripting/lua/LuaWrapper.h create mode 100644 scripting/lua/StdInc.cpp create mode 100644 scripting/lua/StdInc.h create mode 100644 scripting/lua/api/Artifact.cpp create mode 100644 scripting/lua/api/Artifact.h create mode 100644 scripting/lua/api/BattleCb.cpp create mode 100644 scripting/lua/api/BattleCb.h create mode 100644 scripting/lua/api/BonusSystem.cpp create mode 100644 scripting/lua/api/BonusSystem.h create mode 100644 scripting/lua/api/Creature.cpp create mode 100644 scripting/lua/api/Creature.h create mode 100644 scripting/lua/api/Faction.cpp create mode 100644 scripting/lua/api/Faction.h create mode 100644 scripting/lua/api/GameCb.cpp create mode 100644 scripting/lua/api/GameCb.h create mode 100644 scripting/lua/api/HeroClass.cpp create mode 100644 scripting/lua/api/HeroClass.h create mode 100644 scripting/lua/api/HeroInstance.cpp create mode 100644 scripting/lua/api/HeroInstance.h create mode 100644 scripting/lua/api/HeroType.cpp create mode 100644 scripting/lua/api/HeroType.h create mode 100644 scripting/lua/api/ObjectInstance.cpp create mode 100644 scripting/lua/api/ObjectInstance.h create mode 100644 scripting/lua/api/Player.cpp create mode 100644 scripting/lua/api/Player.h create mode 100644 scripting/lua/api/Registry.cpp create mode 100644 scripting/lua/api/Registry.h create mode 100644 scripting/lua/api/ServerCb.cpp create mode 100644 scripting/lua/api/ServerCb.h create mode 100644 scripting/lua/api/Services.cpp create mode 100644 scripting/lua/api/Services.h create mode 100644 scripting/lua/api/Skill.cpp create mode 100644 scripting/lua/api/Skill.h create mode 100644 scripting/lua/api/Spell.cpp create mode 100644 scripting/lua/api/Spell.h create mode 100644 scripting/lua/api/StackInstance.cpp create mode 100644 scripting/lua/api/StackInstance.h create mode 100644 scripting/lua/api/battle/UnitProxy.cpp create mode 100644 scripting/lua/api/battle/UnitProxy.h create mode 100644 scripting/lua/api/events/AdventureEvents.cpp create mode 100644 scripting/lua/api/events/AdventureEvents.h create mode 100644 scripting/lua/api/events/BattleEvents.cpp create mode 100644 scripting/lua/api/events/BattleEvents.h create mode 100644 scripting/lua/api/events/EventBusProxy.cpp create mode 100644 scripting/lua/api/events/EventBusProxy.h create mode 100644 scripting/lua/api/events/GenericEvents.cpp create mode 100644 scripting/lua/api/events/GenericEvents.h create mode 100644 scripting/lua/api/events/SubscriptionRegistryProxy.cpp create mode 100644 scripting/lua/api/events/SubscriptionRegistryProxy.h create mode 100644 scripting/lua/api/netpacks/BattleLogMessage.cpp create mode 100644 scripting/lua/api/netpacks/BattleLogMessage.h create mode 100644 scripting/lua/api/netpacks/BattleStackMoved.cpp create mode 100644 scripting/lua/api/netpacks/BattleStackMoved.h create mode 100644 scripting/lua/api/netpacks/BattleUnitsChanged.cpp create mode 100644 scripting/lua/api/netpacks/BattleUnitsChanged.h create mode 100644 scripting/lua/api/netpacks/EntitiesChanged.cpp create mode 100644 scripting/lua/api/netpacks/EntitiesChanged.h create mode 100644 scripting/lua/api/netpacks/InfoWindow.cpp create mode 100644 scripting/lua/api/netpacks/InfoWindow.h create mode 100644 scripting/lua/api/netpacks/PackForClient.h create mode 100644 scripting/lua/api/netpacks/SetResources.cpp create mode 100644 scripting/lua/api/netpacks/SetResources.h create mode 100644 scripts/lib/Metatype.lua create mode 100644 scripts/lib/erm.lua create mode 100644 scripts/lib/erm/BM.lua create mode 100644 scripts/lib/erm/BU.lua create mode 100644 scripts/lib/erm/DO.lua create mode 100644 scripts/lib/erm/FU.lua create mode 100644 scripts/lib/erm/FU_T.lua create mode 100644 scripts/lib/erm/GM_T.lua create mode 100644 scripts/lib/erm/HE.lua create mode 100644 scripts/lib/erm/IF.lua create mode 100644 scripts/lib/erm/MA.lua create mode 100644 scripts/lib/erm/MF.lua create mode 100644 scripts/lib/erm/MF_T.lua create mode 100644 scripts/lib/erm/OW.lua create mode 100644 scripts/lib/erm/PI_T.lua create mode 100644 scripts/lib/erm/ReceiverBase.lua create mode 100644 scripts/lib/erm/TM.lua create mode 100644 scripts/lib/erm/TM_T.lua create mode 100644 scripts/lib/erm/TriggerBase.lua create mode 100644 scripts/lib/erm/UN.lua create mode 100644 scripts/lib/erm/VR.lua create mode 100644 scripts/lib/verm.lua create mode 100644 test/entity/CArtifactTest.cpp create mode 100644 test/entity/CCreatureTest.cpp create mode 100644 test/entity/CFactionTest.cpp create mode 100644 test/entity/CHeroClassTest.cpp create mode 100644 test/entity/CHeroTest.cpp create mode 100644 test/entity/CSkillTest.cpp create mode 100644 test/erm/ERMPersistenceTest.cpp create mode 100644 test/erm/ERM_BM.cpp create mode 100644 test/erm/ERM_BU.cpp create mode 100644 test/erm/ERM_FU.cpp create mode 100644 test/erm/ERM_GM_T.cpp create mode 100644 test/erm/ERM_MA.cpp create mode 100644 test/erm/ERM_MC.cpp create mode 100644 test/erm/ERM_MF.cpp create mode 100644 test/erm/ERM_OB_T.cpp create mode 100644 test/erm/ERM_TM_T.cpp create mode 100644 test/erm/ERM_UN.cpp create mode 100644 test/erm/ERM_VR.cpp create mode 100644 test/erm/ExamplesTest.cpp create mode 100644 test/events/ApplyDamageTest.cpp create mode 100644 test/events/EventBusTest.cpp create mode 100644 test/mock/BattleFake.cpp create mode 100644 test/mock/BattleFake.h create mode 100644 test/mock/mock_Creature.h create mode 100644 test/mock/mock_CreatureService.h create mode 100644 test/mock/mock_Environment.h create mode 100644 test/mock/mock_IBattleInfoCallback.h create mode 100644 test/mock/mock_IGameEventCallback.h create mode 100644 test/mock/mock_IGameInfoCallback.h create mode 100644 test/mock/mock_ServerCallback.h create mode 100644 test/mock/mock_Services.h create mode 100644 test/mock/mock_events_ApplyDamage.h create mode 100644 test/mock/mock_scripting_Context.h create mode 100644 test/mock/mock_scripting_Pool.h create mode 100644 test/mock/mock_scripting_Script.h create mode 100644 test/mock/mock_scripting_Service.h create mode 100644 test/mock/mock_spells_SpellService.h create mode 100644 test/mock/mock_spells_effects_Registry.h create mode 100644 test/mock/mock_vstd_CLoggerBase.h create mode 100644 test/netpacks/EntitiesChangedTest.cpp create mode 100644 test/netpacks/NetPackFixture.cpp create mode 100644 test/netpacks/NetPackFixture.h create mode 100644 test/scripting/LuaSandboxTest.cpp create mode 100644 test/scripting/LuaSpellEffectAPITest.cpp create mode 100644 test/scripting/LuaSpellEffectTest.cpp create mode 100644 test/scripting/PoolTest.cpp create mode 100644 test/scripting/ScriptFixture.cpp create mode 100644 test/scripting/ScriptFixture.h create mode 100644 test/serializer/JsonUpdaterTest.cpp create mode 100644 test/spells/CSpellTest.cpp create mode 100644 test/testdata/erm/DO1.json create mode 100644 test/testdata/erm/DO1.verm create mode 100644 test/testdata/erm/DO2.json create mode 100644 test/testdata/erm/DO2.verm create mode 100644 test/testdata/erm/heroVar.json create mode 100644 test/testdata/erm/heroVar.verm create mode 100644 test/testdata/erm/indirectVar.json create mode 100644 test/testdata/erm/indirectVar.verm create mode 100644 test/testdata/erm/list-manipulation.json create mode 100644 test/testdata/erm/list-manipulation.verm create mode 100644 test/testdata/erm/re1.json create mode 100644 test/testdata/erm/re1.verm create mode 100644 test/testdata/erm/std.json create mode 100644 test/testdata/erm/std.verm create mode 100644 test/testdata/erm/testy.json create mode 100644 test/testdata/erm/testy.verm create mode 100644 test/testdata/lua/SandboxTest.lua create mode 100644 test/testdata/lua/SpellEffectAPIMoveUnit.lua create mode 100644 test/testdata/lua/SpellEffectAPITest.lua diff --git a/.travis.yml b/.travis.yml index 1536f7227..7fabdc1f0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -29,7 +29,7 @@ matrix: env: VCMI_PLATFORM='mxe' MXE_TARGET=i686-w64-mingw32.shared VCMI_CMAKE_FLAGS='-DENABLE_TEST=0' sudo: required - os: osx - env: VCMI_PLATFORM='mac' + env: VCMI_PLATFORM='mac' VCMI_CMAKE_FLAGS='-DENABLE_TEST=0' addons: apt: diff --git a/AI/BattleAI/BattleAI.cbp b/AI/BattleAI/BattleAI.cbp index 4032325a6..b2c8bcfc0 100644 --- a/AI/BattleAI/BattleAI.cbp +++ b/AI/BattleAI/BattleAI.cbp @@ -69,7 +69,7 @@ - + @@ -100,9 +100,6 @@ - - - diff --git a/AI/BattleAI/BattleAI.cpp b/AI/BattleAI/BattleAI.cpp index 331bc68d5..98e94495e 100644 --- a/AI/BattleAI/BattleAI.cpp +++ b/AI/BattleAI/BattleAI.cpp @@ -10,8 +10,6 @@ #include "StdInc.h" #include "BattleAI.h" -#include - #include "StackWithBonuses.h" #include "EnemyInfo.h" #include "../../lib/CStopWatch.h" @@ -26,26 +24,6 @@ #define LOGL(text) print(text) #define LOGFL(text, formattingEl) print(boost::str(boost::format(text) % formattingEl)) -class RNGStub : public vstd::RNG -{ -public: - vstd::TRandI64 getInt64Range(int64_t lower, int64_t upper) override - { - return [=]()->int64_t - { - return (lower + upper)/2; - }; - } - - vstd::TRand getDoubleRange(double lower, double upper) override - { - return [=]()->double - { - return (lower + upper)/2; - }; - } -}; - enum class SpellTypes { ADVENTURE, BATTLE, OTHER @@ -53,10 +31,10 @@ enum class SpellTypes SpellTypes spellType(const CSpell * spell) { - if(!spell->isCombatSpell() || spell->isCreatureAbility()) + if(!spell->isCombat() || spell->isCreatureAbility()) return SpellTypes::OTHER; - if(spell->isOffensiveSpell() || spell->hasEffects() || spell->hasBattleEffects()) + if(spell->isOffensive() || spell->hasEffects() || spell->hasBattleEffects()) return SpellTypes::BATTLE; return SpellTypes::OTHER; @@ -83,7 +61,9 @@ std::vector CBattleAI::getBrokenWallMoatHexes() const } CBattleAI::CBattleAI() - : side(-1), wasWaitingForRealize(false), wasUnlockingGs(false) + : side(-1), + wasWaitingForRealize(false), + wasUnlockingGs(false) { } @@ -97,12 +77,13 @@ CBattleAI::~CBattleAI() } } -void CBattleAI::init(std::shared_ptr CB) +void CBattleAI::init(std::shared_ptr ENV, std::shared_ptr CB) { setCbc(CB); + env = ENV; cb = CB; playerID = *CB->getPlayerID(); //TODO should be sth in callback - wasWaitingForRealize = cb->waitTillRealize; + wasWaitingForRealize = CB->waitTillRealize; wasUnlockingGs = CB->unlockGsWhenWaiting; CB->waitTillRealize = true; CB->unlockGsWhenWaiting = false; @@ -131,7 +112,7 @@ BattleAction CBattleAI::activeStack( const CStack * stack ) attemptCastingSpell(); - if(auto ret = getCbc()->battleIsFinished()) + if(auto ret = cb->battleIsFinished()) { //spellcast may finish battle //send special preudo-action @@ -144,7 +125,7 @@ BattleAction CBattleAI::activeStack( const CStack * stack ) return *action; //best action is from effective owner point if view, we are effective owner as we received "activeStack" - + //evaluate casting spell for spellcasting stack boost::optional bestSpellcast(boost::none); //TODO: faerie dragon type spell should be selected by server @@ -174,7 +155,7 @@ BattleAction CBattleAI::activeStack( const CStack * stack ) } } - HypotheticBattle hb(getCbc()); + HypotheticBattle hb(env.get(), cb); PotentialTargets targets(stack, &hb); @@ -199,7 +180,7 @@ BattleAction CBattleAI::activeStack( const CStack * stack ) ); return BattleAction::makeMeleeAttack(stack, bestAttack.attack.defender->getPosition(), bestAttack.from); - } + } } else if(bestSpellcast.is_initialized()) { @@ -210,7 +191,7 @@ BattleAction CBattleAI::activeStack( const CStack * stack ) if(stack->waited()) { //ThreatMap threatsToUs(stack); // These lines may be usefull but they are't used in the code. - auto dists = getCbc()->getReachability(stack); + auto dists = cb->getReachability(stack); if(!targets.unreachableEnemies.empty()) { auto closestEnemy = vstd::minElementByFun(targets.unreachableEnemies, [&](const battle::Unit * enemy) -> int @@ -242,7 +223,7 @@ BattleAction CBattleAI::activeStack( const CStack * stack ) return BattleAction::makeMove(stack, stack->getPosition().cloneInDirection(BattleHex::RIGHT)); else return goTowardsNearest(stack, brokenWallMoat); - } + } } } catch(boost::thread_interrupted &) @@ -279,10 +260,10 @@ BattleAction CBattleAI::goTowardsNearest(const CStack * stack, std::vectorcoversPos(hex)) { - logAi->warn("Warning: already standing on neighbouring tile!"); - //We shouldn't even be here... - return BattleAction::makeDefend(stack); - } + logAi->warn("Warning: already standing on neighbouring tile!"); + //We shouldn't even be here... + return BattleAction::makeDefend(stack); + } } BattleHex bestNeighbor = hexes.front(); @@ -330,7 +311,7 @@ BattleAction CBattleAI::useCatapult(const CStack * stack) if(cb->battleGetGateState() == EGateState::CLOSED) { targetHex = cb->wallPartToBattleHex(EWallPart::GATE); - } +} else { EWallPart::EWallPart wallParts[] = { @@ -381,9 +362,9 @@ void CBattleAI::attemptCastingSpell() LOGL("Casting spells sounds like fun. Let's see..."); //Get all spells we can cast std::vector possibleSpells; - vstd::copy_if(VLC->spellh->objects, std::back_inserter(possibleSpells), [hero](const CSpell *s) -> bool + vstd::copy_if(VLC->spellh->objects, std::back_inserter(possibleSpells), [hero, this](const CSpell *s) -> bool { - return s->canBeCast(getCbc().get(), spells::Mode::HERO, hero); + return s->canBeCast(cb.get(), spells::Mode::HERO, hero); }); LOGFL("I can cast %d spells.", possibleSpells.size()); @@ -398,7 +379,7 @@ void CBattleAI::attemptCastingSpell() std::vector possibleCasts; for(auto spell : possibleSpells) { - spells::BattleCast temp(getCbc().get(), hero, spells::Mode::HERO, spell); + spells::BattleCast temp(cb.get(), hero, spells::Mode::HERO, spell); for(auto & target : temp.findPotentialTargets()) { @@ -500,8 +481,6 @@ void CBattleAI::attemptCastingSpell() return ourTurnSpan >= minTurnSpan; }; - RNGStub rngStub; - ValueMap valueOfStack; ValueMap healthOfStack; @@ -536,7 +515,8 @@ void CBattleAI::attemptCastingSpell() { bool enemyHadTurn = false; - HypotheticBattle state(cb); + HypotheticBattle state(env.get(), cb); + evaluateQueue(valueOfStack, turnOrder, &state, 0, &enemyHadTurn); if(!enemyHadTurn) @@ -551,13 +531,17 @@ void CBattleAI::attemptCastingSpell() } } - auto evaluateSpellcast = [&] (PossibleSpellcast * ps) + struct ScriptsCache { - HypotheticBattle state(cb); + //todo: re-implement scripts context cache + }; + + auto evaluateSpellcast = [&] (PossibleSpellcast * ps, std::shared_ptr) + { + HypotheticBattle state(env.get(), cb); spells::BattleCast cast(&state, hero, spells::Mode::HERO, ps->spell); - cast.target = ps->dest; - cast.cast(&state, rngStub); + cast.castEval(state.getServerCallback(), ps->dest); ValueMap newHealthOfStack; ValueMap newValueOfStack; @@ -617,10 +601,12 @@ void CBattleAI::attemptCastingSpell() } }; - std::vector> tasks; + using EvalRunner = ThreadPool; + + EvalRunner::Tasks tasks; for(PossibleSpellcast & psc : possibleCasts) - tasks.push_back(std::bind(evaluateSpellcast, &psc)); + tasks.push_back(std::bind(evaluateSpellcast, &psc, _1)); uint32_t threadCount = boost::thread::hardware_concurrency(); @@ -632,8 +618,15 @@ void CBattleAI::attemptCastingSpell() CStopWatch timer; - CThreadHelper threadHelper(&tasks, threadCount); - threadHelper.run(); + std::vector> scriptsPool; + + for(uint32_t idx = 0; idx < threadCount; idx++) + { + scriptsPool.emplace_back(); + } + + EvalRunner runner(&tasks, scriptsPool); + runner.run(); LOGFL("Evaluation took %d ms", timer.getDiff()); @@ -666,9 +659,9 @@ void CBattleAI::evaluateCreatureSpellcast(const CStack * stack, PossibleSpellcas using ValueMap = PossibleSpellcast::ValueMap; RNGStub rngStub; - HypotheticBattle state(getCbc()); - TStacks all = getCbc()->battleGetAllStacks(false); - + HypotheticBattle state(env.get(), cb); + TStacks all = cb->battleGetAllStacks(false); + ValueMap healthOfStack; ValueMap newHealthOfStack; @@ -678,8 +671,7 @@ void CBattleAI::evaluateCreatureSpellcast(const CStack * stack, PossibleSpellcas } spells::BattleCast cast(&state, stack, spells::Mode::CREATURE_ACTIVE, ps.spell); - cast.target = ps.dest; - cast.cast(&state, rngStub); + cast.castEval(state.getServerCallback(), ps.dest); for(auto unit : all) { @@ -710,7 +702,7 @@ void CBattleAI::evaluateCreatureSpellcast(const CStack * stack, PossibleSpellcas } ps.value = totalGain; -}; +} void CBattleAI::battleStart(const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool Side) { diff --git a/AI/BattleAI/BattleAI.h b/AI/BattleAI/BattleAI.h index e0623619b..47dc84c47 100644 --- a/AI/BattleAI/BattleAI.h +++ b/AI/BattleAI/BattleAI.h @@ -51,6 +51,7 @@ class CBattleAI : public CBattleGameInterface { int side; std::shared_ptr cb; + std::shared_ptr env; //Previous setting of cb bool wasWaitingForRealize, wasUnlockingGs; @@ -59,7 +60,7 @@ public: CBattleAI(); ~CBattleAI(); - void init(std::shared_ptr CB) override; + void init(std::shared_ptr ENV, std::shared_ptr CB) override; void attemptCastingSpell(); void evaluateCreatureSpellcast(const CStack * stack, PossibleSpellcast & ps); //for offensive damaging spells only @@ -74,7 +75,7 @@ public: //void actionFinished(const BattleAction &action) override;//occurs AFTER every action taken by any stack or by the hero //void actionStarted(const BattleAction &action) override;//occurs BEFORE every action taken by any stack or by the hero //void battleAttack(const BattleAttack *ba) override; //called when stack is performing attack - //void battleStacksAttacked(const std::vector & bsa, const std::vector & battleLog) override; //called when stack receives damage (after battleAttack()) + //void battleStacksAttacked(const std::vector & bsa) override; //called when stack receives damage (after battleAttack()) //void battleEnd(const BattleResult *br) override; //void battleResultsApplied() override; //called when all effects of last battle are applied //void battleNewRoundFirst(int round) override; //called at the beginning of each turn before changes are applied; diff --git a/AI/BattleAI/PossibleSpellcast.h b/AI/BattleAI/PossibleSpellcast.h index b96698be2..806832ad5 100644 --- a/AI/BattleAI/PossibleSpellcast.h +++ b/AI/BattleAI/PossibleSpellcast.h @@ -10,8 +10,9 @@ #pragma once +#include + #include "../../lib/battle/Destination.h" -#include "../../lib/spells/Magic.h" class CSpell; diff --git a/AI/BattleAI/StackWithBonuses.cpp b/AI/BattleAI/StackWithBonuses.cpp index 2c1a0cd79..2b3679569 100644 --- a/AI/BattleAI/StackWithBonuses.cpp +++ b/AI/BattleAI/StackWithBonuses.cpp @@ -9,8 +9,14 @@ */ #include "StdInc.h" #include "StackWithBonuses.h" -#include "../../lib/NetPacksBase.h" + +#include + +#include "../../lib/NetPacks.h" #include "../../lib/CStack.h" +#include "../../lib/ScriptHandler.h" + +using scripting::Pool; void actualizeEffect(TBonusListPtr target, const Bonus & ef) { @@ -191,19 +197,27 @@ void StackWithBonuses::removeUnitBonus(const CSelector & selector) vstd::erase_if(bonusesToUpdate, [&](const Bonus & b){return selector(&b);}); } -void StackWithBonuses::spendMana(const spells::PacketSender * server, const int spellCost) const +void StackWithBonuses::spendMana(ServerCallback * server, const int spellCost) const { //TODO: evaluate cast use } -HypotheticBattle::HypotheticBattle(Subject realBattle) +HypotheticBattle::HypotheticBattle(const Environment * ENV, Subject realBattle) : BattleProxy(realBattle), + env(ENV), bonusTreeVersion(1) { auto activeUnit = realBattle->battleActiveUnit(); activeUnitId = activeUnit ? activeUnit->unitId() : -1; - nextId = 0xF0000000; + nextId = 0x00F00000; + + eventBus.reset(new events::EventBus()); + + localEnvironment.reset(new HypotheticEnvironment(this, env)); + serverCallback.reset(new HypotheticServerCallback(this)); + + pool.reset(new scripting::PoolImpl(localEnvironment.get(), serverCallback.get())); } bool HypotheticBattle::unitHasAmmoCart(const battle::Unit * unit) const @@ -348,6 +362,11 @@ void HypotheticBattle::removeUnit(uint32_t id) } } +void HypotheticBattle::updateUnit(uint32_t id, const JsonNode & data) +{ + //TODO: +} + void HypotheticBattle::addUnitBonus(uint32_t id, const std::vector & bonus) { getForUpdate(id)->addUnitBonus(bonus); @@ -400,3 +419,107 @@ int64_t HypotheticBattle::getTreeVersion() const { return getBattleNode()->getTreeVersion() + bonusTreeVersion; } + +Pool * HypotheticBattle::getContextPool() const +{ + return pool.get(); +} + +ServerCallback * HypotheticBattle::getServerCallback() +{ + return serverCallback.get(); +} + +HypotheticBattle::HypotheticServerCallback::HypotheticServerCallback(HypotheticBattle * owner_) + :owner(owner_) +{ + +} + +void HypotheticBattle::HypotheticServerCallback::complain(const std::string & problem) +{ + logAi->error(problem); +} + +bool HypotheticBattle::HypotheticServerCallback::describeChanges() const +{ + return false; +} + +vstd::RNG * HypotheticBattle::HypotheticServerCallback::getRNG() +{ + return &rngStub; +} + +void HypotheticBattle::HypotheticServerCallback::apply(CPackForClient * pack) +{ + logAi->error("Package of type %s is not allowed in battle evaluation", typeid(pack).name()); +} + +void HypotheticBattle::HypotheticServerCallback::apply(BattleLogMessage * pack) +{ + pack->applyBattle(owner); +} + +void HypotheticBattle::HypotheticServerCallback::apply(BattleStackMoved * pack) +{ + pack->applyBattle(owner); +} + +void HypotheticBattle::HypotheticServerCallback::apply(BattleUnitsChanged * pack) +{ + pack->applyBattle(owner); +} + +void HypotheticBattle::HypotheticServerCallback::apply(SetStackEffect * pack) +{ + pack->applyBattle(owner); +} + +void HypotheticBattle::HypotheticServerCallback::apply(StacksInjured * pack) +{ + pack->applyBattle(owner); +} + +void HypotheticBattle::HypotheticServerCallback::apply(BattleObstaclesChanged * pack) +{ + pack->applyBattle(owner); +} + +void HypotheticBattle::HypotheticServerCallback::apply(CatapultAttack * pack) +{ + pack->applyBattle(owner); +} + +HypotheticBattle::HypotheticEnvironment::HypotheticEnvironment(HypotheticBattle * owner_, const Environment * upperEnvironment) + : owner(owner_), + env(upperEnvironment) +{ + +} + +const Services * HypotheticBattle::HypotheticEnvironment::services() const +{ + return env->services(); +} + +const Environment::BattleCb * HypotheticBattle::HypotheticEnvironment::battle() const +{ + return owner; +} + +const Environment::GameCb * HypotheticBattle::HypotheticEnvironment::game() const +{ + return env->game(); +} + +vstd::CLoggerBase * HypotheticBattle::HypotheticEnvironment::logger() const +{ + return env->logger(); +} + +events::EventBus * HypotheticBattle::HypotheticEnvironment::eventBus() const +{ + return owner->eventBus.get(); +} + diff --git a/AI/BattleAI/StackWithBonuses.h b/AI/BattleAI/StackWithBonuses.h index dbeb351c7..dc2ac3ce7 100644 --- a/AI/BattleAI/StackWithBonuses.h +++ b/AI/BattleAI/StackWithBonuses.h @@ -8,6 +8,12 @@ * */ #pragma once + +#include + +#include +#include + #include "../../lib/HeroBonus.h" #include "../../lib/battle/BattleProxy.h" #include "../../lib/battle/CUnitState.h" @@ -15,10 +21,30 @@ class HypotheticBattle; class CStack; +///Fake random generator, used by AI to evaluate random server behavior +class RNGStub : public vstd::RNG +{ +public: + vstd::TRandI64 getInt64Range(int64_t lower, int64_t upper) override + { + return [=]()->int64_t + { + return (lower + upper)/2; + }; + } + + vstd::TRand getDoubleRange(double lower, double upper) override + { + return [=]()->double + { + return (lower + upper)/2; + }; + } +}; + class StackWithBonuses : public battle::CUnitState, public virtual IBonusBearer { public: - std::vector bonusesToAdd; std::vector bonusesToUpdate; std::set> bonusesToRemove; @@ -53,7 +79,7 @@ public: void removeUnitBonus(const CSelector & selector); - void spendMana(const spells::PacketSender * server, const int spellCost) const override; + void spendMana(ServerCallback * server, const int spellCost) const override; private: const IBonusBearer * origBearer; @@ -72,7 +98,9 @@ class HypotheticBattle : public BattleProxy, public battle::IUnitEnvironment public: std::map> stackStates; - HypotheticBattle(Subject realBattle); + const Environment * env; + + HypotheticBattle(const Environment * ENV, Subject realBattle); bool unitHasAmmoCart(const battle::Unit * unit) const override; PlayerColor unitEffectiveOwner(const battle::Unit * unit) const override; @@ -90,6 +118,7 @@ public: void setUnitState(uint32_t id, const JsonNode & data, int64_t healthDelta) override; void moveUnit(uint32_t id, BattleHex destination) override; void removeUnit(uint32_t id) override; + void updateUnit(uint32_t id, const JsonNode & data) override; void addUnitBonus(uint32_t id, const std::vector & bonus) override; void updateUnitBonus(uint32_t id, const std::vector & bonus) override; @@ -107,8 +136,59 @@ public: int64_t getTreeVersion() const; + scripting::Pool * getContextPool() const override; + + ServerCallback * getServerCallback(); + private: + + class HypotheticServerCallback : public ServerCallback + { + public: + HypotheticServerCallback(HypotheticBattle * owner_); + + void complain(const std::string & problem) override; + bool describeChanges() const override; + + vstd::RNG * getRNG() override; + + void apply(CPackForClient * pack) override; + + void apply(BattleLogMessage * pack) override; + void apply(BattleStackMoved * pack) override; + void apply(BattleUnitsChanged * pack) override; + void apply(SetStackEffect * pack) override; + void apply(StacksInjured * pack) override; + void apply(BattleObstaclesChanged * pack) override; + void apply(CatapultAttack * pack) override; + private: + HypotheticBattle * owner; + RNGStub rngStub; + }; + + class HypotheticEnvironment : public Environment + { + public: + HypotheticEnvironment(HypotheticBattle * owner_, const Environment * upperEnvironment); + + const Services * services() const override; + const BattleCb * battle() const override; + const GameCb * game() const override; + vstd::CLoggerBase * logger() const override; + events::EventBus * eventBus() const override; + + private: + HypotheticBattle * owner; + const Environment * env; + }; + int32_t bonusTreeVersion; int32_t activeUnitId; mutable uint32_t nextId; + + std::unique_ptr serverCallback; + std::unique_ptr localEnvironment; + + mutable std::shared_ptr pool; + mutable std::shared_ptr eventBus; }; diff --git a/AI/EmptyAI/CEmptyAI.cpp b/AI/EmptyAI/CEmptyAI.cpp index f7b29f3fe..37cd7eb49 100644 --- a/AI/EmptyAI/CEmptyAI.cpp +++ b/AI/EmptyAI/CEmptyAI.cpp @@ -12,12 +12,22 @@ #include "../../lib/CRandomGenerator.h" -void CEmptyAI::init(std::shared_ptr CB) +void CEmptyAI::saveGame(BinarySerializer & h, const int version) +{ +} + +void CEmptyAI::loadGame(BinaryDeserializer & h, const int version) +{ +} + +void CEmptyAI::init(std::shared_ptr ENV, std::shared_ptr CB) { cb = CB; + env = ENV; human=false; playerID = *cb->getMyColor(); } + void CEmptyAI::yourTurn() { cb->endTurn(); diff --git a/AI/EmptyAI/CEmptyAI.h b/AI/EmptyAI/CEmptyAI.h index 5e7f9013f..3c5df9f12 100644 --- a/AI/EmptyAI/CEmptyAI.h +++ b/AI/EmptyAI/CEmptyAI.h @@ -19,7 +19,10 @@ class CEmptyAI : public CGlobalAI std::shared_ptr cb; public: - void init(std::shared_ptr CB) override; + virtual void saveGame(BinarySerializer & h, const int version) override; + virtual void loadGame(BinaryDeserializer & h, const int version) override; + + void init(std::shared_ptr ENV, std::shared_ptr CB) override; void yourTurn() override; void heroGotLevel(const CGHeroInstance *hero, PrimarySkill::PrimarySkill pskill, std::vector &skills, QueryID queryID) override; void commanderGotLevel (const CCommanderInstance * commander, std::vector skills, QueryID queryID) override; diff --git a/AI/EmptyAI/EmptyAI.cbp b/AI/EmptyAI/EmptyAI.cbp index 77c130a35..304884fd5 100644 --- a/AI/EmptyAI/EmptyAI.cbp +++ b/AI/EmptyAI/EmptyAI.cbp @@ -63,7 +63,7 @@ - + @@ -80,9 +80,6 @@ - - - diff --git a/AI/StupidAI/StupidAI.cbp b/AI/StupidAI/StupidAI.cbp index b379d32d9..c11e07b2e 100644 --- a/AI/StupidAI/StupidAI.cbp +++ b/AI/StupidAI/StupidAI.cbp @@ -18,6 +18,7 @@ + @@ -34,6 +35,7 @@ + @@ -41,10 +43,11 @@ @@ -61,13 +64,12 @@ - + - @@ -77,10 +79,6 @@ - - - - - + diff --git a/AI/StupidAI/StupidAI.cpp b/AI/StupidAI/StupidAI.cpp index 2b721f48b..d23030057 100644 --- a/AI/StupidAI/StupidAI.cpp +++ b/AI/StupidAI/StupidAI.cpp @@ -28,9 +28,10 @@ CStupidAI::~CStupidAI() print("destroyed"); } -void CStupidAI::init(std::shared_ptr CB) +void CStupidAI::init(std::shared_ptr ENV, std::shared_ptr CB) { print("init called, saving ptr to IBattleCallback"); + env = ENV; cbc = cb = CB; } @@ -74,10 +75,12 @@ static bool willSecondHexBlockMoreEnemyShooters(const BattleHex &h1, const Battl int shooters[2] = {0}; //count of shooters on hexes for(int i = 0; i < 2; i++) + { for (auto & neighbour : (i ? h2 : h1).neighbouringTiles()) - if(const CStack *s = cbc->battleGetStackByPos(neighbour)) - if(s->getCreature()->isShooting()) - shooters[i]++; + if(const CStack * s = cbc->battleGetStackByPos(neighbour)) + if(s->isShooter()) + shooters[i]++; + } return shooters[0] < shooters[1]; } @@ -173,7 +176,7 @@ void CStupidAI::battleAttack(const BattleAttack *ba) print("battleAttack called"); } -void CStupidAI::battleStacksAttacked(const std::vector & bsa, const std::vector & battleLog) +void CStupidAI::battleStacksAttacked(const std::vector & bsa) { print("battleStacksAttacked called"); } @@ -293,15 +296,3 @@ BattleAction CStupidAI::goTowards(const CStack * stack, std::vector h } } } - -void CStupidAI::saveGame(BinarySerializer & h, const int version) -{ - //TODO to be implemented with saving/loading during the battles - assert(0); -} - -void CStupidAI::loadGame(BinaryDeserializer & h, const int version) -{ - //TODO to be implemented with saving/loading during the battles - assert(0); -} diff --git a/AI/StupidAI/StupidAI.h b/AI/StupidAI/StupidAI.h index 69a603a75..07a2ebcac 100644 --- a/AI/StupidAI/StupidAI.h +++ b/AI/StupidAI/StupidAI.h @@ -18,19 +18,20 @@ class CStupidAI : public CBattleGameInterface { int side; std::shared_ptr cb; + std::shared_ptr env; void print(const std::string &text) const; public: CStupidAI(); ~CStupidAI(); - void init(std::shared_ptr CB) override; + void init(std::shared_ptr ENV, std::shared_ptr CB) override; void actionFinished(const BattleAction &action) override;//occurs AFTER every action taken by any stack or by the hero void actionStarted(const BattleAction &action) override;//occurs BEFORE every action taken by any stack or by the hero BattleAction activeStack(const CStack * stack) override; //called when it's turn of that stack void battleAttack(const BattleAttack *ba) override; //called when stack is performing attack - void battleStacksAttacked(const std::vector & bsa, const std::vector & battleLog) override; //called when stack receives damage (after battleAttack()) + void battleStacksAttacked(const std::vector & bsa) override; //called when stack receives damage (after battleAttack()) void battleEnd(const BattleResult *br) override; //void battleResultsApplied() override; //called when all effects of last battle are applied void battleNewRoundFirst(int round) override; //called at the beginning of each turn before changes are applied; @@ -42,9 +43,6 @@ public: void battleStart(const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool side) override; //called by engine when battle starts; side=0 - left, side=1 - right void battleCatapultAttacked(const CatapultAttack & ca) override; //called when catapult makes an attack - virtual void saveGame(BinarySerializer & h, const int version) override; - virtual void loadGame(BinaryDeserializer & h, const int version) override; - private: BattleAction goTowards(const CStack * stack, std::vector hexes) const; }; diff --git a/AI/VCAI/AIUtility.cpp b/AI/VCAI/AIUtility.cpp index 963c95279..352a13e68 100644 --- a/AI/VCAI/AIUtility.cpp +++ b/AI/VCAI/AIUtility.cpp @@ -279,7 +279,7 @@ creInfo infoFromDC(const dwellingContent & dc) ci.creID = dc.second.size() ? dc.second.back() : CreatureID(-1); //should never be accessed if (ci.creID != -1) { - ci.cre = VLC->creh->creatures[ci.creID]; + ci.cre = VLC->creh->objects[ci.creID]; ci.level = ci.cre->level; //this is cretaure tier, while tryRealize expects dwelling level. Ignore. } else diff --git a/AI/VCAI/FuzzyHelper.cpp b/AI/VCAI/FuzzyHelper.cpp index 252d05c5c..ed5ae9c52 100644 --- a/AI/VCAI/FuzzyHelper.cpp +++ b/AI/VCAI/FuzzyHelper.cpp @@ -139,7 +139,9 @@ float FuzzyHelper::evaluate(Goals::VisitHero & g) { auto obj = ai->myCb->getObj(ObjectInstanceID(g.objid)); //we assume for now that these goals are similar if(!obj) + { return -100; //hero died in the meantime + } else { g.setpriority(Goals::VisitTile(obj->visitablePos()).sethero(g.hero).accept(this)); diff --git a/AI/VCAI/Goals/AbstractGoal.cpp b/AI/VCAI/Goals/AbstractGoal.cpp index 3aabafab3..2021d5071 100644 --- a/AI/VCAI/Goals/AbstractGoal.cpp +++ b/AI/VCAI/Goals/AbstractGoal.cpp @@ -91,7 +91,7 @@ std::string AbstractGoal::name() const //TODO: virtualize } break; case GET_ART_TYPE: - desc = "GET ARTIFACT OF TYPE " + VLC->arth->artifacts[aid]->Name(); + desc = "GET ARTIFACT OF TYPE " + VLC->artifacts()->getByIndex(aid)->getName(); break; case VISIT_TILE: desc = "VISIT TILE " + tile.toString(); diff --git a/AI/VCAI/Goals/AdventureSpellCast.cpp b/AI/VCAI/Goals/AdventureSpellCast.cpp index 28c1c951d..927eee3d1 100644 --- a/AI/VCAI/Goals/AdventureSpellCast.cpp +++ b/AI/VCAI/Goals/AdventureSpellCast.cpp @@ -33,16 +33,16 @@ TSubgoal AdventureSpellCast::whatToDoToAchieve() auto spell = getSpell(); - logAi->trace("Decomposing adventure spell cast of %s for hero %s", spell->name, hero->name); + logAi->trace("Decomposing adventure spell cast of %s for hero %s", spell->getName(), hero->name); - if(!spell->isAdventureSpell()) - throw cannotFulfillGoalException(spell->name + " is not an adventure spell."); + if(!spell->isAdventure()) + throw cannotFulfillGoalException(spell->getName() + " is not an adventure spell."); if(!hero->canCastThisSpell(spell)) - throw cannotFulfillGoalException("Hero can not cast " + spell->name); + throw cannotFulfillGoalException("Hero can not cast " + spell->getName()); if(hero->mana < hero->getSpellCost(spell)) - throw cannotFulfillGoalException("Hero has not enough mana to cast " + spell->name); + throw cannotFulfillGoalException("Hero has not enough mana to cast " + spell->getName()); if(spellID == SpellID::TOWN_PORTAL && town && town->visitingHero) throw cannotFulfillGoalException("The town is already occupied by " + town->visitingHero->name); @@ -75,10 +75,10 @@ void AdventureSpellCast::accept(VCAI * ai) std::string AdventureSpellCast::name() const { - return "AdventureSpellCast " + spellID.toSpell()->name; + return "AdventureSpellCast " + getSpell()->getName(); } std::string AdventureSpellCast::completeMessage() const { - return "Spell casted successfully " + spellID.toSpell()->name; + return "Spell cast successfully " + getSpell()->getName(); } diff --git a/AI/VCAI/Goals/GatherArmy.cpp b/AI/VCAI/Goals/GatherArmy.cpp index df6620a81..e81a41413 100644 --- a/AI/VCAI/Goals/GatherArmy.cpp +++ b/AI/VCAI/Goals/GatherArmy.cpp @@ -158,7 +158,7 @@ TGoalVec GatherArmy::getAllPossibleSubgoals() { for(auto & creatureID : creLevel.second) { - auto creature = VLC->creh->creatures[creatureID]; + auto creature = VLC->creh->objects[creatureID]; if(ai->ah->freeResources().canAfford(creature->cost)) objs.push_back(obj); //TODO: reserve resources? } diff --git a/AI/VCAI/Goals/GatherTroops.cpp b/AI/VCAI/Goals/GatherTroops.cpp index 19cb9dcad..642429dd7 100644 --- a/AI/VCAI/Goals/GatherTroops.cpp +++ b/AI/VCAI/Goals/GatherTroops.cpp @@ -93,7 +93,7 @@ TGoalVec GatherTroops::getAllPossibleSubgoals() continue; } - auto creature = VLC->creh->creatures[objid]; + auto creature = VLC->creh->objects[objid]; if(t->subID == creature->faction) //TODO: how to force AI to build unupgraded creatures? :O { auto creatures = vstd::tryAt(t->town->creatures, creature->level - 1); @@ -110,7 +110,7 @@ TGoalVec GatherTroops::getAllPossibleSubgoals() solutions.push_back(sptr(BuyArmy(t, creature->AIValue * this->value).setobjid(objid))); } /*else //disable random building requests for now - this code needs to know a lot of town/resource context to do more good than harm - { + { return sptr(BuildThis(bid, t).setpriority(priority)); }*/ } @@ -129,7 +129,7 @@ TGoalVec GatherTroops::getAllPossibleSubgoals() { for(auto type : creature.second) { - if(type == objid && ai->ah->freeResources().canAfford(VLC->creh->creatures[type]->cost)) + if(type == objid && ai->ah->freeResources().canAfford(VLC->creh->objects[type]->cost)) vstd::concatenate(solutions, ai->ah->howToVisitObj(obj)); } } diff --git a/AI/VCAI/MapObjectsEvaluator.cpp b/AI/VCAI/MapObjectsEvaluator.cpp index 96badfd53..e528d4d37 100644 --- a/AI/VCAI/MapObjectsEvaluator.cpp +++ b/AI/VCAI/MapObjectsEvaluator.cpp @@ -41,7 +41,7 @@ MapObjectsEvaluator::MapObjectsEvaluator() objectDatabase[CompoundMapObjectID(primaryID, secondaryID)] = 0; } } - } + } } } @@ -62,7 +62,7 @@ boost::optional MapObjectsEvaluator::getObjectValue(const CGObjectInstance { //special case handling: in-game heroes have hero ID as object subID, but when reading configs available hero object subID's are hero classes auto hero = dynamic_cast(obj); - return getObjectValue(obj->ID, hero->type->heroClass->id); + return getObjectValue(obj->ID, hero->type->heroClass->getIndex()); } else if(obj->ID == Obj::PRISON) { @@ -77,7 +77,7 @@ boost::optional MapObjectsEvaluator::getObjectValue(const CGObjectInstance { for(auto & creatureID : creLevel.second) { - auto creature = VLC->creh->creatures[creatureID]; + auto creature = VLC->creh->objects[creatureID]; aiValue += (creature->AIValue * creature->growth); } } diff --git a/AI/VCAI/Pathfinding/AIPathfinder.cpp b/AI/VCAI/Pathfinding/AIPathfinder.cpp index 878a89892..545535111 100644 --- a/AI/VCAI/Pathfinding/AIPathfinder.cpp +++ b/AI/VCAI/Pathfinding/AIPathfinder.cpp @@ -60,7 +60,7 @@ void AIPathfinder::updatePaths(std::vector heroes) cb->calculatePaths(config, hero); }; - std::vector calculationTasks; + std::vector calculationTasks; for(HeroPtr hero : heroes) { diff --git a/AI/VCAI/VCAI.cbp b/AI/VCAI/VCAI.cbp index f63dbc85e..5b0c0548e 100644 --- a/AI/VCAI/VCAI.cbp +++ b/AI/VCAI/VCAI.cbp @@ -78,7 +78,7 @@ - + @@ -185,9 +185,6 @@ - - - diff --git a/AI/VCAI/VCAI.cpp b/AI/VCAI/VCAI.cpp index f7fb1f7ed..b73440617 100644 --- a/AI/VCAI/VCAI.cpp +++ b/AI/VCAI/VCAI.cpp @@ -579,9 +579,10 @@ void VCAI::showWorldViewEx(const std::vector & objectPositions) NET_EVENT_HANDLER; } -void VCAI::init(std::shared_ptr CB) +void VCAI::init(std::shared_ptr ENV, std::shared_ptr CB) { LOG_TRACE(logAi); + env = ENV; myCb = CB; cbc = CB; @@ -868,7 +869,7 @@ void VCAI::mainLoop() goalsToRemove.clear(); elementarGoals.clear(); ultimateGoalsFromBasic.clear(); - + ah->updatePaths(getMyHeroes()); logAi->debug("Main loop: decomposing %i basic goals", basicGoals.size()); @@ -1082,7 +1083,7 @@ void VCAI::pickBestCreatures(const CArmedInstance * destinationArmy, const CArme && (!destinationArmy->hasStackAtSlot(i) || destinationArmy->getCreature(i) == targetCreature)) { auto weakest = ah->getWeakestCreature(bestArmy); - + if(weakest->creature == targetCreature) { if(1 == source->getStackCount(j)) @@ -1233,7 +1234,7 @@ void VCAI::recruitCreatures(const CGDwelling * d, const CArmedInstance * recruit int count = d->creatures[i].first; CreatureID creID = d->creatures[i].second.back(); - vstd::amin(count, ah->freeResources() / VLC->creh->creatures[creID]->cost); + vstd::amin(count, ah->freeResources() / VLC->creh->objects[creID]->cost); if(count > 0) cb->recruitCreatures(d, recruiter, creID, count, i); } diff --git a/AI/VCAI/VCAI.h b/AI/VCAI/VCAI.h index ab7891317..c9a560305 100644 --- a/AI/VCAI/VCAI.h +++ b/AI/VCAI/VCAI.h @@ -139,7 +139,7 @@ public: std::string getBattleAIName() const override; - void init(std::shared_ptr CB) override; + void init(std::shared_ptr ENV, std::shared_ptr CB) override; void yourTurn() override; void heroGotLevel(const CGHeroInstance * hero, PrimarySkill::PrimarySkill pskill, std::vector & skills, QueryID queryID) override; //pskill is gained primary skill, interface has to choose one of given skills and call callback with selection id diff --git a/CCallback.cpp b/CCallback.cpp index b01c2f36d..81b3e9274 100644 --- a/CCallback.cpp +++ b/CCallback.cpp @@ -22,7 +22,6 @@ #include "lib/CHeroHandler.h" #include "lib/NetPacks.h" #include "client/mapHandler.h" -#include "lib/spells/CSpellHandler.h" #include "lib/CArtHandler.h" #include "lib/GameConstants.h" #include "lib/CPlayerState.h" @@ -328,26 +327,21 @@ int CCallback::mergeOrSwapStacks(const CArmedInstance *s1, const CArmedInstance return swapCreatures(s1, s2, p1, p2); } -void CCallback::registerGameInterface(std::shared_ptr gameEvents) -{ - cl->additionalPlayerInts[*player].push_back(gameEvents); -} - void CCallback::registerBattleInterface(std::shared_ptr battleEvents) { cl->additionalBattleInts[*player].push_back(battleEvents); } -void CCallback::unregisterGameInterface(std::shared_ptr gameEvents) -{ - cl->additionalPlayerInts[*player] -= gameEvents; -} - void CCallback::unregisterBattleInterface(std::shared_ptr battleEvents) { cl->additionalBattleInts[*player] -= battleEvents; } +scripting::Pool * CBattleCallback::getContextPool() const +{ + return cl->getGlobalContextPool(); +} + CBattleCallback::CBattleCallback(boost::optional Player, CClient *C ) { player = Player; diff --git a/CCallback.h b/CCallback.h index 739b13564..ef83589ed 100644 --- a/CCallback.h +++ b/CCallback.h @@ -10,6 +10,7 @@ #pragma once #include "lib/CGameInfoCallback.h" +#include "lib/battle/CPlayerBattleCallback.h" #include "lib/int3.h" // for int3 class CGHeroInstance; @@ -92,11 +93,12 @@ public: int battleMakeAction(const BattleAction * action) override;//for casting spells by hero - DO NOT use it for moving active stack bool battleMakeTacticAction(BattleAction * action) override; // performs tactic phase actions + scripting::Pool * getContextPool() const override; + friend class CCallback; friend class CClient; }; -class CPlayerInterface; class CCallback : public CPlayerSpecificInfoCallback, public IGameActionCallback, public CBattleCallback { public: @@ -111,9 +113,7 @@ public: virtual void calculatePaths(const CGHeroInstance *hero, CPathsInfo &out); //Set of metrhods that allows adding more interfaces for this player that'll receive game event call-ins. - void registerGameInterface(std::shared_ptr gameEvents); void registerBattleInterface(std::shared_ptr battleEvents); - void unregisterGameInterface(std::shared_ptr gameEvents); void unregisterBattleInterface(std::shared_ptr battleEvents); //commands diff --git a/CI/linux/before_install.sh b/CI/linux/before_install.sh index 03247aa38..7799c3f58 100644 --- a/CI/linux/before_install.sh +++ b/CI/linux/before_install.sh @@ -20,6 +20,7 @@ sudo apt-get install -qq cmake ninja-build libboost1.54-all-dev zlib1g-dev sudo apt-get install -qq libsdl2-dev libsdl2-image-dev libsdl2-mixer-dev libsdl2-ttf-dev sudo apt-get install -qq libavformat-dev libswscale-dev sudo apt-get install -qq qt57declarative +sudo apt-get install -qq libluajit-5.1-dev #setup compiler source /opt/qt57/bin/qt57-env.sh diff --git a/CI/mac/before_install.sh b/CI/mac/before_install.sh index 681f6c59e..df248f45b 100644 --- a/CI/mac/before_install.sh +++ b/CI/mac/before_install.sh @@ -1,7 +1,7 @@ #!/bin/sh brew update -brew install smpeg2 libpng freetype sdl2 sdl2_ttf sdl2_image qt5 ffmpeg ninja +brew install smpeg2 libpng freetype sdl2 sdl2_ttf sdl2_image qt5 ffmpeg ninja luajit brew install sdl2_mixer export CMAKE_PREFIX_PATH="/usr/local/opt/qt5:$CMAKE_PREFIX_PATH" diff --git a/CI/mxe/before_install.sh b/CI/mxe/before_install.sh index 23b72bd92..82b969a34 100644 --- a/CI/mxe/before_install.sh +++ b/CI/mxe/before_install.sh @@ -4,17 +4,17 @@ sudo apt-get install -qq nsis ninja-build # MXE repository was too slow for Travis far too often -wget https://github.com/vcmi/vcmi-deps-mxe/releases/download/2018-02-10/mxe-$MXE_TARGET-2018-02-10.tar -tar -xvf mxe-$MXE_TARGET-2018-02-10.tar +wget https://github.com/vcmi/vcmi-deps-mxe/releases/download/2019-06-28/mxe-i686-w64-mingw32.shared-2019-06-28.tar +tar -xvf mxe-i686-w64-mingw32.shared-2019-06-28.tar sudo dpkg -i mxe-*.deb sudo apt-get install -f --yes if false; then # Add MXE repository and key -echo "deb http://pkg.mxe.cc/repos/apt/debian wheezy main" \ +echo "deb http://pkg.mxe.cc/repos/apt trusty main" \ | sudo tee /etc/apt/sources.list.d/mxeapt.list -sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys D43A795B73B16ABE9643FE1AFD8FFF16DB45C6AB +sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 86B72ED9 # Install needed packages sudo apt-get update -qq @@ -30,7 +30,8 @@ mxe-$MXE_TARGET-sdl2-mixer \ mxe-$MXE_TARGET-sdl2-ttf \ mxe-$MXE_TARGET-ffmpeg \ mxe-$MXE_TARGET-qt \ -mxe-$MXE_TARGET-qtbase +mxe-$MXE_TARGET-qtbase \ +mxe-i686-w64-mingw32.static-luajit fi # Disable diff --git a/CMakeLists.txt b/CMakeLists.txt index efd020251..e41dbeab7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -44,7 +44,8 @@ set(VCMI_VERSION_MAJOR 0) set(VCMI_VERSION_MINOR 99) set(VCMI_VERSION_PATCH 0) -option(ENABLE_ERM "Enable compilation of ERM scripting module" OFF) +option(ENABLE_ERM "Enable compilation of ERM scripting module" ON) +option(ENABLE_LUA "Enable compilation of LUA scripting module" ON) option(ENABLE_LAUNCHER "Enable compilation of launcher" ON) option(ENABLE_TEST "Enable compilation of unit tests" ON) option(ENABLE_PCH "Enable compilation using precompiled headers" ON) @@ -122,7 +123,6 @@ if(APPLE) endif(APPLE) if(WIN32) - add_definitions(-DBOOST_THREAD_USE_LIB) # Windows Vista or newer for FuzzyLite 6 to compile add_definitions(-D_WIN32_WINNT=0x0600) @@ -231,6 +231,21 @@ if(ENABLE_LAUNCHER) find_package(Qt5Network REQUIRED) endif() +if(ENABLE_LUA) + # MXE paths hardcoded for current dependencies pack - tried and could not make it work another way + if((MINGW) AND (${CMAKE_CROSSCOMPILING}) AND (DEFINED MSYS)) + set(LUA_INCLUDE_DIR "/usr/lib/mxe/usr/i686-w64-mingw32.static/include/luajit-2.0") + set(LUA_LIBRARY "/usr/lib/mxe/usr/i686-w64-mingw32.static/lib/libluajit-5.1.a") + endif() + find_package(LuaJIT) + if(LUAJIT_FOUND) + message(STATUS "Using LuaJIT provided by system") + else() + message(STATUS "Cannot find LuaJIT! Fallback to using usual Lua.") + find_package(Lua REQUIRED) + endif() +endif() + ############################################ # Output directories # ############################################ @@ -294,6 +309,9 @@ set(SCRIPTING_LIB_DIR "${LIB_DIR}/scripting") if(ENABLE_ERM) add_subdirectory(scripting/erm) endif() +if(ENABLE_LUA) + add_subdirectory(scripting/lua) +endif() if(NOT MINIZIP_FOUND) add_subdirectory_with_folder("3rdparty" lib/minizip) set(MINIZIP_LIBRARIES minizip) @@ -315,6 +333,7 @@ endif() ####################################### install(DIRECTORY config DESTINATION ${DATA_DIR}) +install(DIRECTORY scripts DESTINATION ${DATA_DIR}) install(DIRECTORY Mods DESTINATION ${DATA_DIR}) # that script is useless for Windows diff --git a/Global.h b/Global.h index 506f0fc51..ed0bc63dd 100644 --- a/Global.h +++ b/Global.h @@ -727,24 +727,6 @@ namespace vstd } using boost::math::round; - - static std::pair splitStringToPair(std::string input, char separator) - { - std::pair ret; - size_t splitPos = input.find(separator); - - if (splitPos == std::string::npos) - { - ret.first.clear(); - ret.second = input; - } - else - { - ret.first = input.substr(0, splitPos); - ret.second = input.substr(splitPos + 1); - } - return ret; - } } using vstd::operator-=; using vstd::make_unique; diff --git a/Mods/vcmi/Data/s/std.verm b/Mods/vcmi/Data/s/std.verm deleted file mode 100644 index 865997ce7..000000000 --- a/Mods/vcmi/Data/s/std.verm +++ /dev/null @@ -1,40 +0,0 @@ -VERM -; standard verm file, global engine things should be put here - -!?PI; -; example 1 --- Hello World -![print ^Hello world!^] - -; example 2 --- simple arithmetics -![defun add [x y] [+ x y]] -![print [add 2 3]] - -; example 3 --- semantic macros -![defmacro do-n-times [times body] - `[progn - [setq do-counter 0] - [setq do-max ,times] - [do [< do-counter do-max] - [progn - [setq do-counter [+ do-counter 1]] - ,body - ] - ] - ] -] -![do-n-times 4 [print ^tekst\n^]] - - -; example 4 --- conditional expression -![if [> 2 1] [print ^Wieksze^] [print ^Mniejsze^]] - -; example 5 --- lambda expressions -![[lambda [x y] [if [> x y] [print ^wieksze^] [print ^mniejsze^]]] 2 3] - -; example 6 --- resursion -![defun factorial [n] - [if [= n 0] 1 - [* n [factorial [- n 1]]] - ] -] -![print [factorial 8]] \ No newline at end of file diff --git a/Mods/vcmi/Data/s/testy.erm b/Mods/vcmi/Data/s/testy.erm deleted file mode 100644 index 082c1891e..000000000 --- a/Mods/vcmi/Data/s/testy.erm +++ /dev/null @@ -1,14 +0,0 @@ -ZVSE -!?PI; - !!VRv2777:S4; - !!DO1/0/5/1&v2777<>1:P0; - -!?FU1; - !!VRv2778:Sx16%2; - !!IF&x16>3:M^Hello world number %X16! To duza liczba^; - !!IF&v2778==0&x16<=3:M^Hello world number %X16! To mala parzysta liczba^; - !!IF&v2778==1&x16<=3:M^Hello world number %X16! To mala nieparzysta liczba^; - -!?PI; - !!VRz10:S^Composed hello ^; - !!IF:M^%Z10%%world%%, v2777=%V2777, v2778=%V2778!^; \ No newline at end of file diff --git a/client/CGameInfo.cpp b/client/CGameInfo.cpp index ac607a8c3..2fe9f4fd6 100644 --- a/client/CGameInfo.cpp +++ b/client/CGameInfo.cpp @@ -9,8 +9,6 @@ */ #include "StdInc.h" #include "CGameInfo.h" -#include "../lib/CSkillHandler.h" -#include "../lib/CGeneralTextHandler.h" #include "../lib/VCMI_Lib.h" @@ -24,13 +22,14 @@ CGameInfo::CGameInfo() generaltexth = nullptr; mh = nullptr; townh = nullptr; + globalServices = nullptr; } void CGameInfo::setFromLib() { + globalServices = VLC; modh = VLC->modh; generaltexth = VLC->generaltexth; - arth = VLC->arth; creh = VLC->creh; townh = VLC->townh; heroh = VLC->heroh; @@ -39,3 +38,58 @@ void CGameInfo::setFromLib() skillh = VLC->skillh; objtypeh = VLC->objtypeh; } + +const ArtifactService * CGameInfo::artifacts() const +{ + return globalServices->artifacts(); +} + +const CreatureService * CGameInfo::creatures() const +{ + return globalServices->creatures(); +} + +const FactionService * CGameInfo::factions() const +{ + return globalServices->factions(); +} + +const HeroClassService * CGameInfo::heroClasses() const +{ + return globalServices->heroClasses(); +} + +const HeroTypeService * CGameInfo::heroTypes() const +{ + return globalServices->heroTypes(); +} + +const scripting::Service * CGameInfo::scripts() const +{ + return globalServices->scripts(); +} + +const spells::Service * CGameInfo::spells() const +{ + return globalServices->spells(); +} + +const SkillService * CGameInfo::skills() const +{ + return globalServices->skills(); +} + +void CGameInfo::updateEntity(Metatype metatype, int32_t index, const JsonNode & data) +{ + logGlobal->error("CGameInfo::updateEntity call is not expected."); +} + +spells::effects::Registry * CGameInfo::spellEffects() +{ + return nullptr; +} + +const spells::effects::Registry * CGameInfo::spellEffects() const +{ + return globalServices->spellEffects(); +} diff --git a/client/CGameInfo.h b/client/CGameInfo.h index 371b487f3..d9947c1d4 100644 --- a/client/CGameInfo.h +++ b/client/CGameInfo.h @@ -9,11 +9,12 @@ */ #pragma once +#include + #include "../lib/ConstTransitivePtr.h" class CModHandler; class CMapHandler; -class CArtHandler; class CHeroHandler; class CCreatureHandler; class CSpellHandler; @@ -48,11 +49,25 @@ extern CClientState * CCS; /// CGameInfo class /// for allowing different functions for accessing game informations -class CGameInfo +class CGameInfo : public Services { public: + const ArtifactService * artifacts() const override; + const CreatureService * creatures() const override; + const FactionService * factions() const override; + const HeroClassService * heroClasses() const override; + const HeroTypeService * heroTypes() const override; + const scripting::Service * scripts() const override; + const spells::Service * spells() const override; + const SkillService * skills() const override; + + void updateEntity(Metatype metatype, int32_t index, const JsonNode & data) override; + + const spells::effects::Registry * spellEffects() const override; + spells::effects::Registry * spellEffects() override; + + ConstTransitivePtr modh; //public? - ConstTransitivePtr arth; ConstTransitivePtr heroh; ConstTransitivePtr creh; ConstTransitivePtr spellh; @@ -65,8 +80,8 @@ public: void setFromLib(); - friend class CClient; - CGameInfo(); +private: + const Services * globalServices; }; extern const CGameInfo* CGI; diff --git a/client/CMT.cpp b/client/CMT.cpp index 5cc09bb18..2fd21064e 100644 --- a/client/CMT.cpp +++ b/client/CMT.cpp @@ -14,6 +14,8 @@ #include +#include + #include "gui/SDL_Extensions.h" #include "CGameInfo.h" #include "mapHandler.h" @@ -46,9 +48,9 @@ #include "../lib/NetPacks.h" #include "CMessage.h" #include "../lib/CModHandler.h" +#include "../lib/ScriptHandler.h" #include "../lib/CTownHandler.h" #include "../lib/CArtHandler.h" -#include "../lib/CScriptingModule.h" #include "../lib/GameConstants.h" #include "gui/CGuiHandler.h" #include "../lib/logging/CBasicLogConfigurator.h" @@ -95,14 +97,13 @@ SDL_Surface *screen = nullptr, //main screen surface *screen2 = nullptr, //and hlp surface (used to store not-active interfaces layer) *screenBuf = screen; //points to screen (if only advmapint is present) or screen2 (else) - should be used when updating controls which are not regularly redrawed -std::queue events; +std::queue SDLEventsQueue; boost::mutex eventsM; static po::variables_map vm; //static bool setResolution = false; //set by event handling thread after resolution is adjusted -static bool ermInteractiveMode = false; //structurize when time is right void processCommand(const std::string &message); static void setScreenRes(int w, int h, int bpp, bool fullscreen, int displayIndex, bool resetVideo=true); void playIntro(); @@ -576,29 +577,10 @@ void processCommand(const std::string &message) // if(LOCPLINT && LOCPLINT->cingconsole) // LOCPLINT->cingconsole->print(message); - if(ermInteractiveMode) - { - if(cn == "exit") - { - ermInteractiveMode = false; - return; - } - else - { - if(CSH->client && CSH->client->erm) - CSH->client->erm->executeUserCommand(message); - std::cout << "erm>"; - } - } - else if(message==std::string("die, fool")) + if(message==std::string("die, fool")) { exit(EXIT_SUCCESS); } - else if(cn == "erm") - { - ermInteractiveMode = true; - std::cout << "erm>"; - } else if(cn==std::string("activate")) { int what; @@ -725,6 +707,28 @@ void processCommand(const std::string &message) std::cout << "\rExtracting done :)\n"; std::cout << " Extracted files can be found in " << outPath << " directory\n"; } + else if(message=="get scripts") + { + std::cout << "Command accepted.\t"; + + const bfs::path outPath = + VCMIDirs::get().userCachePath() / "extracted" / "scripts"; + + bfs::create_directories(outPath); + + for(auto & kv : VLC->scriptHandler->objects) + { + std::string name = kv.first; + boost::algorithm::replace_all(name,":","_"); + + const scripting::ScriptImpl * script = kv.second.get(); + bfs::path filePath = outPath / (name + ".lua"); + bfs::ofstream file(filePath); + file << script->getSource(); + } + std::cout << "\rExtracting done :)\n"; + std::cout << " Extracted files can be found in " << outPath << " directory\n"; + } else if(message=="get txt") { std::cout << "Command accepted.\t"; @@ -891,7 +895,7 @@ void processCommand(const std::string &message) { YourTurn yt; yt.player = player; - yt.daysWithoutCastle = CSH->client->getPlayer(player)->daysWithoutCastle; + yt.daysWithoutCastle = CSH->client->getPlayerState(player)->daysWithoutCastle; yt.applyCl(CSH->client); }; @@ -1360,7 +1364,7 @@ static void handleEvent(SDL_Event & ev) { boost::unique_lock lock(eventsM); - events.push(ev); + SDLEventsQueue.push(ev); } } diff --git a/client/CMusicHandler.cpp b/client/CMusicHandler.cpp index 1a9ec649e..a95e360a6 100644 --- a/client/CMusicHandler.cpp +++ b/client/CMusicHandler.cpp @@ -13,8 +13,6 @@ #include "CMusicHandler.h" #include "CGameInfo.h" #include "SDLRWwrapper.h" -#include "../lib/CCreatureHandler.h" -#include "../lib/spells/CSpellHandler.h" #include "../lib/JsonNode.h" #include "../lib/GameConstants.h" #include "../lib/filesystem/Filesystem.h" diff --git a/client/CMusicHandler.h b/client/CMusicHandler.h index df1ede0a2..36fe6f2ec 100644 --- a/client/CMusicHandler.h +++ b/client/CMusicHandler.h @@ -12,7 +12,6 @@ #include "../lib/CConfigHandler.h" #include "../lib/CSoundBase.h" -class CSpell; struct _Mix_Music; struct SDL_RWops; typedef struct _Mix_Music Mix_Music; diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index f66f9221b..0f3b98866 100644 --- a/client/CPlayerInterface.cpp +++ b/client/CPlayerInterface.cpp @@ -8,6 +8,9 @@ * */ #include "StdInc.h" + +#include + #include "windows/CAdvmapInterface.h" #include "battle/CBattleInterface.h" #include "battle/CBattleInterfaceClasses.h" @@ -82,7 +85,7 @@ using namespace CSDL_Ext; void processCommand(const std::string &message, CClient *&client); -extern std::queue events; +extern std::queue SDLEventsQueue; extern boost::mutex eventsM; boost::recursive_mutex * CPlayerInterface::pim = new boost::recursive_mutex; @@ -144,9 +147,10 @@ CPlayerInterface::~CPlayerInterface() if (LOCPLINT == this) LOCPLINT = nullptr; } -void CPlayerInterface::init(std::shared_ptr CB) +void CPlayerInterface::init(std::shared_ptr ENV, std::shared_ptr CB) { cb = CB; + env = ENV; initializeHeroTownList(); // always recreate advmap interface to avoid possible memory-corruption bugs @@ -372,10 +376,10 @@ void CPlayerInterface::heroMoved(const TryMoveHero & details) //check if user cancelled movement { boost::unique_lock un(eventsM); - while(!events.empty()) + while(!SDLEventsQueue.empty()) { - SDL_Event ev = events.front(); - events.pop(); + SDL_Event ev = SDLEventsQueue.front(); + SDLEventsQueue.pop(); switch(ev.type) { case SDL_MOUSEBUTTONDOWN: @@ -692,7 +696,7 @@ void CPlayerInterface::battleStart(const CCreatureSet *army1, const CCreatureSet if (settings["adventure"]["quickCombat"].Bool()) { autofightingAI = CDynLibHandler::getNewBattleAI(settings["server"]["friendlyAI"].String()); - autofightingAI->init(cb); + autofightingAI->init(env, cb); autofightingAI->battleStart(army1, army2, int3(0,0,0), hero1, hero2, side); isAutoFightOn = true; cb->registerBattleInterface(autofightingAI); @@ -707,7 +711,7 @@ void CPlayerInterface::battleStart(const CCreatureSet *army1, const CCreatureSet BATTLE_EVENT_POSSIBLE_RETURN; } -void CPlayerInterface::battleUnitsChanged(const std::vector & units, const std::vector & customEffects, const std::vector & battleLog) +void CPlayerInterface::battleUnitsChanged(const std::vector & units, const std::vector & customEffects) { EVENT_HANDLER_CALLED_BY_CLIENT; BATTLE_EVENT_POSSIBLE_RETURN; @@ -769,7 +773,6 @@ void CPlayerInterface::battleUnitsChanged(const std::vector & units } battleInt->displayCustomEffects(customEffects); - battleInt->displayBattleLog(battleLog); } void CPlayerInterface::battleObstaclesChanged(const std::vector & obstacles) @@ -919,6 +922,14 @@ void CPlayerInterface::battleEnd(const BattleResult *br) battleInt->battleFinished(*br); } +void CPlayerInterface::battleLogMessage(const std::vector & lines) +{ + EVENT_HANDLER_CALLED_BY_CLIENT; + BATTLE_EVENT_POSSIBLE_RETURN; + + battleInt->displayBattleLog(lines); +} + void CPlayerInterface::battleStackMoved(const CStack * stack, std::vector dest, int distance) { EVENT_HANDLER_CALLED_BY_CLIENT; @@ -948,7 +959,7 @@ void CPlayerInterface::battleTriggerEffect (const BattleTriggerEffect & bte) RETURN_IF_QUICK_COMBAT; battleInt->battleTriggerEffect(bte); } -void CPlayerInterface::battleStacksAttacked(const std::vector & bsa, const std::vector & battleLog) +void CPlayerInterface::battleStacksAttacked(const std::vector & bsa) { EVENT_HANDLER_CALLED_BY_CLIENT; BATTLE_EVENT_POSSIBLE_RETURN; @@ -977,7 +988,7 @@ void CPlayerInterface::battleStacksAttacked(const std::vectorstacksAreAttacked(arg, battleLog); + battleInt->stacksAreAttacked(arg); } void CPlayerInterface::battleAttack(const BattleAttack * ba) { @@ -1424,29 +1435,20 @@ void CPlayerInterface::showGarrisonDialog( const CArmedInstance *up, const CGHer * Shows the dialog that appears when right-clicking an artifact that can be assembled * into a combinational one on an artifact screen. Does not require the combination of * artifacts to be legal. - * @param artifactID ID of a constituent artifact. - * @param assembleTo ID of artifact to assemble a constituent into, not used when assemble - * is false. - * @param assemble True if the artifact is to be assembled, false if it is to be disassembled. */ -void CPlayerInterface::showArtifactAssemblyDialog (ui32 artifactID, ui32 assembleTo, bool assemble, CFunctionList onYes, CFunctionList onNo) +void CPlayerInterface::showArtifactAssemblyDialog(const Artifact * artifact, const Artifact * assembledArtifact, CFunctionList onYes) { - const CArtifact &artifact = *CGI->arth->artifacts[artifactID]; - std::string text = artifact.Description(); + std::string text = artifact->getDescription(); text += "\n\n"; std::vector> scs; - if(assemble) + if(assembledArtifact) { - const CArtifact &assembledArtifact = *CGI->arth->artifacts[assembleTo]; - // You possess all of the components to... - text += boost::str(boost::format(CGI->generaltexth->allTexts[732]) % assembledArtifact.Name()); + text += boost::str(boost::format(CGI->generaltexth->allTexts[732]) % assembledArtifact->getName()); // Picture of assembled artifact at bottom. - auto sc = std::make_shared(CComponent::artifact, assembledArtifact.id, 0); - //sc->description = assembledArtifact.Description(); - //sc->subtitle = assembledArtifact.Name(); + auto sc = std::make_shared(CComponent::artifact, assembledArtifact->getIndex(), 0); scs.push_back(sc); } else @@ -1455,7 +1457,7 @@ void CPlayerInterface::showArtifactAssemblyDialog (ui32 artifactID, ui32 assembl text += CGI->generaltexth->allTexts[733]; } - showYesNoDialog(text, onYes, onNo, scs); + showYesNoDialog(text, onYes, nullptr, scs); } void CPlayerInterface::requestRealized( PackageApplied *pa ) @@ -1612,8 +1614,8 @@ void CPlayerInterface::playerBlocked(int reason, bool start) { if(reason == PlayerBlocked::EReason::UPCOMING_BATTLE) { - if(CSH->howManyPlayerInterfaces() > 1 && LOCPLINT != this && LOCPLINT->makingTurn == false) - { + if(CSH->howManyPlayerInterfaces() > 1 && LOCPLINT != this && LOCPLINT->makingTurn == false) + { //one of our players who isn't last in order got attacked not by our another player (happens for example in hotseat mode) boost::unique_lock lock(eventsM); //TODO: copied from yourTurn, no idea if it's needed LOCPLINT = this; @@ -2236,13 +2238,13 @@ void CPlayerInterface::advmapSpellCast(const CGHeroInstance * caster, int spellI if(spellID == SpellID::FLY || spellID == SpellID::WATER_WALK) eraseCurrentPathOf(caster, false); - const CSpell * spell = CGI->spellh->objects.at(spellID); + const spells::Spell * spell = CGI->spells()->getByIndex(spellID); if(spellID == SpellID::VIEW_EARTH) { //TODO: implement on server side - int level = caster->getSpellSchoolLevel(spell); - adventureInt->worldViewOptions.showAllTerrain = (level>2); + const auto level = caster->getSpellSchoolLevel(spell); + adventureInt->worldViewOptions.showAllTerrain = (level > 2); } auto castSoundPath = spell->getCastSound(); @@ -2360,7 +2362,7 @@ void CPlayerInterface::acceptTurn() components.push_back(Component(Component::FLAG, playerColor.getNum(), 0, 0)); MetaString text; - const auto & optDaysWithoutCastle = cb->getPlayer(playerColor)->daysWithoutCastle; + const auto & optDaysWithoutCastle = cb->getPlayerState(playerColor)->daysWithoutCastle; if(optDaysWithoutCastle) { @@ -2646,9 +2648,9 @@ bool CPlayerInterface::capturedAllEvents() if (ignoreEvents) { boost::unique_lock un(eventsM); - while(!events.empty()) + while(!SDLEventsQueue.empty()) { - events.pop(); + SDLEventsQueue.pop(); } return true; } diff --git a/client/CPlayerInterface.h b/client/CPlayerInterface.h index 528b68dd4..10d06f80f 100644 --- a/client/CPlayerInterface.h +++ b/client/CPlayerInterface.h @@ -19,6 +19,8 @@ #define sprintf_s snprintf #endif +class Artifact; + class CButton; class CToggleGroup; struct TryMoveHero; @@ -62,7 +64,9 @@ namespace boost class CPlayerInterface : public CGameInterface, public IUpdateable { const CArmedInstance * currentSelection; + public: + std::shared_ptr env; ObjectInstanceID destinationTeleport; //contain -1 or object id if teleportation int3 destinationTeleportPos; @@ -183,21 +187,22 @@ public: void battleEnd(const BattleResult *br) override; //end of battle void battleNewRoundFirst(int round) override; //called at the beginning of each turn before changes are applied; used for HP regen handling void battleNewRound(int round) override; //called at the beginning of each turn, round=-1 is the tactic phase, round=0 is the first "normal" turn + void battleLogMessage(const std::vector & lines) override; void battleStackMoved(const CStack * stack, std::vector dest, int distance) override; void battleSpellCast(const BattleSpellCast *sc) override; void battleStacksEffectsSet(const SetStackEffect & sse) override; //called when a specific effect is set to stacks void battleTriggerEffect(const BattleTriggerEffect & bte) override; //various one-shot effect - void battleStacksAttacked(const std::vector & bsa, const std::vector & battleLog) override; + void battleStacksAttacked(const std::vector & bsa) override; void battleStartBefore(const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2) override; //called by engine just before battle starts; side=0 - left, side=1 - right void battleStart(const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool side) override; //called by engine when battle starts; side=0 - left, side=1 - right - void battleUnitsChanged(const std::vector & units, const std::vector & customEffects, const std::vector & battleLog) override; + void battleUnitsChanged(const std::vector & units, const std::vector & customEffects) override; void battleObstaclesChanged(const std::vector & obstacles) override; void battleCatapultAttacked(const CatapultAttack & ca) override; //called when catapult makes an attack void battleGateStateChanged(const EGateState state) override; void yourTacticPhase(int distance) override; //-------------// - void showArtifactAssemblyDialog(ui32 artifactID, ui32 assembleTo, bool assemble, CFunctionList onYes, CFunctionList onNo); + void showArtifactAssemblyDialog(const Artifact * artifact, const Artifact * assembledArtifact, CFunctionList onYes); void garrisonsChanged(std::vector objs); void garrisonChanged(const CGObjectInstance * obj); void heroKilled(const CGHeroInstance* hero); @@ -210,7 +215,7 @@ public: void openTownWindow(const CGTownInstance * town); //shows townscreen void openHeroWindow(const CGHeroInstance * hero); //shows hero window with given hero void updateInfo(const CGObjectInstance * specific); - void init(std::shared_ptr CB) override; + void init(std::shared_ptr ENV, std::shared_ptr CB) override; int3 repairScreenPos(int3 pos); //returns position closest to pos we can center screen on void activateForSpectator(); // TODO: spectator probably need own player interface class diff --git a/client/CServerHandler.cpp b/client/CServerHandler.cpp index d38793d46..8a010a629 100644 --- a/client/CServerHandler.cpp +++ b/client/CServerHandler.cpp @@ -45,6 +45,8 @@ #include #include "../lib/serializer/Cast.h" +#include + template class CApplyOnLobby; #ifdef VCMI_ANDROID diff --git a/client/Client.cpp b/client/Client.cpp index 4839b5122..45ccaf9b8 100644 --- a/client/Client.cpp +++ b/client/Client.cpp @@ -44,11 +44,12 @@ #include "lobby/CBonusSelection.h" #include "battle/CBattleInterface.h" #include "../lib/CThreadHelper.h" -#include "../lib/CScriptingModule.h" #include "../lib/registerTypes/RegisterTypes.h" #include "gui/CGuiHandler.h" #include "CMT.h" #include "CServerHandler.h" +#include "../lib/ScriptHandler.h" +#include #ifdef VCMI_ANDROID #include "lib/CAndroidVMHelper.h" @@ -105,6 +106,39 @@ public: } }; +CPlayerEnvironment::CPlayerEnvironment(PlayerColor player_, CClient * cl_, std::shared_ptr mainCallback_) + : player(player_), + cl(cl_), + mainCallback(mainCallback_) +{ + +} + +const Services * CPlayerEnvironment::services() const +{ + return VLC; +} + +vstd::CLoggerBase * CPlayerEnvironment::logger() const +{ + return logGlobal; +} + +events::EventBus * CPlayerEnvironment::eventBus() const +{ + return cl->eventBus();//always get actual value +} + +const CPlayerEnvironment::BattleCb * CPlayerEnvironment::battle() const +{ + return mainCallback.get(); +} + +const CPlayerEnvironment::GameCb * CPlayerEnvironment::game() const +{ + return mainCallback.get(); +} + CClient::CClient() { @@ -114,7 +148,36 @@ CClient::CClient() registerTypesClientPacks2(*applier); IObjectInterface::cb = this; gs = nullptr; - erm = nullptr; +} + +CClient::~CClient() +{ + IObjectInterface::cb = nullptr; +} + +const Services * CClient::services() const +{ + return VLC; //todo: this should be CGI +} + +const CClient::BattleCb * CClient::battle() const +{ + return this; +} + +const CClient::GameCb * CClient::game() const +{ + return this; +} + +vstd::CLoggerBase * CClient::logger() const +{ + return logGlobal; +} + +events::EventBus * CClient::eventBus() const +{ + return clientEventBus.get(); } void CClient::newGame() @@ -122,11 +185,14 @@ void CClient::newGame() CSH->th->update(); CMapService mapService; gs = new CGameState(); + gs->preInit(VLC); logNetwork->trace("\tCreating gamestate: %i", CSH->th->getDiff()); gs->init(&mapService, CSH->si.get(), settings["general"]["saveRandomMaps"].Bool()); logNetwork->trace("Initializing GameState (together): %d ms", CSH->th->getDiff()); initMapHandler(); + reinitScripting(); + initPlayerEnvironments(); initPlayerInterfaces(); } @@ -168,10 +234,16 @@ void CClient::loadGame() throw; //obviously we cannot continue here } logNetwork->trace("Loaded common part of save %d ms", CSH->th->getDiff()); - + gs->preInit(VLC); gs->updateOnLoad(CSH->si.get()); initMapHandler(); + + reinitScripting(); + + initPlayerEnvironments(); + serialize(loader->serializer, loader->serializer.fileVersion); + initPlayerInterfaces(); } @@ -190,6 +262,13 @@ void CClient::serialize(BinarySerializer & h, const int version) h & i->second->human; i->second->saveGame(h, version); } + + if(version >= 800) + { + JsonNode scriptsState; + clientScripts->serializeState(h.saving, scriptsState); + h & scriptsState; + } } void CClient::serialize(BinaryDeserializer & h, const int version) @@ -253,6 +332,17 @@ void CClient::serialize(BinaryDeserializer & h, const int version) } nInt.reset(); } + + { + JsonNode scriptsState; + if(version >= 800) + { + h & scriptsState; + } + + clientScripts->serializeState(h.saving, scriptsState); + } + logNetwork->trace("Loaded client part of save %d ms", CSH->th->getDiff()); } @@ -270,6 +360,8 @@ void CClient::save(const std::string & fname) void CClient::endGame() { + clientScripts.reset(); + //suggest interfaces to finish their stuff (AI should interrupt any bg working threads) for(auto & i : playerint) i.second->finish(); @@ -296,8 +388,8 @@ void CClient::endGame() playerint.clear(); battleints.clear(); - callbacks.clear(); battleCallbacks.clear(); + playerEnvironments.clear(); logNetwork->info("Deleted playerInts."); logNetwork->info("Client stopped."); } @@ -319,6 +411,24 @@ void CClient::initMapHandler() pathCache.clear(); } +void CClient::initPlayerEnvironments() +{ + playerEnvironments.clear(); + + auto allPlayers = CSH->getAllClientPlayers(CSH->c->connectionID); + + for(auto & color : allPlayers) + { + logNetwork->info("Preparing environment for player %s", color.getStr()); + playerEnvironments[color] = std::make_shared(color, this, std::make_shared(gs, color, this)); + } + + if(settings["session"]["spectate"].Bool()) + { + playerEnvironments[PlayerColor::SPECTATOR] = std::make_shared(PlayerColor::SPECTATOR, this, std::make_shared(gs, boost::none, this)); + } +} + void CClient::initPlayerInterfaces() { for(auto & elem : gs->scenarioOps->playerInfos) @@ -327,19 +437,20 @@ void CClient::initPlayerInterfaces() if(!vstd::contains(CSH->getAllClientPlayers(CSH->c->connectionID), color)) continue; - if(vstd::contains(playerint, color)) - continue; - - logNetwork->trace("Preparing interface for player %s", color.getStr()); - if(elem.second.isControlledByAI()) + if(!vstd::contains(playerint, color)) { - auto AiToGive = aiNameForPlayer(elem.second, false); - logNetwork->info("Player %s will be lead by %s", color, AiToGive); - installNewPlayerInterface(CDynLibHandler::getNewAI(AiToGive), color); - } - else - { - installNewPlayerInterface(std::make_shared(color), color); + logNetwork->info("Preparing interface for player %s", color.getStr()); + if(elem.second.isControlledByAI()) + { + auto AiToGive = aiNameForPlayer(elem.second, false); + logNetwork->info("Player %s will be lead by %s", color.getStr(), AiToGive); + installNewPlayerInterface(CDynLibHandler::getNewAI(AiToGive), color); + } + else + { + logNetwork->info("Player %s will be lead by human", color.getStr()); + installNewPlayerInterface(std::make_shared(color), color); + } } } @@ -379,41 +490,32 @@ std::string CClient::aiNameForPlayer(bool battleAI) return goodAI; } -void CClient::installNewPlayerInterface(std::shared_ptr gameInterface, boost::optional color, bool battlecb) +void CClient::installNewPlayerInterface(std::shared_ptr gameInterface, PlayerColor color, bool battlecb) { boost::unique_lock un(*CPlayerInterface::pim); - PlayerColor colorUsed = color.get_value_or(PlayerColor::UNFLAGGABLE); - if(!color) - privilegedGameEventReceivers.push_back(gameInterface); + playerint[color] = gameInterface; - playerint[colorUsed] = gameInterface; - - logGlobal->trace("\tInitializing the interface for player %s", colorUsed); + logGlobal->trace("\tInitializing the interface for player %s", color.getStr()); auto cb = std::make_shared(gs, color, this); - callbacks[colorUsed] = cb; - battleCallbacks[colorUsed] = cb; - gameInterface->init(cb); + battleCallbacks[color] = cb; + gameInterface->init(playerEnvironments.at(color), cb); installNewBattleInterface(gameInterface, color, battlecb); } -void CClient::installNewBattleInterface(std::shared_ptr battleInterface, boost::optional color, bool needCallback) +void CClient::installNewBattleInterface(std::shared_ptr battleInterface, PlayerColor color, bool needCallback) { boost::unique_lock un(*CPlayerInterface::pim); - PlayerColor colorUsed = color.get_value_or(PlayerColor::UNFLAGGABLE); - if(!color) - privilegedBattleEventReceivers.push_back(battleInterface); - - battleints[colorUsed] = battleInterface; + battleints[color] = battleInterface; if(needCallback) { - logGlobal->trace("\tInitializing the battle interface for player %s", *color); + logGlobal->trace("\tInitializing the battle interface for player %s", color.getStr()); auto cbc = std::make_shared(color, this); - battleCallbacks[colorUsed] = cbc; - battleInterface->init(cbc); + battleCallbacks[color] = cbc; + battleInterface->init(playerEnvironments.at(color), cbc); } } @@ -437,14 +539,6 @@ void CClient::handlePack(CPack * pack) delete pack; } -void CClient::commitPackage(CPackForClient * pack) -{ - CommitPackage cp; - cp.freePack = false; - cp.packToCommit = pack; - sendRequest(&cp, PlayerColor::NEUTRAL); -} - int CClient::sendRequest(const CPackForServer * request, PlayerColor player) { static ui32 requestCounter = 0; @@ -464,6 +558,7 @@ int CClient::sendRequest(const CPackForServer * request, PlayerColor player) void CClient::battleStarted(const BattleInfo * info) { + setBattle(info); for(auto & battleCb : battleCallbacks) { if(vstd::contains_if(info->sides, [&](const SideInBattle& side) {return side.color == battleCb.first; }) @@ -472,9 +567,6 @@ void CClient::battleStarted(const BattleInfo * info) battleCb.second->setBattle(info); } } -// for(ui8 side : info->sides) -// if(battleCallbacks.count(side)) -// battleCallbacks[side]->setBattle(info); std::shared_ptr att, def; auto & leftSide = info->sides[0], & rightSide = info->sides[1]; @@ -552,6 +644,8 @@ void CClient::battleFinished() if(settings["session"]["spectate"].Bool() && !settings["session"]["spectate-skip-battle"].Bool()) battleCallbacks[PlayerColor::SPECTATOR]->setBattle(nullptr); + + setBattle(nullptr); } void CClient::startPlayerBattleAction(PlayerColor color) @@ -645,6 +739,23 @@ PlayerColor CClient::getLocalPlayer() const return getCurrentPlayer(); } +scripting::Pool * CClient::getGlobalContextPool() const +{ + return clientScripts.get(); +} + +scripting::Pool * CClient::getContextPool() const +{ + return clientScripts.get(); +} + +void CClient::reinitScripting() +{ + clientEventBus = make_unique(); + clientScripts.reset(new scripting::PoolImpl(this)); +} + + #ifdef VCMI_ANDROID extern "C" JNIEXPORT void JNICALL Java_eu_vcmi_vcmi_NativeMethods_notifyServerClosed(JNIEnv * env, jobject cls) { diff --git a/client/Client.h b/client/Client.h index 68e3843b7..e8c77d8fc 100644 --- a/client/Client.h +++ b/client/Client.h @@ -9,8 +9,11 @@ */ #pragma once +#include + #include "../lib/IGameCallback.h" #include "../lib/battle/BattleAction.h" +#include "../lib/battle/CBattleInfoCallback.h" #include "../lib/CStopWatch.h" #include "../lib/int3.h" #include "../lib/CondSh.h" @@ -28,7 +31,6 @@ class CGameInterface; class CCallback; class BattleAction; class CClient; -class CScriptingModule; struct CPathsInfo; class BinaryDeserializer; class BinarySerializer; @@ -37,6 +39,16 @@ namespace boost { class thread; } template class CApplier; class CBaseForCLApply; +namespace scripting +{ + class PoolImpl; +} + +namespace events +{ + class EventBus; +} + template class ThreadSafeVector { @@ -95,32 +107,40 @@ public: } }; -/// Class which handles client - server logic -class CClient : public IGameCallback +class CPlayerEnvironment : public Environment +{ +public: + PlayerColor player; + CClient * cl; + std::shared_ptr mainCallback; + + CPlayerEnvironment(PlayerColor player_, CClient * cl_, std::shared_ptr mainCallback_); + const Services * services() const override; + vstd::CLoggerBase * logger() const override; + events::EventBus * eventBus() const override; + const BattleCb * battle() const override; + const GameCb * game() const override; +}; + +/// Class which handles client - server logic +class CClient : public IGameCallback, public CBattleInfoCallback, public Environment { - std::shared_ptr> applier; - - mutable boost::mutex pathCacheMutex; - std::map> pathCache; - - std::map> playerActionThreads; - void waitForMoveAndSend(PlayerColor color); - public: - std::map> callbacks; //callbacks given to player interfaces - std::map> battleCallbacks; //callbacks given to player interfaces - std::vector> privilegedGameEventReceivers; //scripting modules, spectator interfaces - std::vector> privilegedBattleEventReceivers; //scripting modules, spectator interfaces std::map> playerint; std::map> battleints; - std::map>> additionalPlayerInts; std::map>> additionalBattleInts; boost::optional curbaction; - CScriptingModule * erm; CClient(); + ~CClient(); + + const Services * services() const override; + const BattleCb * battle() const override; + const GameCb * game() const override; + vstd::CLoggerBase * logger() const override; + events::EventBus * eventBus() const override; void newGame(); void loadGame(); @@ -131,17 +151,16 @@ public: void endGame(); void initMapHandler(); + void initPlayerEnvironments(); void initPlayerInterfaces(); std::string aiNameForPlayer(const PlayerSettings & ps, bool battleAI); //empty means no AI -> human std::string aiNameForPlayer(bool battleAI); - void installNewPlayerInterface(std::shared_ptr gameInterface, boost::optional color, bool battlecb = false); - void installNewBattleInterface(std::shared_ptr battleInterface, boost::optional color, bool needCallback = true); - + void installNewPlayerInterface(std::shared_ptr gameInterface, PlayerColor color, bool battlecb = false); + void installNewBattleInterface(std::shared_ptr battleInterface, PlayerColor color, bool needCallback = true); static ThreadSafeVector waitingRequest; //FIXME: make this normal field (need to join all threads before client destruction) void handlePack(CPack * pack); //applies the given pack and deletes it - void commitPackage(CPackForClient * pack) override; int sendRequest(const CPackForServer * request, PlayerColor player); //returns ID given to that request void battleStarted(const BattleInfo * info); @@ -160,7 +179,6 @@ public: void changeSpells(const CGHeroInstance * hero, bool give, const std::set & spells) override {}; bool removeObject(const CGObjectInstance * obj) override {return false;}; - void setBlockVis(ObjectInstanceID objid, bool bv) override {}; void setOwner(const CGObjectInstance * obj, PlayerColor owner) override {}; void changePrimSkill(const CGHeroInstance * hero, PrimarySkill::PrimarySkill which, si64 val, bool abs = false) override {}; void changeSecSkill(const CGHeroInstance * hero, SecondarySkill which, int val, bool abs = false) override {}; @@ -190,7 +208,6 @@ public: void putArtifact(const ArtifactLocation & al, const CArtifactInstance * a) override {}; void removeArtifact(const ArtifactLocation & al) override {}; bool moveArtifact(const ArtifactLocation & al1, const ArtifactLocation & al2) override {return false;}; - void synchronizeArtifactHandlerLists() override {}; void showCompInfo(ShowInInfobox * comp) override {}; void heroVisitCastle(const CGTownInstance * obj, const CGHeroInstance * hero) override {}; @@ -199,7 +216,6 @@ public: void startBattlePrimary(const CArmedInstance * army1, const CArmedInstance * army2, int3 tile, const CGHeroInstance * hero1, const CGHeroInstance * hero2, bool creatureBank = false, const CGTownInstance * town = nullptr) override {}; //use hero=nullptr for no hero void startBattleI(const CArmedInstance * army1, const CArmedInstance * army2, int3 tile, bool creatureBank = false) override {}; //if any of armies is hero, hero will be used void startBattleI(const CArmedInstance * army1, const CArmedInstance * army2, bool creatureBank = false) override {}; //if any of armies is hero, hero will be used, visitable tile of second obj is place of battle - void setAmount(ObjectInstanceID objid, ui32 val) override {}; bool moveHero(ObjectInstanceID hid, int3 dst, ui8 teleporting, bool transit = false, PlayerColor asker = PlayerColor::NEUTRAL) override {return false;}; void giveHeroBonus(GiveBonus * bonus) override {}; void setMovePoints(SetMovePoints * smp) override {}; @@ -211,4 +227,28 @@ public: void changeFogOfWar(int3 center, ui32 radius, PlayerColor player, bool hide) override {} void changeFogOfWar(std::unordered_set & tiles, PlayerColor player, bool hide) override {} + + void setObjProperty(ObjectInstanceID objid, int prop, si64 val) override {} + + void showInfoDialog(InfoWindow * iw) override {}; + void showInfoDialog(const std::string & msg, PlayerColor player) override {}; + + scripting::Pool * getGlobalContextPool() const override; + scripting::Pool * getContextPool() const override; +private: + std::map> battleCallbacks; //callbacks given to player interfaces + std::map> playerEnvironments; + + std::shared_ptr clientScripts; + std::unique_ptr clientEventBus; + + std::shared_ptr> applier; + + mutable boost::mutex pathCacheMutex; + std::map> pathCache; + + std::map> playerActionThreads; + + void waitForMoveAndSend(PlayerColor color); + void reinitScripting(); }; diff --git a/client/Graphics.cpp b/client/Graphics.cpp index 236e5c52c..93351c81e 100644 --- a/client/Graphics.cpp +++ b/client/Graphics.cpp @@ -10,6 +10,14 @@ #include "StdInc.h" #include "Graphics.h" +#include +#include +#include +#include +#include +#include +#include + #include "../lib/filesystem/Filesystem.h" #include "../lib/filesystem/CBinaryReader.h" #include "gui/SDL_Extensions.h" @@ -19,19 +27,15 @@ #include "CGameInfo.h" #include "../lib/VCMI_Lib.h" #include "../CCallback.h" -#include "../lib/CHeroHandler.h" -#include "../lib/CTownHandler.h" #include "../lib/CGeneralTextHandler.h" -#include "../lib/CCreatureHandler.h" #include "CBitmapHandler.h" -#include "../lib/CSkillHandler.h" -#include "../lib/spells/CSpellHandler.h" #include "../lib/CGameState.h" #include "../lib/JsonNode.h" #include "../lib/vcmi_endian.h" #include "../lib/CStopWatch.h" #include "../lib/mapObjects/CObjectClassesHandler.h" #include "../lib/mapObjects/CObjectHandler.h" +#include "../lib/CHeroHandler.h" using namespace CSDL_Ext; @@ -163,9 +167,9 @@ void Graphics::load() void Graphics::loadHeroAnimations() { - for(auto & elem : CGI->heroh->classes.heroClasses) + for(auto & elem : CGI->heroh->classes.objects) { - for (auto & templ : VLC->objtypeh->getHandlerFor(Obj::HERO, elem->id)->getTemplates()) + for (auto & templ : VLC->objtypeh->getHandlerFor(Obj::HERO, elem->getIndex())->getTemplates()) { if (!heroAnimations.count(templ.animationFile)) heroAnimations[templ.animationFile] = loadHeroAnimation(templ.animationFile); @@ -423,74 +427,36 @@ void Graphics::loadErmuToPicture() assert (etp_idx == 44); } -void Graphics::addImageListEntry(size_t index, std::string listName, std::string imageName) +void Graphics::addImageListEntry(size_t index, const std::string & listName, const std::string & imageName) { if (!imageName.empty()) { JsonNode entry; - entry["frame"].Float() = static_cast(index); + entry["frame"].Integer() = index; entry["file"].String() = imageName; imageLists["SPRITES/" + listName]["images"].Vector().push_back(entry); } } +void Graphics::addImageListEntries(const EntityService * service) +{ + auto cb = std::bind(&Graphics::addImageListEntry, this, _1, _2, _3); + + auto loopCb = [&](const Entity * entity, bool & stop) + { + entity->registerIcons(cb); + }; + + service->forEachBase(loopCb); +} + void Graphics::initializeImageLists() { - for(const CCreature * creature : CGI->creh->creatures) - { - addImageListEntry(creature->iconIndex, "CPRSMALL", creature->smallIconName); - addImageListEntry(creature->iconIndex, "TWCRPORT", creature->largeIconName); - } - - for(const CHero * hero : CGI->heroh->heroes) - { - addImageListEntry(hero->imageIndex, "UN32", hero->iconSpecSmall); - addImageListEntry(hero->imageIndex, "UN44", hero->iconSpecLarge); - addImageListEntry(hero->imageIndex, "PORTRAITSLARGE", hero->portraitLarge); - addImageListEntry(hero->imageIndex, "PORTRAITSSMALL", hero->portraitSmall); - } - - for(const CArtifact * art : CGI->arth->artifacts) - { - addImageListEntry(art->iconIndex, "ARTIFACT", art->image); - addImageListEntry(art->iconIndex, "ARTIFACTLARGE", art->large); - } - - for(const CFaction * faction : CGI->townh->factions) - { - if (faction->town) - { - auto & info = faction->town->clientInfo; - addImageListEntry(info.icons[0][0], "ITPT", info.iconLarge[0][0]); - addImageListEntry(info.icons[0][1], "ITPT", info.iconLarge[0][1]); - addImageListEntry(info.icons[1][0], "ITPT", info.iconLarge[1][0]); - addImageListEntry(info.icons[1][1], "ITPT", info.iconLarge[1][1]); - - addImageListEntry(info.icons[0][0] + 2, "ITPA", info.iconSmall[0][0]); - addImageListEntry(info.icons[0][1] + 2, "ITPA", info.iconSmall[0][1]); - addImageListEntry(info.icons[1][0] + 2, "ITPA", info.iconSmall[1][0]); - addImageListEntry(info.icons[1][1] + 2, "ITPA", info.iconSmall[1][1]); - } - } - - for(const CSpell * spell : CGI->spellh->objects) - { - addImageListEntry(spell->id, "SPELLS", spell->iconBook); - addImageListEntry(spell->id+1, "SPELLINT", spell->iconEffect); - addImageListEntry(spell->id, "SPELLBON", spell->iconScenarioBonus); - addImageListEntry(spell->id, "SPELLSCR", spell->iconScroll); - } - - for(const CSkill * skill : CGI->skillh->objects) - { - for(int level = 1; level <= 3; level++) - { - int frame = 2 + level + 3 * skill->id; - const CSkill::LevelInfo & skillAtLevel = skill->at(level); - addImageListEntry(frame, "SECSK32", skillAtLevel.iconSmall); - addImageListEntry(frame, "SECSKILL", skillAtLevel.iconMedium); - addImageListEntry(frame, "SECSK82", skillAtLevel.iconLarge); - } - } + addImageListEntries(CGI->creatures()); + addImageListEntries(CGI->heroTypes()); + addImageListEntries(CGI->artifacts()); + addImageListEntries(CGI->factions()); + addImageListEntries(CGI->spells()); + addImageListEntries(CGI->skills()); } diff --git a/client/Graphics.h b/client/Graphics.h index ba6fa7bf8..9cc4fd92a 100644 --- a/client/Graphics.h +++ b/client/Graphics.h @@ -23,6 +23,7 @@ struct InfoAboutTown; class CGObjectInstance; class ObjectTemplate; class CAnimation; +class EntityService; enum EFonts { @@ -32,7 +33,9 @@ enum EFonts /// Handles fonts, hero images, town images, various graphics class Graphics { - void addImageListEntry(size_t index, std::string listName, std::string imageName); + void addImageListEntry(size_t index, const std::string & listName, const std::string & imageName); + + void addImageListEntries(const EntityService * service); void initializeBattleGraphics(); void loadPaletteAndColors(); diff --git a/client/NetPacksClient.cpp b/client/NetPacksClient.cpp index 02b30eb84..d2c67c840 100644 --- a/client/NetPacksClient.cpp +++ b/client/NetPacksClient.cpp @@ -45,12 +45,6 @@ // TODO: as Tow suggested these template should all be part of CClient // This will require rework spectator interface properly though -template -void callPrivilegedInterfaces(CClient * cl, void (T::*ptr)(Args...), Args2 && ...args) -{ - for(auto &ger : cl->privilegedGameEventReceivers) - ((*ger).*ptr)(std::forward(args)...); -} template bool callOnlyThatInterface(CClient * cl, PlayerColor player, void (T::*ptr)(Args...), Args2 && ...args) @@ -67,7 +61,6 @@ template bool callInterfaceIfPresent(CClient * cl, PlayerColor player, void (T::*ptr)(Args...), Args2 && ...args) { bool called = callOnlyThatInterface(cl, player, ptr, std::forward(args)...); - callPrivilegedInterfaces(cl, ptr, std::forward(args)...); return called; } @@ -84,18 +77,10 @@ void callOnlyThatBattleInterface(CClient * cl, PlayerColor player, void (T::*ptr } } -template -void callPrivilegedBattleInterfaces(CClient * cl, void (T::*ptr)(Args...), Args2 && ...args) -{ - for(auto & ber : cl->privilegedBattleEventReceivers) - ((*ber).*ptr)(std::forward(args)...); -} - template void callBattleInterfaceIfPresent(CClient * cl, PlayerColor player, void (T::*ptr)(Args...), Args2 && ...args) { callOnlyThatInterface(cl, player, ptr, std::forward(args)...); - callPrivilegedBattleInterfaces(cl, ptr, std::forward(args)...); } //calls all normal interfaces and privileged ones, playerints may be updated when iterating over it, so we need a copy @@ -116,10 +101,8 @@ void callBattleInterfaceIfPresentForBothSides(CClient * cl, void (T::*ptr)(Args. { callOnlyThatBattleInterface(cl, PlayerColor::SPECTATOR, ptr, std::forward(args)...); } - callPrivilegedBattleInterfaces(cl, ptr, std::forward(args)...); } - void SetResources::applyCl(CClient *cl) { //todo: inform on actual resource set transfered @@ -307,7 +290,7 @@ void GiveBonus::applyCl(CClient *cl) break; case PLAYER: { - const PlayerState *p = GS(cl)->getPlayer(PlayerColor(id)); + const PlayerState *p = GS(cl)->getPlayerState(PlayerColor(id)); callInterfaceIfPresent(cl, PlayerColor(id), &IGameEventsReceiver::playerBonusChanged, *p->getBonusList().back(), true); } break; @@ -351,7 +334,7 @@ void RemoveBonus::applyCl(CClient *cl) break; case PLAYER: { - //const PlayerState *p = GS(cl)->getPlayer(id); + //const PlayerState *p = GS(cl)->getPlayerState(id); callInterfaceIfPresent(cl, PlayerColor(id), &IGameEventsReceiver::playerBonusChanged, bonus, false); } break; @@ -370,7 +353,7 @@ void RemoveObject::applyFirstCl(CClient *cl) { //below line contains little cheat for AI so it will be aware of deletion of enemy heroes that moved or got re-covered by FoW //TODO: loose requirements as next AI related crashes appear, for example another player collects object that got re-covered by FoW, unsure if AI code workarounds this - if(GS(cl)->isVisible(o, i->first) || (!cl->getPlayer(i->first)->human && o->ID == Obj::HERO && o->tempOwner != i->first)) + if(GS(cl)->isVisible(o, i->first) || (!cl->getPlayerState(i->first)->human && o->ID == Obj::HERO && o->tempOwner != i->first)) i->second->objectRemoved(o); } } @@ -387,7 +370,7 @@ void TryMoveHero::applyFirstCl(CClient *cl) //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++) { - auto ps = GS(cl)->getPlayer(i->first); + auto ps = GS(cl)->getPlayerState(i->first); if(ps && (GS(cl)->isVisible(start - int3(1, 0, 0), i->first) || GS(cl)->isVisible(end - int3(1, 0, 0), i->first))) { if(ps->human) @@ -611,8 +594,6 @@ void BattleStart::applyFirstCl(CClient *cl) info->tile, info->sides[0].hero, info->sides[1].hero); callOnlyThatBattleInterface(cl, PlayerColor::SPECTATOR, &IBattleEventsReceiver::battleStartBefore, info->sides[0].armyObject, info->sides[1].armyObject, info->tile, info->sides[0].hero, info->sides[1].hero); - callPrivilegedBattleInterfaces(cl, &IBattleEventsReceiver::battleStartBefore, info->sides[0].armyObject, info->sides[1].armyObject, - info->tile, info->sides[0].hero, info->sides[1].hero); } void BattleStart::applyCl(CClient *cl) @@ -651,6 +632,11 @@ void BattleSetActiveStack::applyCl(CClient *cl) cl->startPlayerBattleAction(playerToCall); } +void BattleLogMessage::applyCl(CClient * cl) +{ + callBattleInterfaceIfPresentForBothSides(cl, &IBattleEventsReceiver::battleLogMessage, lines); +} + void BattleTriggerEffect::applyCl(CClient * cl) { callBattleInterfaceIfPresentForBothSides(cl, &IBattleEventsReceiver::battleTriggerEffect, *this); @@ -680,7 +666,7 @@ void BattleAttack::applyFirstCl(CClient *cl) void BattleAttack::applyCl(CClient *cl) { - callBattleInterfaceIfPresentForBothSides(cl, &IBattleEventsReceiver::battleStacksAttacked, bsa, battleLog); + callBattleInterfaceIfPresentForBothSides(cl, &IBattleEventsReceiver::battleStacksAttacked, bsa); } void StartAction::applyFirstCl(CClient *cl) @@ -702,7 +688,7 @@ void SetStackEffect::applyCl(CClient *cl) void StacksInjured::applyCl(CClient *cl) { - callBattleInterfaceIfPresentForBothSides(cl, &IBattleEventsReceiver::battleStacksAttacked, stacks, battleLog); + callBattleInterfaceIfPresentForBothSides(cl, &IBattleEventsReceiver::battleStacksAttacked, stacks); } void BattleResultsApplied::applyCl(CClient *cl) @@ -714,7 +700,7 @@ void BattleResultsApplied::applyCl(CClient *cl) void BattleUnitsChanged::applyCl(CClient * cl) { - callBattleInterfaceIfPresentForBothSides(cl, &IBattleEventsReceiver::battleUnitsChanged, changedStacks, customEffects, battleLog); + callBattleInterfaceIfPresentForBothSides(cl, &IBattleEventsReceiver::battleUnitsChanged, changedStacks, customEffects); } void BattleObstaclesChanged::applyCl(CClient *cl) @@ -795,7 +781,7 @@ void PlayerMessageClient::applyCl(CClient *cl) if(player.isSpectator()) str << "Spectator: " << text; else - str << cl->getPlayer(player)->nodeName() <<": " << text; + str << cl->getPlayerState(player)->nodeName() <<": " << text; if(LOCPLINT) LOCPLINT->cingconsole->print(str.str()); } @@ -918,3 +904,9 @@ void SetAvailableArtifacts::applyCl(CClient *cl) callInterfaceIfPresent(cl, cl->getTile(bm->visitablePos())->visitableObjects.back()->tempOwner, &IGameEventsReceiver::availableArtifactsChanged, bm); } } + + +void EntitiesChanged::applyCl(CClient *cl) +{ + cl->invalidatePaths(); +} diff --git a/client/VCMI_client.cbp b/client/VCMI_client.cbp index d488c8b57..845082b67 100644 --- a/client/VCMI_client.cbp +++ b/client/VCMI_client.cbp @@ -4,6 +4,7 @@ diff --git a/client/battle/CBattleAnimations.cpp b/client/battle/CBattleAnimations.cpp index b39932a78..3ca1b7935 100644 --- a/client/battle/CBattleAnimations.cpp +++ b/client/battle/CBattleAnimations.cpp @@ -29,7 +29,6 @@ #include "../../lib/CStack.h" #include "../../lib/CTownHandler.h" #include "../../lib/mapObjects/CGTownInstance.h" -#include "../../lib/spells/CSpellHandler.h" CBattleAnimation::CBattleAnimation(CBattleInterface * _owner) : owner(_owner), ID(_owner->animIDhelper++) @@ -762,15 +761,15 @@ bool CShootingAnimation::init() if(shooterInfo->idNumber == CreatureID::ARROW_TOWERS) { int creID = owner->siegeH->town->town->clientInfo.siegeShooter; - shooterInfo = CGI->creh->creatures[creID]; + shooterInfo = CGI->creh->operator[](creID); } if(!shooterInfo->animation.missleFrameAngles.size()) logAnim->error("Mod error: Creature '%s' on the Archer's tower is not a shooter. Mod should be fixed. Trying to use archer's data instead..." , shooterInfo->nameSing); - - auto & angles = shooterInfo->animation.missleFrameAngles.size() + + auto & angles = shooterInfo->animation.missleFrameAngles.size() ? shooterInfo->animation.missleFrameAngles - : CGI->creh->creatures[CreatureID::ARCHER]->animation.missleFrameAngles; + : CGI->creh->operator[](CreatureID::ARCHER)->animation.missleFrameAngles; ProjectileInfo spi; spi.shotDone = false; diff --git a/client/battle/CBattleInterface.cpp b/client/battle/CBattleInterface.cpp index 648d35ab0..ab6d49219 100644 --- a/client/battle/CBattleInterface.cpp +++ b/client/battle/CBattleInterface.cpp @@ -38,6 +38,8 @@ #include "../../lib/CondSh.h" #include "../../lib/CRandomGenerator.h" #include "../../lib/spells/CSpellHandler.h" +#include "../../lib/spells/ISpellMechanics.h" +#include "../../lib/spells/Problem.h" #include "../../lib/CTownHandler.h" #include "../../lib/CGameState.h" #include "../../lib/mapping/CMap.h" @@ -398,7 +400,7 @@ CBattleInterface::CBattleInterface(const CCreatureSet *army1, const CCreatureSet for(auto hex : bfield) addChild(hex.get()); - if (tacticsMode) + if(tacticsMode) bTacticNextStack(); CCS->musich->stopMusic(); @@ -415,7 +417,6 @@ CBattleInterface::CBattleInterface(const CCreatureSet *army1, const CCreatureSet }; CCS->soundh->setCallback(battleIntroSoundChannel, onIntroPlayed); - memset(stackCountOutsideHexes, 1, GameConstants::BFIELD_SIZE *sizeof(bool)); //initialize array with trues currentAction = PossiblePlayerBattleAction::INVALID; selectedAction = PossiblePlayerBattleAction::INVALID; @@ -579,7 +580,7 @@ void CBattleInterface::keyPressed(const SDL_KeyboardEvent & key) if(!battleActionsStarted) CCS->soundh->stopSound(battleIntroSoundChannel); else - endCastingSpell(); + endCastingSpell(); } } void CBattleInterface::mouseMoved(const SDL_MouseMotionEvent &sEvent) @@ -794,7 +795,7 @@ void CBattleInterface::bSurrenderf() if (cost >= 0) { std::string enemyHeroName = curInt->cb->battleGetEnemyHero().name; - if(enemyHeroName.empty()) + if (enemyHeroName.empty()) { logGlobal->warn("Surrender performed without enemy hero, should not happen!"); enemyHeroName = "#ENEMY#"; @@ -871,7 +872,7 @@ void CBattleInterface::bAutofightf() blockUI(true); auto ai = CDynLibHandler::getNewBattleAI(settings["server"]["friendlyAI"].String()); - ai->init(curInt->cb); + ai->init(curInt->env, curInt->cb); ai->battleStart(army1, army2, int3(0,0,0), attackingHeroInstance, defendingHeroInstance, curInt->cb->battleGetMySide()); curInt->autofightingAI = ai; curInt->cb->registerBattleInterface(ai); @@ -910,14 +911,14 @@ void CBattleInterface::bSpellf() if (blockingBonus->source == Bonus::ARTIFACT) { - const int artID = blockingBonus->sid; + const int32_t artID = blockingBonus->sid; //If we have artifact, put name of our hero. Otherwise assume it's the enemy. //TODO check who *really* is source of bonus std::string heroName = myHero->hasArt(artID) ? myHero->name : enemyHero().name; //%s wields the %s, an ancient artifact which creates a p dead to all magic. LOCPLINT->showInfoDialog(boost::str(boost::format(CGI->generaltexth->allTexts[683]) - % heroName % CGI->arth->artifacts[artID]->Name())); + % heroName % CGI->artifacts()->getByIndex(artID)->getName())); } } } @@ -964,7 +965,7 @@ void CBattleInterface::unitAdded(const CStack * stack) if(stack->initialPosition < 0) //turret { - const CCreature *turretCreature = CGI->creh->creatures[siegeH->town->town->clientInfo.siegeShooter]; + const CCreature *turretCreature = CGI->creh->objects[siegeH->town->town->clientInfo.siegeShooter]; creAnims[stack->ID] = AnimationControls::getAnimation(turretCreature); @@ -1012,7 +1013,7 @@ void CBattleInterface::initStackProjectile(const CStack * stack) { const CCreature * creature;//creature whose shots should be loaded if(stack->getCreature()->idNumber == CreatureID::ARROW_TOWERS) - creature = CGI->creh->creatures[siegeH->town->town->clientInfo.siegeShooter]; + creature = CGI->creh->objects[siegeH->town->town->clientInfo.siegeShooter]; else creature = stack->getCreature(); @@ -1062,7 +1063,7 @@ void CBattleInterface::stackMoved(const CStack *stack, std::vector de waitForAnims(); } -void CBattleInterface::stacksAreAttacked(std::vector attackedInfos, const std::vector & battleLog) +void CBattleInterface::stacksAreAttacked(std::vector attackedInfos) { for(auto & attackedInfo : attackedInfos) { @@ -1089,8 +1090,6 @@ void CBattleInterface::stacksAreAttacked(std::vector attacked killedBySide.at(side) += attackedInfo.amountKilled; } - int killed = killedBySide[0] + killedBySide[1]; - for(ui8 side = 0; side < 2; side++) { if(killedBySide.at(side) > killedBySide.at(1-side)) @@ -1106,11 +1105,6 @@ void CBattleInterface::stacksAreAttacked(std::vector attacked if (attackedInfo.cloneKilled) stackRemoved(attackedInfo.defender->ID); } - - if(!battleLog.empty()) - displayBattleLog(battleLog); - else - printConsoleAttacked(attackedInfos.front().defender, damage, killed, attackedInfos.front().attacker, (targets > 1)); //creatures perish } void CBattleInterface::stackAttacking( const CStack *attacker, BattleHex dest, const CStack *attacked, bool shooting ) @@ -1377,9 +1371,6 @@ void CBattleInterface::spellCast(const BattleSpellCast * sc) displayEffect(elem.effect, stack->getPosition()); } - //displaying message in console - displayBattleLog(sc->battleLog); - waitForAnims(); //mana absorption if (sc->manaGained > 0) @@ -1393,9 +1384,6 @@ void CBattleInterface::spellCast(const BattleSpellCast * sc) void CBattleInterface::battleStacksEffectsSet(const SetStackEffect & sse) { - for(const MetaString & line : sse.battleLog) - console->addText(line.toString()); - if(activeStack != nullptr) redrawBackgroundWithHexes(activeStack); } @@ -1474,28 +1462,19 @@ void CBattleInterface::displayEffect(ui32 effect, BattleHex destTile) addNewAnim(new CEffectAnimation(this, customAnim, destTile)); } -void CBattleInterface::displaySpellAnimation(const CSpell::TAnimation & animation, BattleHex destinationTile) -{ - if (animation.pause > 0) - { - addNewAnim(new CDummyAnimation(this, animation.pause)); - } - else - { - addNewAnim(new CEffectAnimation(this, animation.resourceName, destinationTile, false, animation.verticalPosition == VerticalPosition::BOTTOM)); - } -} - void CBattleInterface::displaySpellCast(SpellID spellID, BattleHex destinationTile) { - const CSpell *spell = spellID.toSpell(); + const CSpell * spell = spellID.toSpell(); - if (spell == nullptr) + if(spell == nullptr) return; - for (const CSpell::TAnimation & animation : spell->animationInfo.cast) + for(const CSpell::TAnimation & animation : spell->animationInfo.cast) { - displaySpellAnimation(animation, destinationTile); + if(animation.pause > 0) + addNewAnim(new CDummyAnimation(this, animation.pause)); + else + addNewAnim(new CEffectAnimation(this, animation.resourceName, destinationTile, false, animation.verticalPosition == VerticalPosition::BOTTOM)); } } @@ -1503,25 +1482,32 @@ void CBattleInterface::displaySpellEffect(SpellID spellID, BattleHex destination { const CSpell *spell = spellID.toSpell(); - if (spell == nullptr) + if(spell == nullptr) return; - for (const CSpell::TAnimation & animation : spell->animationInfo.affect) + for(const CSpell::TAnimation & animation : spell->animationInfo.affect) { - displaySpellAnimation(animation, destinationTile); + if(animation.pause > 0) + addNewAnim(new CDummyAnimation(this, animation.pause)); + else + addNewAnim(new CEffectAnimation(this, animation.resourceName, destinationTile, false, animation.verticalPosition == VerticalPosition::BOTTOM)); + } } void CBattleInterface::displaySpellHit(SpellID spellID, BattleHex destinationTile) { - const CSpell *spell = spellID.toSpell(); + const CSpell * spell = spellID.toSpell(); - if (spell == nullptr) + if(spell == nullptr) return; - for (const CSpell::TAnimation & animation : spell->animationInfo.hit) + for(const CSpell::TAnimation & animation : spell->animationInfo.hit) { - displaySpellAnimation(animation, destinationTile); + if(animation.pause > 0) + addNewAnim(new CDummyAnimation(this, animation.pause)); + else + addNewAnim(new CEffectAnimation(this, animation.resourceName, destinationTile, false, animation.verticalPosition == VerticalPosition::BOTTOM)); } } @@ -1712,9 +1698,19 @@ void CBattleInterface::enterCreatureCastingMode() if (vstd::contains(possibleActions, PossiblePlayerBattleAction::NO_LOCATION)) { - const spells::Caster *caster = activeStack; - const CSpell *spell = SpellID(creatureSpellToCast).toSpell(); - const bool isCastingPossible = spell->canBeCastAt(curInt->cb.get(), spells::Mode::CREATURE_ACTIVE, caster, BattleHex::INVALID); + const spells::Caster * caster = activeStack; + const CSpell * spell = SpellID(creatureSpellToCast).toSpell(); + + spells::Target target; + target.emplace_back(); + + + spells::BattleCast cast(curInt->cb.get(), caster, spells::Mode::CREATURE_ACTIVE, spell); + + auto m = spell->battleMechanics(&cast); + spells::detail::ProblemImpl ignored; + + const bool isCastingPossible = m->canBeCastAt(ignored, target); if (isCastingPossible) { @@ -1732,7 +1728,7 @@ void CBattleInterface::enterCreatureCastingMode() auto actionFilterPredicate = [](const PossiblePlayerBattleAction x) { return (x != PossiblePlayerBattleAction::ANY_LOCATION) && (x != PossiblePlayerBattleAction::NO_LOCATION) && - (x != PossiblePlayerBattleAction::FREE_LOCATION) && (x != PossiblePlayerBattleAction::AIMED_SPELL_CREATURE) && + (x != PossiblePlayerBattleAction::FREE_LOCATION) && (x != PossiblePlayerBattleAction::AIMED_SPELL_CREATURE) && (x != PossiblePlayerBattleAction::OBSTACLE); }; @@ -1766,7 +1762,7 @@ void CBattleInterface::reorderPossibleActionsPriority(const CStack * stack, Mous case PossiblePlayerBattleAction::OBSTACLE: if(!stack->hasBonusOfType(Bonus::NO_SPELLCAST_BY_DEFAULT) && context == MouseHoveredHexContext::OCCUPIED_HEX) return 1; - else + else return 100;//bottom priority break; case PossiblePlayerBattleAction::RANDOM_GENIE_SPELL: @@ -1784,9 +1780,9 @@ void CBattleInterface::reorderPossibleActionsPriority(const CStack * stack, Mous case PossiblePlayerBattleAction::MOVE_STACK: return 8; break; case PossiblePlayerBattleAction::CATAPULT: - return 9; break; + return 9; break; case PossiblePlayerBattleAction::HEAL: - return 10; break; + return 10; break; default: return 200; break; } @@ -1800,39 +1796,6 @@ void CBattleInterface::reorderPossibleActionsPriority(const CStack * stack, Mous std::make_heap(possibleActions.begin(), possibleActions.end(), comparer); } -void CBattleInterface::printConsoleAttacked(const CStack * defender, int dmg, int killed, const CStack * attacker, bool multiple) -{ - std::string formattedText; - if(attacker) //ignore if stacks were killed by spell - { - MetaString text; - attacker->addText(text, MetaString::GENERAL_TXT, 376); - attacker->addNameReplacement(text); - text.addReplacement(dmg); - formattedText = text.toString(); - } - - if(killed > 0) - { - if(attacker) - formattedText.append(" "); - - boost::format txt; - if(killed > 1) - { - txt = boost::format(CGI->generaltexth->allTexts[379]) % killed % (multiple ? CGI->generaltexth->allTexts[43] : defender->getCreature()->namePl); // creatures perish - } - else //killed == 1 - { - txt = boost::format(CGI->generaltexth->allTexts[378]) % (multiple ? CGI->generaltexth->allTexts[42] : defender->getCreature()->nameSing); // creature perishes - } - std::string trimmed = boost::to_string(txt); - boost::algorithm::trim(trimmed); // these default h3 texts have unnecessary new lines, so get rid of them before displaying - formattedText.append(trimmed); - } - console->addText(formattedText); -} - void CBattleInterface::endAction(const BattleAction* action) { const CStack *stack = curInt->cb->battleGetStackByID(action->stackNumber); @@ -2571,7 +2534,16 @@ bool CBattleInterface::isCastingPossibleHere(const CStack *sactive, const CStack else { const spells::Mode mode = creatureCasting ? spells::Mode::CREATURE_ACTIVE : spells::Mode::HERO; - isCastingPossible = sp->canBeCastAt(curInt->cb.get(), mode, caster, myNumber); + + spells::Target target; + target.emplace_back(myNumber); + + spells::BattleCast cast(curInt->cb.get(), caster, mode, sp); + + auto m = sp->battleMechanics(&cast); + spells::detail::ProblemImpl problem; //todo: display problem in status bar + + isCastingPossible = m->canBeCastAt(problem, target); } } else @@ -3050,7 +3022,7 @@ void CBattleInterface::show(SDL_Surface *to) showProjectiles(to); if(battleActionsStarted) - updateBattleAnimations(); + updateBattleAnimations(); SDL_SetClipRect(to, &buf); //restoring previous clip_rect @@ -3064,7 +3036,7 @@ void CBattleInterface::show(SDL_Surface *to) //we may have changed active interface (another side in hot-seat), // so we can't continue drawing with old setting. show(to); - } + } } void CBattleInterface::showBackground(SDL_Surface *to) @@ -3172,7 +3144,9 @@ void CBattleInterface::showHighlightedHexes(SDL_Surface *to) if(caster && spell) //when casting spell { // printing shaded hex(es) - auto shaded = spell->rangeInHexes(curInt->cb.get(), mode, caster, currentlyHoveredHex); + spells::BattleCast event(curInt->cb.get(), caster, mode, spell); + auto shaded = spell->battleMechanics(&event)->rangeInHexes(currentlyHoveredHex); + for(BattleHex shadedHex : shaded) { if((shadedHex.getX() != 0) && (shadedHex.getX() != GameConstants::BFIELD_WIDTH - 1)) @@ -3691,7 +3665,17 @@ void CBattleInterface::redrawBackgroundWithHexes(const CStack *activeStack) attackableHexes.clear(); if (activeStack) occupyableHexes = curInt->cb->battleGetAvailableHexes(activeStack, true, &attackableHexes); - curInt->cb->battleGetStackCountOutsideHexes(stackCountOutsideHexes); + + auto fillStackCountOutsideHexes = [&]() + { + auto accessibility = curInt->cb->getAccesibility(); + + for(int i = 0; i < accessibility.size(); i++) + stackCountOutsideHexes.at(i) = (accessibility[i] == EAccessibility::ACCESSIBLE); + }; + + fillStackCountOutsideHexes(); + //prepare background graphic with hexes and shaded hexes blitAt(background, 0, 0, backgroundWithHexes); diff --git a/client/battle/CBattleInterface.h b/client/battle/CBattleInterface.h index ad8151f90..79ace75df 100644 --- a/client/battle/CBattleInterface.h +++ b/client/battle/CBattleInterface.h @@ -9,7 +9,9 @@ */ #pragma once -#include "../../lib/ConstTransitivePtr.h" //may be reundant +#include + +#include "../../lib/ConstTransitivePtr.h" //may be redundant #include "../../lib/GameConstants.h" #include "CBattleAnimations.h" @@ -50,6 +52,7 @@ class CBattleGameInterface; struct CustomEffectInfo; class CAnimation; class IImage; +class CSpell; /// Small struct which contains information about the id of the attacked stack, the damage dealt,... struct StackAttackedInfo @@ -153,7 +156,7 @@ private: void activateStack(); //sets activeStack to stackToActivate etc. //FIXME: No, it's not clear at all std::vector occupyableHexes, //hexes available for active stack attackableHexes; //hexes attackable by active stack - bool stackCountOutsideHexes[GameConstants::BFIELD_SIZE]; // hexes that when in front of a unit cause it's amount box to move back + std::array stackCountOutsideHexes; // hexes that when in front of a unit cause it's amount box to move back BattleHex previouslyHoveredHex; //number of hex that was hovered by the cursor a while ago BattleHex currentlyHoveredHex; //number of hex that is supposed to be hovered (for a while it may be inappropriately set, but will be renewed soon) int attackingHex; //hex from which the stack would perform attack with current cursor @@ -187,8 +190,6 @@ private: //force active stack to cast a spell if possible void enterCreatureCastingMode(); - void printConsoleAttacked(const CStack *defender, int dmg, int killed, const CStack *attacker, bool Multiple); - std::list projectiles; //projectiles flying on battlefield void giveCommand(EActionType action, BattleHex tile = BattleHex(), si32 additional = -1); void sendCommand(BattleAction *& command, const CStack * actor = nullptr); @@ -340,7 +341,7 @@ public: void stackActivated(const CStack *stack); //active stack has been changed void stackMoved(const CStack *stack, std::vector destHex, int distance); //stack with id number moved to destHex void waitForAnims(); - void stacksAreAttacked(std::vector attackedInfos, const std::vector & battleLog); //called when a certain amount of stacks has been attacked + void stacksAreAttacked(std::vector attackedInfos); //called when a certain amount of stacks has been attacked void stackAttacking(const CStack *attacker, BattleHex dest, const CStack *attacked, bool shooting); //called when stack with id ID is attacking something on hex dest void newRoundFirst( int round ); void newRound(int number); //caled when round is ended; number is the number of round @@ -361,8 +362,6 @@ public: void displaySpellEffect(SpellID spellID, BattleHex destinationTile); //displays spell`s affected animation void displaySpellHit(SpellID spellID, BattleHex destinationTile); //displays spell`s affected animation - void displaySpellAnimation(const CSpell::TAnimation & animation, BattleHex destinationTile); - void battleTriggerEffect(const BattleTriggerEffect & bte); void setBattleCursor(const int myNumber); //really complex and messy, sets attackingHex void endAction(const BattleAction* action); diff --git a/client/battle/CBattleInterfaceClasses.cpp b/client/battle/CBattleInterfaceClasses.cpp index 02689ecdb..fb32093fd 100644 --- a/client/battle/CBattleInterfaceClasses.cpp +++ b/client/battle/CBattleInterfaceClasses.cpp @@ -448,8 +448,8 @@ CBattleResultWindow::CBattleResultWindow(const BattleResult & br, CPlayerInterfa if(best != stacks.end()) //should be always but to be safe... { - icons.push_back(std::make_shared("TWCRPORT", (*best)->type->idNumber+2, 0, xs[i], 38)); - sideNames[i] = CGI->creh->creatures[(*best)->type->idNumber]->namePl; + icons.push_back(std::make_shared("TWCRPORT", (*best)->type->getIconIndex(), 0, xs[i], 38)); + sideNames[i] = (*best)->type->getPluralName(); } } } @@ -471,7 +471,7 @@ CBattleResultWindow::CBattleResultWindow(const BattleResult & br, CPlayerInterfa int yPos = 344 + step * 97; for(auto & elem : br.casualties[step]) { - icons.push_back(std::make_shared("CPRSMALL", CGI->creh->creatures[elem.first]->iconIndex, 0, xPos, yPos)); + icons.push_back(std::make_shared("CPRSMALL", CGI->creatures()->getByIndex(elem.first)->getIconIndex(), 0, xPos, yPos)); std::ostringstream amount; amount<(xPos + 16, yPos + 42, FONT_SMALL, CENTER, Colors::WHITE, amount.str())); diff --git a/client/gui/CGuiHandler.cpp b/client/gui/CGuiHandler.cpp index 64f65db1c..bf386b492 100644 --- a/client/gui/CGuiHandler.cpp +++ b/client/gui/CGuiHandler.cpp @@ -23,7 +23,7 @@ #include "../CPlayerInterface.h" #include "../battle/CBattleInterface.h" -extern std::queue events; +extern std::queue SDLEventsQueue; extern boost::mutex eventsM; boost::thread_specific_ptr inGuiThread; @@ -188,18 +188,18 @@ void CGuiHandler::handleEvents() return; boost::unique_lock lock(eventsM); - while(!events.empty()) + while(!SDLEventsQueue.empty()) { continueEventHandling = true; - SDL_Event ev = events.front(); + SDL_Event ev = SDLEventsQueue.front(); current = &ev; - events.pop(); + SDLEventsQueue.pop(); // In a sequence of mouse motion events, skip all but the last one. // This prevents freezes when every motion event takes longer to handle than interval at which // the events arrive (like dragging on the minimap in world view, with redraw at every event) // so that the events would start piling up faster than they can be processed. - if ((ev.type == SDL_MOUSEMOTION) && !events.empty() && (events.front().type == SDL_MOUSEMOTION)) + if ((ev.type == SDL_MOUSEMOTION) && !SDLEventsQueue.empty() && (SDLEventsQueue.front().type == SDL_MOUSEMOTION)) continue; handleCurrentEvent(); diff --git a/client/lobby/CBonusSelection.cpp b/client/lobby/CBonusSelection.cpp index 02d712521..64f82a6de 100644 --- a/client/lobby/CBonusSelection.cpp +++ b/client/lobby/CBonusSelection.cpp @@ -10,6 +10,10 @@ #include "StdInc.h" #include "CBonusSelection.h" + +#include +#include + #include "CSelectionBase.h" #include "../CGameInfo.h" @@ -34,9 +38,7 @@ #include "../../lib/filesystem/Filesystem.h" #include "../../lib/CGeneralTextHandler.h" -#include "../../lib/CArtHandler.h" #include "../../lib/CBuildingHandler.h" -#include "../../lib/spells/CSpellHandler.h" #include "../../lib/CSkillHandler.h" #include "../../lib/CTownHandler.h" @@ -176,13 +178,13 @@ void CBonusSelection::createBonusesIcons() { case CScenarioTravel::STravelBonus::SPELL: desc = CGI->generaltexth->allTexts[715]; - boost::algorithm::replace_first(desc, "%s", CGI->spellh->objects[bonDescs[i].info2]->name); + boost::algorithm::replace_first(desc, "%s", CGI->spells()->getByIndex(bonDescs[i].info2)->getName()); break; case CScenarioTravel::STravelBonus::MONSTER: picNumber = bonDescs[i].info2 + 2; desc = CGI->generaltexth->allTexts[717]; boost::algorithm::replace_first(desc, "%d", boost::lexical_cast(bonDescs[i].info3)); - boost::algorithm::replace_first(desc, "%s", CGI->creh->creatures[bonDescs[i].info2]->namePl); + boost::algorithm::replace_first(desc, "%s", CGI->creatures()->getByIndex(bonDescs[i].info2)->getPluralName()); break; case CScenarioTravel::STravelBonus::BUILDING: { @@ -202,18 +204,18 @@ void CBonusSelection::createBonusesIcons() picName = graphics->ERMUtoPicture[faction][buildID]; picNumber = -1; - if(vstd::contains(CGI->townh->factions[faction]->town->buildings, buildID)) - desc = CGI->townh->factions[faction]->town->buildings.find(buildID)->second->Name(); + if(vstd::contains((*CGI->townh)[faction]->town->buildings, buildID)) + desc = (*CGI->townh)[faction]->town->buildings.find(buildID)->second->Name(); break; } case CScenarioTravel::STravelBonus::ARTIFACT: desc = CGI->generaltexth->allTexts[715]; - boost::algorithm::replace_first(desc, "%s", CGI->arth->artifacts[bonDescs[i].info2]->Name()); + boost::algorithm::replace_first(desc, "%s", CGI->artifacts()->getByIndex(bonDescs[i].info2)->getName()); break; case CScenarioTravel::STravelBonus::SPELL_SCROLL: desc = CGI->generaltexth->allTexts[716]; - boost::algorithm::replace_first(desc, "%s", CGI->spellh->objects[bonDescs[i].info2]->name); + boost::algorithm::replace_first(desc, "%s", CGI->spells()->getByIndex(bonDescs[i].info2)->getName()); break; case CScenarioTravel::STravelBonus::PRIMARY_SKILL: { @@ -318,7 +320,7 @@ void CBonusSelection::createBonusesIcons() } else { - boost::algorithm::replace_first(desc, "%s", CGI->heroh->heroes[bonDescs[i].info2]->name); + boost::algorithm::replace_first(desc, "%s", CGI->heroh->objects[bonDescs[i].info2]->name); } break; } diff --git a/client/lobby/OptionsTab.cpp b/client/lobby/OptionsTab.cpp index 1c0de4ce6..2cbbde96e 100644 --- a/client/lobby/OptionsTab.cpp +++ b/client/lobby/OptionsTab.cpp @@ -83,7 +83,7 @@ size_t OptionsTab::CPlayerSettingsHelper::getImageIndex() TOWN_RANDOM = 38, TOWN_NONE = 39, // Special frames in ITPA HERO_RANDOM = 163, HERO_NONE = 164 // Special frames in PortraitsSmall }; - auto factionIndex = settings.castle >= CGI->townh->factions.size() ? 0 : settings.castle; + auto factionIndex = settings.castle >= CGI->townh->size() ? 0 : settings.castle; switch(type) { @@ -95,7 +95,7 @@ size_t OptionsTab::CPlayerSettingsHelper::getImageIndex() case PlayerSettings::RANDOM: return TOWN_RANDOM; default: - return CGI->townh->factions[factionIndex]->town->clientInfo.icons[true][false] + 2; + return (*CGI->townh)[factionIndex]->town->clientInfo.icons[true][false] + 2; } case HERO: switch(settings.hero) @@ -108,11 +108,10 @@ size_t OptionsTab::CPlayerSettingsHelper::getImageIndex() { if(settings.heroPortrait >= 0) return settings.heroPortrait; - auto index = settings.hero >= CGI->heroh->heroes.size() ? 0 : settings.hero; - return CGI->heroh->heroes[index]->imageIndex; + auto index = settings.hero >= CGI->heroh->size() ? 0 : settings.hero; + return (*CGI->heroh)[index]->imageIndex; } } - case BONUS: { switch(settings.bonus) @@ -125,7 +124,7 @@ size_t OptionsTab::CPlayerSettingsHelper::getImageIndex() return GOLD; case PlayerSettings::RESOURCE: { - switch(CGI->townh->factions[factionIndex]->town->primaryRes) + switch((*CGI->townh)[factionIndex]->town->primaryRes) { case Res::WOOD_AND_ORE: return WOOD_ORE; @@ -181,11 +180,11 @@ std::string OptionsTab::CPlayerSettingsHelper::getName() return CGI->generaltexth->allTexts[522]; default: { - auto factionIndex = settings.castle >= CGI->townh->factions.size() ? 0 : settings.castle; - return CGI->townh->factions[factionIndex]->name; - } + auto factionIndex = settings.castle >= CGI->townh->size() ? 0 : settings.castle; + return (*CGI->townh)[factionIndex]->name; } } + } case HERO: { switch(settings.hero) @@ -198,8 +197,8 @@ std::string OptionsTab::CPlayerSettingsHelper::getName() { if(!settings.heroName.empty()) return settings.heroName; - auto index = settings.hero >= CGI->heroh->heroes.size() ? 0 : settings.hero; - return CGI->heroh->heroes[index]->name; + auto index = settings.hero >= CGI->heroh->size() ? 0 : settings.hero; + return (*CGI->heroh)[index]->name; } } } @@ -245,8 +244,8 @@ std::string OptionsTab::CPlayerSettingsHelper::getTitle() } std::string OptionsTab::CPlayerSettingsHelper::getSubtitle() { - auto factionIndex = settings.castle >= CGI->townh->factions.size() ? 0 : settings.castle; - auto heroIndex = settings.hero >= CGI->heroh->heroes.size() ? 0 : settings.hero; + auto factionIndex = settings.castle >= CGI->townh->size() ? 0 : settings.castle; + auto heroIndex = settings.hero >= CGI->heroh->size() ? 0 : settings.hero; switch(type) { @@ -255,7 +254,7 @@ std::string OptionsTab::CPlayerSettingsHelper::getSubtitle() case HERO: { if(settings.hero >= 0) - return getName() + " - " + CGI->heroh->heroes[heroIndex]->heroClass->name; + return getName() + " - " + (*CGI->heroh)[heroIndex]->heroClass->name; return getName(); } @@ -267,7 +266,7 @@ std::string OptionsTab::CPlayerSettingsHelper::getSubtitle() return CGI->generaltexth->allTexts[87]; //500-1000 case PlayerSettings::RESOURCE: { - switch(CGI->townh->factions[factionIndex]->town->primaryRes) + switch((*CGI->townh)[factionIndex]->town->primaryRes) { case Res::MERCURY: return CGI->generaltexth->allTexts[694]; @@ -289,7 +288,7 @@ std::string OptionsTab::CPlayerSettingsHelper::getSubtitle() std::string OptionsTab::CPlayerSettingsHelper::getDescription() { - auto factionIndex = settings.castle >= CGI->townh->factions.size() ? 0 : settings.castle; + auto factionIndex = settings.castle >= CGI->townh->size() ? 0 : settings.castle; switch(type) { @@ -309,7 +308,7 @@ std::string OptionsTab::CPlayerSettingsHelper::getDescription() return CGI->generaltexth->allTexts[92]; //At the start of the game, 500-1000 gold is added to your Kingdom's resource pool case PlayerSettings::RESOURCE: { - switch(CGI->townh->factions[factionIndex]->town->primaryRes) + switch((*CGI->townh)[factionIndex]->town->primaryRes) { case Res::MERCURY: return CGI->generaltexth->allTexts[690]; @@ -376,9 +375,9 @@ void OptionsTab::CPlayerOptionTooltipBox::genTownWindow() pos = Rect(0, 0, 228, 290); genHeader(); labelAssociatedCreatures = std::make_shared(pos.w / 2 + 8, 122, FONT_MEDIUM, CENTER, Colors::YELLOW, CGI->generaltexth->allTexts[79]); - auto factionIndex = settings.castle >= CGI->townh->factions.size() ? 0 : settings.castle; + auto factionIndex = settings.castle >= CGI->townh->size() ? 0 : settings.castle; std::vector> components; - const CTown * town = CGI->townh->factions[factionIndex]->town; + const CTown * town = (*CGI->townh)[factionIndex]->town; for(auto & elem : town->creatures) { @@ -393,10 +392,10 @@ void OptionsTab::CPlayerOptionTooltipBox::genHeroWindow() pos = Rect(0, 0, 292, 226); genHeader(); labelHeroSpeciality = std::make_shared(pos.w / 2 + 4, 117, FONT_MEDIUM, CENTER, Colors::YELLOW, CGI->generaltexth->allTexts[78]); - auto heroIndex = settings.hero >= CGI->heroh->heroes.size() ? 0 : settings.hero; + auto heroIndex = settings.hero >= CGI->heroh->size() ? 0 : settings.hero; - imageSpeciality = std::make_shared("UN44", CGI->heroh->heroes[heroIndex]->imageIndex, 0, pos.w / 2 - 22, 134); - labelSpecialityName = std::make_shared(pos.w / 2, 188, FONT_SMALL, CENTER, Colors::WHITE, CGI->heroh->heroes[heroIndex]->specName); + imageSpeciality = std::make_shared("UN44", (*CGI->heroh)[heroIndex]->imageIndex, 0, pos.w / 2 - 22, 134); + labelSpecialityName = std::make_shared(pos.w / 2, 188, FONT_SMALL, CENTER, Colors::WHITE, (*CGI->heroh)[heroIndex]->specName); } void OptionsTab::CPlayerOptionTooltipBox::genBonusWindow() diff --git a/client/widgets/CArtifactHolder.cpp b/client/widgets/CArtifactHolder.cpp index ad79022ef..746b2fc0c 100644 --- a/client/widgets/CArtifactHolder.cpp +++ b/client/widgets/CArtifactHolder.cpp @@ -25,7 +25,6 @@ #include "../../CCallback.h" #include "../../lib/CArtHandler.h" -#include "../../lib/spells/CSpellHandler.h" #include "../../lib/CGeneralTextHandler.h" #include "../../lib/mapObjects/CGHeroInstance.h" @@ -45,10 +44,11 @@ void CHeroArtPlace::createImage() OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE); si32 imageIndex = 0; - if(ourArt) - imageIndex = ourArt->artType->iconIndex; + if(locked) imageIndex = ArtifactID::ART_LOCK; + else if(ourArt) + imageIndex = ourArt->artType->getIconIndex(); image = std::make_shared("artifact", imageIndex); if(!ourArt) @@ -68,7 +68,7 @@ void CHeroArtPlace::lockSlot(bool on) if (on) image->setFrame(ArtifactID::ART_LOCK); else if (ourArt) - image->setFrame(ourArt->artType->iconIndex); + image->setFrame(ourArt->artType->getIconIndex()); else image->setFrame(0); } @@ -172,7 +172,7 @@ void CHeroArtPlace::clickLeft(tribool down, bool previousState) else if(cur->isBig()) { //war machines cannot go to backpack - LOCPLINT->showInfoDialog(boost::str(boost::format(CGI->generaltexth->allTexts[153]) % cur->Name())); + LOCPLINT->showInfoDialog(boost::str(boost::format(CGI->generaltexth->allTexts[153]) % cur->getName())); } else { @@ -197,18 +197,6 @@ void CHeroArtPlace::clickLeft(tribool down, bool previousState) (!ourArt || ourOwner->curHero->tempOwner == LOCPLINT->playerID)) { setMeAsDest(); -// -// // Special case when the dest artifact can't be fit into the src slot. -// //CGI->arth->unequipArtifact(ourOwner->curHero->artifWorn, slotID); -// const CArtifactsOfHero* srcAOH = ourOwner->commonInfo->src.AOH; -// ui16 srcSlotID = ourOwner->commonInfo->src.slotID; -// if (ourArt && srcSlotID < 19 && !ourArt->canBePutAt(ArtifactLocation(srcAOH->curHero, srcSlotID))) -// { -// // Put dest artifact into owner's backpack. -// ourOwner->commonInfo->src.AOH = ourOwner; -// ourOwner->commonInfo->src.slotID = ourOwner->curHero->artifacts.size() + 19; -// } - ourOwner->realizeCurrentTransaction(); } } @@ -226,14 +214,12 @@ bool CHeroArtPlace::askToAssemble(const CArtifactInstance *art, ArtifactPosition for(const CArtifact *combination : assemblyPossibilities) { LOCPLINT->showArtifactAssemblyDialog( - art->artType->id, - combination->id, - true, - std::bind(&CCallback::assembleArtifacts, LOCPLINT->cb.get(), hero, slot, true, combination->id), - 0); + art->artType, + combination, + std::bind(&CCallback::assembleArtifacts, LOCPLINT->cb.get(), hero, slot, true, combination->id)); if(assemblyPossibilities.size() > 2) - logGlobal->warn("More than one possibility of assembling on %s... taking only first", art->artType->Name()); + logGlobal->warn("More than one possibility of assembling on %s... taking only first", art->artType->getName()); return true; } return false; @@ -243,14 +229,14 @@ void CHeroArtPlace::clickRight(tribool down, bool previousState) { if(ourArt && down && !locked && text.size() && !picked) //if there is no description or it's a lock, do nothing ;] { - if (slotID < GameConstants::BACKPACK_START) + if(slotID < GameConstants::BACKPACK_START) { if(ourOwner->allowedAssembling) { std::vector assemblyPossibilities = ourArt->assemblyPossibilities(ourOwner->curHero); // If the artifact can be assembled, display dialog. - if (askToAssemble(ourArt, slotID, ourOwner->curHero)) + if(askToAssemble(ourArt, slotID, ourOwner->curHero)) { return; } @@ -259,11 +245,9 @@ void CHeroArtPlace::clickRight(tribool down, bool previousState) if(ourArt->canBeDisassembled()) { LOCPLINT->showArtifactAssemblyDialog( - ourArt->artType->id, - 0, - false, - std::bind(&CCallback::assembleArtifacts, LOCPLINT->cb.get(), ourOwner->curHero, slotID, false, ArtifactID()), - 0); + ourArt->artType, + nullptr, + std::bind(&CCallback::assembleArtifacts, LOCPLINT->cb.get(), ourOwner->curHero, slotID, false, ArtifactID())); return; } } @@ -294,7 +278,7 @@ void CHeroArtPlace::select () } } - CCS->curh->dragAndDropCursor(make_unique("artifact", ourArt->artType->iconIndex)); + CCS->curh->dragAndDropCursor(make_unique("artifact", ourArt->artType->getIconIndex())); ourOwner->commonInfo->src.setTo(this, false); ourOwner->markPossibleSlots(ourArt); @@ -389,7 +373,7 @@ void CHeroArtPlace::setArtifact(const CArtifactInstance *art) } image->enable(); - image->setFrame(locked ? ArtifactID::ART_LOCK : art->artType->iconIndex); + image->setFrame(locked ? ArtifactID::ART_LOCK : art->artType->getIconIndex()); text = art->getEffectiveDescription(ourOwner->curHero); @@ -414,7 +398,7 @@ void CHeroArtPlace::setArtifact(const CArtifactInstance *art) if (locked) // Locks should appear as empty. hoverText = CGI->generaltexth->allTexts[507]; else - hoverText = boost::str(boost::format(CGI->generaltexth->heroscrn[1]) % ourArt->artType->Name()); + hoverText = boost::str(boost::format(CGI->generaltexth->heroscrn[1]) % ourArt->artType->getName()); } void CArtifactsOfHero::SCommonPart::reset() @@ -769,7 +753,7 @@ void CArtifactsOfHero::artifactMoved(const ArtifactLocation &src, const Artifact commonInfo->src.art = dst.getArt(); commonInfo->src.slotID = dst.slot; assert(commonInfo->src.AOH); - CCS->curh->dragAndDropCursor(make_unique("artifact", dst.getArt()->artType->iconIndex)); + CCS->curh->dragAndDropCursor(make_unique("artifact", dst.getArt()->artType->getIconIndex())); markPossibleSlots(dst.getArt()); } } @@ -1020,7 +1004,7 @@ void CCommanderArtPlace::createImage() int imageIndex = 0; if(ourArt) - imageIndex = ourArt->artType->iconIndex; + imageIndex = ourArt->artType->getIconIndex(); image = std::make_shared("artifact", imageIndex); if(!ourArt) @@ -1055,7 +1039,7 @@ void CCommanderArtPlace::setArtifact(const CArtifactInstance * art) } image->enable(); - image->setFrame(art->artType->iconIndex); + image->setFrame(art->artType->getIconIndex()); text = art->getEffectiveDescription(); diff --git a/client/widgets/CComponent.cpp b/client/widgets/CComponent.cpp index 6f6a49598..b2fbb26fa 100644 --- a/client/widgets/CComponent.cpp +++ b/client/widgets/CComponent.cpp @@ -112,7 +112,7 @@ const std::vector CComponent::getFileName() case spell: return gen(spellsArr); case morale: return gen(moraleArr); case luck: return gen(luckArr); - case building: return std::vector(4, CGI->townh->factions[subtype]->town->clientInfo.buildingsIcons); + case building: return std::vector(4, (*CGI->townh)[subtype]->town->clientInfo.buildingsIcons); case hero: return gen(heroArr); case flag: return gen(flagArr); } @@ -127,8 +127,8 @@ size_t CComponent::getIndex() case primskill: return subtype; case secskill: return subtype*3 + 3 + val - 1; case resource: return subtype; - case creature: return CGI->creh->creatures[subtype]->iconIndex; - case artifact: return CGI->arth->artifacts[subtype]->iconIndex; + case creature: return CGI->creatures()->getByIndex(subtype)->getIconIndex(); + case artifact: return CGI->artifacts()->getByIndex(subtype)->getIconIndex(); case experience: return 4; case spell: return subtype; case morale: return val+3; @@ -159,15 +159,15 @@ std::string CComponent::getDescription() } else { - art.reset(CArtifactInstance::createScroll(static_cast(val))); + art.reset(CArtifactInstance::createScroll(SpellID(val))); } return art->getEffectiveDescription(); } case experience: return CGI->generaltexth->allTexts[241]; - case spell: return CGI->spellh->objects[subtype]->getLevelInfo(val).description; + case spell: return CGI->spellh->objects[subtype]->getLevelDescription(val); case morale: return CGI->generaltexth->heroscrn[ 4 - (val>0) + (val<0)]; case luck: return CGI->generaltexth->heroscrn[ 7 - (val>0) + (val<0)]; - case building: return CGI->townh->factions[subtype]->town->buildings[BuildingID(val)]->Description(); + case building: return (*CGI->townh)[subtype]->town->buildings[BuildingID(val)]->Description(); case hero: return ""; case flag: return ""; } @@ -193,8 +193,8 @@ std::string CComponent::getSubtitleInternal() case primskill: return boost::str(boost::format("%+d %s") % val % (subtype < 4 ? CGI->generaltexth->primarySkillNames[subtype] : CGI->generaltexth->allTexts[387])); case secskill: return CGI->generaltexth->levels[val-1] + "\n" + CGI->skillh->skillName(subtype); case resource: return boost::lexical_cast(val); - case creature: return (val? boost::lexical_cast(val) + " " : "") + CGI->creh->creatures[subtype]->*(val != 1 ? &CCreature::namePl : &CCreature::nameSing); - case artifact: return CGI->arth->artifacts[subtype]->Name(); + case creature: return (val? boost::lexical_cast(val) + " " : "") + CGI->creh->objects[subtype]->*(val != 1 ? &CCreature::namePl : &CCreature::nameSing); + case artifact: return CGI->artifacts()->getByIndex(subtype)->getName(); case experience: { if(subtype == 1) //+1 level - tree of knowledge @@ -208,15 +208,15 @@ std::string CComponent::getSubtitleInternal() return boost::lexical_cast(val); //amount of experience OR level required for seer hut; } } - case spell: return CGI->spellh->objects[subtype]->name; + case spell: return CGI->spells()->getByIndex(subtype)->getName(); case morale: return ""; case luck: return ""; case building: { - auto building = CGI->townh->factions[subtype]->town->buildings[BuildingID(val)]; + auto building = (*CGI->townh)[subtype]->town->buildings[BuildingID(val)]; if(!building) { - logGlobal->error("Town of faction %s has no building #%d", CGI->townh->factions[subtype]->town->faction->name, val); + logGlobal->error("Town of faction %s has no building #%d", (*CGI->townh)[subtype]->town->faction->name, val); return (boost::format("Missing building #%d") % val).str(); } return building->Name(); @@ -224,7 +224,7 @@ std::string CComponent::getSubtitleInternal() case hero: return ""; case flag: return CGI->generaltexth->capColors[subtype]; } - assert(0); + logGlobal->error("Invalid CComponent type: %d", (int)compType); return ""; } diff --git a/client/widgets/CGarrisonInt.cpp b/client/widgets/CGarrisonInt.cpp index 697e5ca49..7831d527c 100644 --- a/client/widgets/CGarrisonInt.cpp +++ b/client/widgets/CGarrisonInt.cpp @@ -355,7 +355,7 @@ void CGarrisonSlot::update() if(creature) { creatureImage->enable(); - creatureImage->setFrame(creature->iconIndex); + creatureImage->setFrame(creature->getIconIndex()); stackCount->enable(); stackCount->setText(boost::lexical_cast(myStack->count)); @@ -367,12 +367,12 @@ void CGarrisonSlot::update() } } -CGarrisonSlot::CGarrisonSlot(CGarrisonInt * Owner, int x, int y, SlotID IID, CGarrisonSlot::EGarrisonType Upg, const CStackInstance * Creature) - : ID(IID), - owner(Owner), - myStack(Creature), - creature(nullptr), - upg(Upg) +CGarrisonSlot::CGarrisonSlot(CGarrisonInt * Owner, int x, int y, SlotID IID, CGarrisonSlot::EGarrisonType Upg, const CStackInstance * creature_) + : ID(IID), + owner(Owner), + myStack(creature_), + creature(creature_ ? creature_->type : nullptr), + upg(Upg) { OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE); @@ -382,7 +382,7 @@ CGarrisonSlot::CGarrisonSlot(CGarrisonInt * Owner, int x, int y, SlotID IID, CGa std::string imgName = owner->smallIcons ? "cprsmall" : "TWCRPORT"; creatureImage = std::make_shared(imgName, 0); - creatureImage->disable(); + creatureImage->disable(); selectionImage = std::make_shared(imgName, 1); selectionImage->disable(); @@ -496,13 +496,13 @@ CGarrisonInt::CGarrisonInt(int x, int y, int inx, const Point & garsOffset, const CArmedInstance * s1, const CArmedInstance * s2, bool _removableUnits, bool smallImgs, bool _twoRows) : highlighted(nullptr), - inSplittingMode(false), - interx(inx), - garOffset(garsOffset), - pb(false), - smallIcons(smallImgs), - removableUnits(_removableUnits), - twoRows(_twoRows) + inSplittingMode(false), + interx(inx), + garOffset(garsOffset), + pb(false), + smallIcons(smallImgs), + removableUnits(_removableUnits), + twoRows(_twoRows) { OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE); diff --git a/client/widgets/CGarrisonInt.h b/client/widgets/CGarrisonInt.h index 1defc7b08..9e7b7eda4 100644 --- a/client/widgets/CGarrisonInt.h +++ b/client/widgets/CGarrisonInt.h @@ -53,7 +53,7 @@ public: void clickRight(tribool down, bool previousState) override; void clickLeft(tribool down, bool previousState) override; void update(); - CGarrisonSlot(CGarrisonInt *Owner, int x, int y, SlotID IID, EGarrisonType Upg=EGarrisonType::UP, const CStackInstance * Creature=nullptr); + CGarrisonSlot(CGarrisonInt *Owner, int x, int y, SlotID IID, EGarrisonType Upg=EGarrisonType::UP, const CStackInstance * creature_ = nullptr); void splitIntoParts(EGarrisonType type, int amount, int maxOfSplittedSlots); void handleSplittingShortcuts(); diff --git a/client/widgets/MiscWidgets.cpp b/client/widgets/MiscWidgets.cpp index 773417f35..8dc61130f 100644 --- a/client/widgets/MiscWidgets.cpp +++ b/client/widgets/MiscWidgets.cpp @@ -243,7 +243,7 @@ void CArmyTooltip::init(const InfoAboutArmy &army) continue; } - icons.push_back(std::make_shared("CPRSMALL", slot.second.type->iconIndex, 0, slotsPos[slot.first.getNum()].x, slotsPos[slot.first.getNum()].y)); + icons.push_back(std::make_shared("CPRSMALL", slot.second.type->getIconIndex(), 0, slotsPos[slot.first.getNum()].x, slotsPos[slot.first.getNum()].y)); std::string subtitle; if(army.army.isDetailed) @@ -443,12 +443,12 @@ CCreaturePic::CCreaturePic(int x, int y, const CCreature * cre, bool Big, bool A TFaction faction = cre->faction; - assert(CGI->townh->factions.size() > faction); + assert(CGI->townh->size() > faction); if(Big) - bg = std::make_shared(CGI->townh->factions[faction]->creatureBg130); + bg = std::make_shared((*CGI->townh)[faction]->creatureBg130); else - bg = std::make_shared(CGI->townh->factions[faction]->creatureBg120); + bg = std::make_shared((*CGI->townh)[faction]->creatureBg120); anim = std::make_shared(0, 0, cre->animDefName); anim->clipRect(cre->isDoubleWide()?170:150, 155, bg->pos.w, bg->pos.h); anim->startPreview(cre->hasBonusOfType(Bonus::SIEGE_WEAPON)); diff --git a/client/windows/CCastleInterface.cpp b/client/windows/CCastleInterface.cpp index 925a04f48..88843f536 100644 --- a/client/windows/CCastleInterface.cpp +++ b/client/windows/CCastleInterface.cpp @@ -112,11 +112,11 @@ void CBuildingRect::hover(bool on) void CBuildingRect::clickLeft(tribool down, bool previousState) { if( previousState && getBuilding() && area && !down && (parent->selectedBuilding==this)) - if (!CSDL_Ext::isTransparent(area, GH.current->motion.x - pos.x, GH.current->motion.y - pos.y)) //inside building image + if (!CSDL_Ext::isTransparent(area, GH.current->motion.x-pos.x, GH.current->motion.y-pos.y) ) //inside building image { auto building = getBuilding(); parent->buildingClicked(building->bid, building->subId, building->upgrade); - } +} } void CBuildingRect::clickRight(tribool down, bool previousState) @@ -231,7 +231,7 @@ std::string CBuildingRect::getSubtitle()//hover text for building if(availableCreatures.size()) { int creaID = availableCreatures.back();//taking last of available creatures - return CGI->generaltexth->allTexts[16] + " " + CGI->creh->creatures.at(creaID)->namePl; + return CGI->generaltexth->allTexts[16] + " " + CGI->creh->objects.at(creaID)->namePl; } else { @@ -271,7 +271,7 @@ CDwellingInfoBox::CDwellingInfoBox(int centerX, int centerY, const CGTownInstanc OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE); background->colorize(Town->tempOwner); - const CCreature * creature = CGI->creh->creatures.at(Town->creatures.at(level).second.back()); + const CCreature * creature = CGI->creh->objects.at(Town->creatures.at(level).second.back()); title = std::make_shared(80, 30, FONT_SMALL, CENTER, Colors::WHITE, creature->namePl); animation = std::make_shared(30, 44, creature, true, true); @@ -607,7 +607,7 @@ void CCastleBuildings::recreate() const CStructure * toAdd = *boost::max_element(entry.second, [=](const CStructure * a, const CStructure * b) { return build->getDistance(a->building->bid) - < build->getDistance(b->building->bid); + < build->getDistance(b->building->bid); }); buildings.push_back(std::make_shared(this, town, toAdd)); @@ -731,7 +731,7 @@ void CCastleBuildings::buildingClicked(BuildingID building, BuildingSubID::EBuil case BuildingSubID::FOUNTAIN_OF_FORTUNE: enterFountain(building, subID, upgrades); - break; + break; case BuildingSubID::FREELANCERS_GUILD: if(getHero()) @@ -751,7 +751,7 @@ void CCastleBuildings::buildingClicked(BuildingID building, BuildingSubID::EBuil if(upgrades == BuildingID::TAVERN) LOCPLINT->showTavernWindow(town); else - enterBuilding(building); + enterBuilding(building); break; case BuildingSubID::CASTLE_GATE: @@ -794,9 +794,11 @@ void CCastleBuildings::enterBlacksmith(ArtifactID artifactID) LOCPLINT->showInfoDialog(boost::str(boost::format(CGI->generaltexth->allTexts[273]) % town->town->buildings.find(BuildingID::BLACKSMITH)->second->Name())); return; } - int price = CGI->arth->artifacts[artifactID]->price; + auto art = artifactID.toArtifact(CGI->artifacts()); + + int price = art->getPrice(); bool possible = LOCPLINT->cb->getResourceAmount(Res::GOLD) >= price && !hero->hasArt(artifactID); - CreatureID cre = artifactID.toArtifact()->warMachine; + CreatureID cre = art->getWarMachine(); GH.pushIntT(possible, cre, artifactID, hero->id); } @@ -844,7 +846,7 @@ void CCastleBuildings::enterToTheQuickRecruitmentWindow() void CCastleBuildings::enterFountain(const BuildingID & building, BuildingSubID::EBuildingSubID subID, BuildingID::EBuildingID upgrades) { - std::vector> comps(1, std::make_shared(CComponent::building,town->subID, building)); + std::vector> comps(1, std::make_shared(CComponent::building,town->subID,building)); std::string descr = town->town->buildings.find(building)->second->Description(); std::string hasNotProduced; std::string hasProduced; @@ -878,9 +880,9 @@ void CCastleBuildings::enterFountain(const BuildingID & building, BuildingSubID: else //Mystic Pond produced something; { descr += "\n\n" + hasProduced; - boost::algorithm::replace_first(descr, "%s", CGI->generaltexth->restypes[town->bonusValue.first]); - boost::algorithm::replace_first(descr, "%d", boost::lexical_cast(town->bonusValue.second)); - } + boost::algorithm::replace_first(descr,"%s",CGI->generaltexth->restypes[town->bonusValue.first]); + boost::algorithm::replace_first(descr,"%d",boost::lexical_cast(town->bonusValue.second)); + } } LOCPLINT->showInfoDialog(descr, comps); } @@ -972,9 +974,9 @@ CCreaInfo::CCreaInfo(Point position, const CGTownInstance * Town, int Level, boo addUsedEvents(LCLICK | RCLICK | HOVER); ui32 creatureID = town->creatures[level].second.back(); - creature = CGI->creh->creatures[creatureID]; + creature = CGI->creh->objects[creatureID]; - picture = std::make_shared("CPRSMALL", creature->iconIndex, 0, 8, 0); + picture = std::make_shared("CPRSMALL", creature->getIconIndex(), 0, 8, 0); std::string value; if(showAvailable) @@ -1627,7 +1629,7 @@ CFortScreen::RecruitArea::RecruitArea(int posX, int posY, const CGTownInstance * Rect sizes(287, 4, 96, 18); values.push_back(std::make_shared(sizes, CGI->generaltexth->allTexts[190], CGI->generaltexth->fcommands[0], getMyCreature()->getAttack(false))); sizes.y+=20; - values.push_back(std::make_shared(sizes, CGI->generaltexth->allTexts[191], CGI->generaltexth->fcommands[1], getMyCreature()->getDefence(false))); + values.push_back(std::make_shared(sizes, CGI->generaltexth->allTexts[191], CGI->generaltexth->fcommands[1], getMyCreature()->getDefense(false))); sizes.y+=21; values.push_back(std::make_shared(sizes, CGI->generaltexth->allTexts[199], CGI->generaltexth->fcommands[2], getMyCreature()->getMinDamage(false), getMyCreature()->getMaxDamage(false))); sizes.y+=20; @@ -1642,9 +1644,9 @@ CFortScreen::RecruitArea::RecruitArea(int posX, int posY, const CGTownInstance * const CCreature * CFortScreen::RecruitArea::getMyCreature() { if(!town->creatures.at(level).second.empty()) // built - return VLC->creh->creatures[town->creatures.at(level).second.back()]; + return VLC->creh->objects[town->creatures.at(level).second.back()]; if(!town->town->creatures.at(level).empty()) // there are creatures on this level - return VLC->creh->creatures[town->town->creatures.at(level).front()]; + return VLC->creh->objects[town->town->creatures.at(level).front()]; return nullptr; } @@ -1750,13 +1752,13 @@ CMageGuildScreen::Scroll::Scroll(Point position, const CSpell *Spell) void CMageGuildScreen::Scroll::clickLeft(tribool down, bool previousState) { if(down) - LOCPLINT->showInfoDialog(spell->getLevelInfo(0).description, std::make_shared(CComponent::spell, spell->id)); + LOCPLINT->showInfoDialog(spell->getLevelDescription(0), std::make_shared(CComponent::spell, spell->id)); } void CMageGuildScreen::Scroll::clickRight(tribool down, bool previousState) { if(down) - CRClickPopup::createAndPush(spell->getLevelInfo(0).description, std::make_shared(CComponent::spell, spell->id)); + CRClickPopup::createAndPush(spell->getLevelDescription(0), std::make_shared(CComponent::spell, spell->id)); } void CMageGuildScreen::Scroll::hover(bool on) @@ -1781,15 +1783,15 @@ CBlacksmithDialog::CBlacksmithDialog(bool possible, CreatureID creMachineID, Art animBG = std::make_shared("TPSMITBK", 64, 50); animBG->needRefresh = true; - const CCreature * creature = CGI->creh->creatures[creMachineID]; + const CCreature * creature = CGI->creh->objects[creMachineID]; anim = std::make_shared(64, 50, creature->animDefName); anim->clipRect(113,125,200,150); title = std::make_shared(165, 28, FONT_BIG, CENTER, Colors::YELLOW, - boost::str(boost::format(CGI->generaltexth->allTexts[274]) % creature->nameSing)); + boost::str(boost::format(CGI->generaltexth->allTexts[274]) % creature->nameSing)); costText = std::make_shared(165, 218, FONT_MEDIUM, CENTER, Colors::WHITE, CGI->generaltexth->jktexts[43]); costValue = std::make_shared(165, 290, FONT_MEDIUM, CENTER, Colors::WHITE, - boost::lexical_cast(CGI->arth->artifacts[aid]->price)); + boost::lexical_cast(aid.toArtifact(CGI->artifacts())->getPrice())); std::string text = boost::str(boost::format(CGI->generaltexth->allTexts[595]) % creature->nameSing); buy = std::make_shared(Point(42, 312), "IBUY30.DEF", CButton::tooltip(text), [&](){ close(); }, SDLK_RETURN); diff --git a/client/windows/CCreatureWindow.cpp b/client/windows/CCreatureWindow.cpp index 27572922f..64ce5ee0f 100644 --- a/client/windows/CCreatureWindow.cpp +++ b/client/windows/CCreatureWindow.cpp @@ -10,6 +10,9 @@ #include "StdInc.h" #include "CCreatureWindow.h" +#include +#include + #include "../CGameInfo.h" #include "../CPlayerInterface.h" #include "../widgets/Buttons.h" @@ -26,7 +29,6 @@ #include "../../lib/CGeneralTextHandler.h" #include "../../lib/CModHandler.h" #include "../../lib/CHeroHandler.h" -#include "../../lib/spells/CSpellHandler.h" #include "../../lib/CGameState.h" using namespace CSDL_Ext; @@ -193,7 +195,7 @@ CStackWindow::ActiveSpellsSection::ActiveSpellsSection(CStackWindow * owner, int std::vector spells = battleStack->activeSpells(); for(si32 effect : spells) { - const CSpell * sp = CGI->spellh->objects[effect]; + const spells::Spell * spell = CGI->spells()->getByIndex(effect); std::string spellText; @@ -204,7 +206,7 @@ CStackWindow::ActiveSpellsSection::ActiveSpellsSection(CStackWindow * owner, int if (hasGraphics) { spellText = CGI->generaltexth->allTexts[610]; //"%s, duration: %d rounds." - boost::replace_first(spellText, "%s", sp->name); + boost::replace_first(spellText, "%s", spell->getName()); //FIXME: support permanent duration int duration = battleStack->getBonusLocalFirst(Selector::source(Bonus::SPELL_EFFECT,effect))->turnsRemain; boost::replace_first(spellText, "%d", boost::lexical_cast(duration)); @@ -320,7 +322,7 @@ CStackWindow::ButtonsSection::ButtonsSection(CStackWindow * owner, int yOffset) }; auto upgradeBtn = std::make_shared(Point(221 + (int)buttonIndex * 40, 5), "stackWindow/upgradeButton", CGI->generaltexth->zelp[446], onClick, SDLK_1); - upgradeBtn->addOverlay(std::make_shared("CPRSMALL", VLC->creh->creatures[upgradeInfo.info.newID[buttonIndex]]->iconIndex)); + upgradeBtn->addOverlay(std::make_shared("CPRSMALL", VLC->creh->objects[upgradeInfo.info.newID[buttonIndex]]->iconIndex)); upgrade[buttonIndex] = upgradeBtn; } @@ -520,7 +522,7 @@ CStackWindow::MainSection::MainSection(CStackWindow * owner, int yOffset, bool s if(battleStack != nullptr) // in battle { addStatLabel(EStat::ATTACK, parent->info->creature->getAttack(battleStack->isShooter()), battleStack->getAttack(battleStack->isShooter())); - addStatLabel(EStat::DEFENCE, parent->info->creature->getDefence(battleStack->isShooter()), battleStack->getDefence(battleStack->isShooter())); + addStatLabel(EStat::DEFENCE, parent->info->creature->getDefense(battleStack->isShooter()), battleStack->getDefense(battleStack->isShooter())); addStatLabel(EStat::DAMAGE, parent->info->stackNode->getMinDamage(battleStack->isShooter()) * dmgMultiply, battleStack->getMaxDamage(battleStack->isShooter()) * dmgMultiply); addStatLabel(EStat::HEALTH, parent->info->creature->MaxHealth(), battleStack->MaxHealth()); addStatLabel(EStat::SPEED, parent->info->creature->Speed(), battleStack->Speed()); @@ -540,7 +542,7 @@ CStackWindow::MainSection::MainSection(CStackWindow * owner, int yOffset, bool s const bool caster = parent->info->stackNode->valOfBonuses(Bonus::CASTS); addStatLabel(EStat::ATTACK, parent->info->creature->getAttack(shooter), parent->info->stackNode->getAttack(shooter)); - addStatLabel(EStat::DEFENCE, parent->info->creature->getDefence(shooter), parent->info->stackNode->getDefence(shooter)); + addStatLabel(EStat::DEFENCE, parent->info->creature->getDefense(shooter), parent->info->stackNode->getDefense(shooter)); addStatLabel(EStat::DAMAGE, parent->info->stackNode->getMinDamage(shooter) * dmgMultiply, parent->info->stackNode->getMaxDamage(shooter) * dmgMultiply); addStatLabel(EStat::HEALTH, parent->info->creature->MaxHealth(), parent->info->stackNode->MaxHealth()); addStatLabel(EStat::SPEED, parent->info->creature->Speed(), parent->info->stackNode->Speed()); diff --git a/client/windows/CHeroWindow.h b/client/windows/CHeroWindow.h index cb686291c..b973117e8 100644 --- a/client/windows/CHeroWindow.h +++ b/client/windows/CHeroWindow.h @@ -16,7 +16,6 @@ class CButton; struct SDL_Surface; class CGHeroInstance; -class CArtifact; class CHeroWindow; class LClickableAreaHero; class LRClickableAreaWText; diff --git a/client/windows/CKingdomInterface.cpp b/client/windows/CKingdomInterface.cpp index e5e845714..0957aab22 100644 --- a/client/windows/CKingdomInterface.cpp +++ b/client/windows/CKingdomInterface.cpp @@ -163,7 +163,7 @@ std::string InfoBoxAbstractHeroData::getNameText() case HERO_EXPERIENCE: return CGI->generaltexth->jktexts[6]; case HERO_SPECIAL: - return CGI->heroh->heroes[getSubID()]->specName; + return CGI->heroh->objects[getSubID()]->specName; case HERO_SECONDARY_SKILL: if (getValue()) return CGI->skillh->skillName(getSubID()); @@ -229,7 +229,7 @@ size_t InfoBoxAbstractHeroData::getImageIndex() switch (type) { case HERO_SPECIAL: - return VLC->heroh->heroes[getSubID()]->imageIndex; + return CGI->heroh->objects[getSubID()]->imageIndex; case HERO_PRIMARY_SKILL: return getSubID(); case HERO_MANA: @@ -256,7 +256,7 @@ void InfoBoxAbstractHeroData::prepareMessage(std::string & text, std::shared_ptr switch (type) { case HERO_SPECIAL: - text = CGI->heroh->heroes[getSubID()]->specDescr; + text = CGI->heroh->objects[getSubID()]->specDescr; break; case HERO_PRIMARY_SKILL: text = CGI->generaltexth->arraytxt[2+getSubID()]; diff --git a/client/windows/CSpellWindow.cpp b/client/windows/CSpellWindow.cpp index 3ba5ee167..32a68dd07 100644 --- a/client/windows/CSpellWindow.cpp +++ b/client/windows/CSpellWindow.cpp @@ -120,7 +120,7 @@ CSpellWindow::CSpellWindow(const CGHeroInstance * _myHero, CPlayerInterface * _m for(const auto spell : mySpells) { - int * sitesPerOurTab = spell->isCombatSpell() ? sitesPerTabBattle : sitesPerTabAdv; + int * sitesPerOurTab = spell->isCombat() ? sitesPerTabBattle : sitesPerTabAdv; ++sitesPerOurTab[4]; @@ -332,7 +332,7 @@ void CSpellWindow::computeSpellsPerArea() spellsCurSite.reserve(mySpells.size()); for(const CSpell * spell : mySpells) { - if(spell->isCombatSpell() ^ !battleSpellsOnly + if(spell->isCombat() ^ !battleSpellsOnly && ((selectedTab == 4) || spell->school.at((ESpellSchool)selectedTab)) ) { @@ -522,7 +522,7 @@ void CSpellWindow::SpellArea::clickLeft(tribool down, bool previousState) { if(mySpell && !down) { - int spellCost = owner->myInt->cb->getSpellCost(mySpell, owner->myHero); + auto spellCost = owner->myInt->cb->getSpellCost(mySpell, owner->myHero); if(spellCost > owner->myHero->mana) //insufficient mana { owner->myInt->showInfoDialog(boost::str(boost::format(CGI->generaltexth->allTexts[206]) % spellCost % owner->myHero->mana)); @@ -531,8 +531,8 @@ void CSpellWindow::SpellArea::clickLeft(tribool down, bool previousState) //anything that is not combat spell is adventure spell //this not an error in general to cast even creature ability with hero - const bool combatSpell = mySpell->isCombatSpell(); - if(mySpell->isCombatSpell() != !mySpell->isAdventureSpell()) + const bool combatSpell = mySpell->isCombat(); + if(combatSpell == mySpell->isAdventure()) { logGlobal->error("Spell have invalid flags"); return; @@ -545,7 +545,7 @@ void CSpellWindow::SpellArea::clickLeft(tribool down, bool previousState) if((combatSpell ^ inCombat) || inCastle) { std::vector> hlp(1, std::make_shared(CComponent::spell, mySpell->id, 0)); - owner->myInt->showInfoDialog(mySpell->getLevelInfo(schoolLevel).description, hlp); + owner->myInt->showInfoDialog(mySpell->getLevelDescription(schoolLevel), hlp); } else if(combatSpell) { @@ -600,7 +600,7 @@ void CSpellWindow::SpellArea::clickRight(tribool down, bool previousState) boost::algorithm::replace_first(dmgInfo, "%d", boost::lexical_cast(causedDmg)); } - CRClickPopup::createAndPush(mySpell->getLevelInfo(schoolLevel).description + dmgInfo, std::make_shared(CComponent::spell, mySpell->id)); + CRClickPopup::createAndPush(mySpell->getLevelDescription(schoolLevel) + dmgInfo, std::make_shared(CComponent::spell, mySpell->id)); } } @@ -625,7 +625,7 @@ void CSpellWindow::SpellArea::setSpell(const CSpell * spell) mySpell = spell; if(mySpell) { - int whichSchool = 0; //0 - air magic, 1 - fire magic, 2 - water magic, 3 - earth magic, + int32_t whichSchool = 0; //0 - air magic, 1 - fire magic, 2 - water magic, 3 - earth magic, schoolLevel = owner->myHero->getSpellSchoolLevel(mySpell, &whichSchool); auto spellCost = owner->myInt->cb->getSpellCost(mySpell, owner->myHero); diff --git a/client/windows/CTradeWindow.cpp b/client/windows/CTradeWindow.cpp index 43b4baa18..05091a70d 100644 --- a/client/windows/CTradeWindow.cpp +++ b/client/windows/CTradeWindow.cpp @@ -114,9 +114,9 @@ int CTradeWindow::CTradeableItem::getIndex() case ARTIFACT_TYPE: case ARTIFACT_INSTANCE: case ARTIFACT_PLACEHOLDER: - return VLC->arth->artifacts[id]->iconIndex; + return CGI->artifacts()->getByIndex(id)->getIconIndex(); case CREATURE: - return VLC->creh->creatures[id]->iconIndex; + return CGI->creatures()->getByIndex(id)->getIconIndex(); default: return -1; } @@ -244,13 +244,13 @@ void CTradeWindow::CTradeableItem::hover(bool on) { case CREATURE: case CREATURE_PLACEHOLDER: - GH.statusbar->setText(boost::str(boost::format(CGI->generaltexth->allTexts[481]) % CGI->creh->creatures[id]->namePl)); + GH.statusbar->setText(boost::str(boost::format(CGI->generaltexth->allTexts[481]) % CGI->creh->objects[id]->namePl)); break; case ARTIFACT_PLACEHOLDER: if(id < 0) GH.statusbar->setText(CGI->generaltexth->zelp[582].first); else - GH.statusbar->setText(CGI->arth->artifacts[id]->Name()); + GH.statusbar->setText(CGI->artifacts()->getByIndex(id)->getName()); break; } } @@ -263,13 +263,13 @@ void CTradeWindow::CTradeableItem::clickRight(tribool down, bool previousState) { case CREATURE: case CREATURE_PLACEHOLDER: - //GH.statusbar->print(boost::str(boost::format(CGI->generaltexth->allTexts[481]) % CGI->creh->creatures[id]->namePl)); + //GH.statusbar->print(boost::str(boost::format(CGI->generaltexth->allTexts[481]) % CGI->creh->objects[id]->namePl)); break; case ARTIFACT_TYPE: case ARTIFACT_PLACEHOLDER: //TODO: it's would be better for market to contain actual CArtifactInstance and not just ids of certain artifact type so we can use getEffectiveDescription. if(id >= 0) - adventureInt->handleRightClick(CGI->arth->artifacts[id]->Description(), down); + adventureInt->handleRightClick(CGI->artifacts()->getByIndex(id)->getDescription(), down); break; } } @@ -285,14 +285,14 @@ std::string CTradeWindow::CTradeableItem::getName(int number) const return CGI->generaltexth->restypes[id]; case CREATURE: if(number == 1) - return CGI->creh->creatures[id]->nameSing; + return CGI->creh->objects[id]->nameSing; else - return CGI->creh->creatures[id]->namePl; + return CGI->creh->objects[id]->namePl; case ARTIFACT_TYPE: case ARTIFACT_INSTANCE: - return CGI->arth->artifacts[id]->Name(); + return CGI->artifacts()->getByIndex(id)->getName(); } - assert(0); + logGlobal->error("Invalid trade item type: %d", (int)type); return ""; } @@ -668,14 +668,14 @@ CMarketplaceWindow::CMarketplaceWindow(const IMarket * Market, const CGHeroInsta switch (mode) { case EMarketMode::CREATURE_RESOURCE: - title = CGI->townh->factions[ETownType::STRONGHOLD]->town->buildings[BuildingID::FREELANCERS_GUILD]->Name(); + title = (*CGI->townh)[ETownType::STRONGHOLD]->town->buildings[BuildingID::FREELANCERS_GUILD]->Name(); break; case EMarketMode::RESOURCE_ARTIFACT: - title = CGI->townh->factions[market->o->subID]->town->buildings[BuildingID::ARTIFACT_MERCHANT]->Name(); + title = (*CGI->townh)[market->o->subID]->town->buildings[BuildingID::ARTIFACT_MERCHANT]->Name(); sliderNeeded = false; break; case EMarketMode::ARTIFACT_RESOURCE: - title = CGI->townh->factions[market->o->subID]->town->buildings[BuildingID::ARTIFACT_MERCHANT]->Name(); + title = (*CGI->townh)[market->o->subID]->town->buildings[BuildingID::ARTIFACT_MERCHANT]->Name(); sliderNeeded = false; break; default: diff --git a/client/windows/GUIClasses.cpp b/client/windows/GUIClasses.cpp index 305a9c824..94f69c93b 100644 --- a/client/windows/GUIClasses.cpp +++ b/client/windows/GUIClasses.cpp @@ -159,7 +159,7 @@ void CRecruitmentWindow::buy() if(dst->ID == Obj::HERO) { txt = CGI->generaltexth->allTexts[425]; //The %s would join your hero, but there aren't enough provisions to support them. - boost::algorithm::replace_first(txt, "%s", slider->getValue() > 1 ? CGI->creh->creatures[crid]->namePl : CGI->creh->creatures[crid]->nameSing); + boost::algorithm::replace_first(txt, "%s", slider->getValue() > 1 ? CGI->creh->objects[crid]->namePl : CGI->creh->objects[crid]->nameSing); } else { @@ -249,7 +249,7 @@ void CRecruitmentWindow::availableCreaturesChanged() //create new cards for(auto & creature : boost::adaptors::reverse(dwelling->creatures[i].second)) - cards.push_back(std::make_shared(this, CGI->creh->creatures[creature], amount)); + cards.push_back(std::make_shared(this, CGI->creh->objects[creature], amount)); } const int creatureWidth = 102; @@ -1040,7 +1040,7 @@ CPuzzleWindow::CPuzzleWindow(const int3 & GrailPos, double discoveredRatio) int faction = LOCPLINT->cb->getStartInfo()->playerInfos.find(LOCPLINT->playerID)->second.castle; - auto & puzzleMap = CGI->townh->factions[faction]->puzzleMap; + auto & puzzleMap = (*CGI->townh)[faction]->puzzleMap; for(auto & elem : puzzleMap) { @@ -1276,10 +1276,10 @@ CUniversityWindow::CUniversityWindow(const CGHeroInstance * _hero, const IMarket { auto faction = town->town->faction->index; auto bid = town->town->getSpecialBuilding(BuildingSubID::MAGIC_UNIVERSITY)->bid; - titlePic = std::make_shared(CGI->townh->factions[faction]->town->clientInfo.buildingsIcons, bid); + titlePic = std::make_shared((*CGI->townh)[faction]->town->clientInfo.buildingsIcons, bid); } else - titlePic = std::make_shared(CGI->townh->factions[ETownType::CONFLUX]->town->clientInfo.buildingsIcons, BuildingID::MAGIC_UNIVERSITY); + titlePic = std::make_shared((*CGI->townh)[ETownType::CONFLUX]->town->clientInfo.buildingsIcons, BuildingID::MAGIC_UNIVERSITY); } else titlePic = std::make_shared("UNIVBLDG"); diff --git a/cmake_modules/FindLuaJIT.cmake b/cmake_modules/FindLuaJIT.cmake new file mode 100644 index 000000000..2cc028c09 --- /dev/null +++ b/cmake_modules/FindLuaJIT.cmake @@ -0,0 +1,50 @@ +# Locate LuaJIT library +# This module defines +# LUAJIT_FOUND, if false, do not try to link to Lua +# LUA_INCLUDE_DIR, where to find lua.h +# LUA_VERSION_STRING, the version of Lua found (since CMake 2.8.8) +# +# This module is similar to FindLua51.cmake except that it finds LuaJit instead. + +FIND_PATH(LUA_INCLUDE_DIR luajit.h + HINTS + $ENV{LUA_DIR} + PATH_SUFFIXES include/luajit include/luajit-2.1 include/luajit-2.0 include/luajit-5_1-2.1 include/luajit-5_1-2.0 include + PATHS + ~/Library/Frameworks + /Library/Frameworks + /sw # Fink + /opt/local # DarwinPorts + /opt/csw # Blastwave + /opt +) + +FIND_LIBRARY(LUA_LIBRARY + NAMES luajit-5.1 lua51 + HINTS + $ENV{LUA_DIR} + PATH_SUFFIXES lib64 lib + PATHS + ~/Library/Frameworks + /Library/Frameworks + /sw + /opt/local + /opt/csw + /opt +) + +IF(LUA_INCLUDE_DIR AND EXISTS "${LUA_INCLUDE_DIR}/luajit.h") + FILE(STRINGS "${LUA_INCLUDE_DIR}/luajit.h" lua_version_str REGEX "^#define[ \t]+LUA_RELEASE[ \t]+\"LuaJIT .+\"") + + STRING(REGEX REPLACE "^#define[ \t]+LUA_RELEASE[ \t]+\"LuaJIT ([^\"]+)\".*" "\\1" LUA_VERSION_STRING "${lua_version_str}") + UNSET(lua_version_str) +ENDIF() + +INCLUDE(FindPackageHandleStandardArgs) +# handle the QUIETLY and REQUIRED arguments and set LUAJIT_FOUND to TRUE if +# all listed variables are TRUE +FIND_PACKAGE_HANDLE_STANDARD_ARGS(LuaJit + REQUIRED_VARS LUA_LIBRARY LUA_INCLUDE_DIR + VERSION_VAR LUA_VERSION_STRING) + +MARK_AS_ADVANCED(LUA_INCLUDE_DIR LUA_LIBRARY LUA_MATH_LIBRARY) diff --git a/config/filesystem.json b/config/filesystem.json index 4917922e0..56aa27585 100644 --- a/config/filesystem.json +++ b/config/filesystem.json @@ -48,6 +48,10 @@ "MODS/": [ {"type" : "dir", "path" : "Mods", "depth": 1} + ], + "SCRIPTS/": + [ + {"type" : "dir", "path" : "scripts"} ] } } diff --git a/config/schemas/mod.json b/config/schemas/mod.json index 63db3e252..e6e18167a 100644 --- a/config/schemas/mod.json +++ b/config/schemas/mod.json @@ -92,6 +92,11 @@ "description": "List of configuration files for objects", "items": { "type":"string", "format" : "textFile" } }, + "scripts": { + "type":"array", + "description": "List of configuration files for scripts", + "items": { "type":"string", "format" : "textFile" } + }, "spells": { "type":"array", "description": "List of configuration files for spells", @@ -106,7 +111,7 @@ "type":"array", "description": "List of configuration files for RMG templates", "items": { "type":"string", "format" : "textFile" } - + }, "changelog" : { diff --git a/config/schemas/script.json b/config/schemas/script.json new file mode 100644 index 000000000..371c1372b --- /dev/null +++ b/config/schemas/script.json @@ -0,0 +1,22 @@ +{ + "type" : "object", + "$schema" : "http://json-schema.org/draft-04/schema", + + "title" : "VCMI script format", + "description" : "Format used to configure script environment", + + + "required" : ["source"], + + "properties" : { + "source" : { + "type": "string", + "description": "Full VFS path to script source (extension required)" + }, + "implements" :{ + "type": "string", + "enum": ["ANYTHING", "BATTLE_EFFECT"], + "description": "" + } + } +} diff --git a/include/vcmi/Artifact.h b/include/vcmi/Artifact.h new file mode 100644 index 000000000..7ffd8431f --- /dev/null +++ b/include/vcmi/Artifact.h @@ -0,0 +1,27 @@ +/* + * Artifact.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 "Entity.h" + +class ArtifactID; +class CreatureID; + +class DLL_LINKAGE Artifact : public EntityWithBonuses +{ +public: + virtual bool isBig() const = 0; + virtual bool isTradable() const = 0; + virtual const std::string & getDescription() const = 0; + virtual const std::string & getEventText() const = 0; + virtual uint32_t getPrice() const = 0; + virtual CreatureID getWarMachine() const = 0; +}; diff --git a/include/vcmi/ArtifactService.h b/include/vcmi/ArtifactService.h new file mode 100644 index 000000000..3dc8588b1 --- /dev/null +++ b/include/vcmi/ArtifactService.h @@ -0,0 +1,21 @@ +/* + * ArtifactService.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 "EntityService.h" + +class ArtifactID; +class Artifact; + +class DLL_LINKAGE ArtifactService : public EntityServiceT +{ +public: +}; diff --git a/include/vcmi/Creature.h b/include/vcmi/Creature.h new file mode 100644 index 000000000..c3c5ba2a2 --- /dev/null +++ b/include/vcmi/Creature.h @@ -0,0 +1,45 @@ +/* + * Creature.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 "Entity.h" + +class CreatureID; + +class DLL_LINKAGE Creature : public EntityWithBonuses +{ +public: + virtual const std::string & getPluralName() const = 0; + virtual const std::string & getSingularName() const = 0; + virtual uint32_t getMaxHealth() const = 0; + + virtual int32_t getAdvMapAmountMin() const = 0; + virtual int32_t getAdvMapAmountMax() const = 0; + virtual int32_t getAIValue() const = 0; + virtual int32_t getFightValue() const = 0; + virtual int32_t getLevel() const = 0; + virtual int32_t getGrowth() const = 0; + virtual int32_t getHorde() const = 0; + virtual int32_t getFactionIndex() const = 0; + + virtual int32_t getBaseAttack() const = 0; + virtual int32_t getBaseDefense() const = 0; + virtual int32_t getBaseDamageMin() const = 0; + virtual int32_t getBaseDamageMax() const = 0; + virtual int32_t getBaseHitPoints() const = 0; + virtual int32_t getBaseSpellPoints() const = 0; + virtual int32_t getBaseSpeed() const = 0; + virtual int32_t getBaseShots() const = 0; + + virtual int32_t getCost(int32_t resIndex) const = 0; + + virtual bool isDoubleWide() const = 0; +}; diff --git a/include/vcmi/CreatureService.h b/include/vcmi/CreatureService.h new file mode 100644 index 000000000..2e3ad04e0 --- /dev/null +++ b/include/vcmi/CreatureService.h @@ -0,0 +1,21 @@ +/* + * CreatureService.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 "EntityService.h" + +class CreatureID; +class Creature; + +class DLL_LINKAGE CreatureService : public EntityServiceT +{ +public: +}; diff --git a/include/vcmi/Entity.h b/include/vcmi/Entity.h new file mode 100644 index 000000000..877cb3372 --- /dev/null +++ b/include/vcmi/Entity.h @@ -0,0 +1,42 @@ +/* + * Entity.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 + +class IBonusBearer; + +class DLL_LINKAGE Entity +{ +public: + using IconRegistar = std::function; + + virtual ~Entity() = default; + + virtual int32_t getIndex() const = 0; + virtual int32_t getIconIndex() const = 0; + virtual const std::string & getJsonKey() const = 0; + virtual const std::string & getName() const = 0; + + virtual void registerIcons(const IconRegistar & cb) const = 0; +}; + +template +class DLL_LINKAGE EntityT : public Entity +{ +public: + virtual IdType getId() const = 0; +}; + +template +class DLL_LINKAGE EntityWithBonuses : public EntityT +{ +public: + virtual const IBonusBearer * accessBonuses() const = 0; +}; diff --git a/include/vcmi/EntityService.h b/include/vcmi/EntityService.h new file mode 100644 index 000000000..1b818131e --- /dev/null +++ b/include/vcmi/EntityService.h @@ -0,0 +1,32 @@ +/* + * EntityService.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 + +class Entity; + +class DLL_LINKAGE EntityService +{ +public: + virtual ~EntityService() = default; + + virtual const Entity * getBaseByIndex(const int32_t index) const = 0; + virtual void forEachBase(const std::function & cb) const = 0; +}; + +template +class DLL_LINKAGE EntityServiceT : public EntityService +{ +public: + virtual const EntityType * getById(const IdType & id) const = 0; + virtual const EntityType * getByIndex(const int32_t index) const = 0; + + virtual void forEach(const std::function & cb) const = 0; +}; diff --git a/include/vcmi/Environment.h b/include/vcmi/Environment.h new file mode 100644 index 000000000..0fa63aa89 --- /dev/null +++ b/include/vcmi/Environment.h @@ -0,0 +1,36 @@ +/* + * Environment.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 + +class Services; + +class IGameInfoCallback; +class IBattleInfoCallback; + +namespace events +{ + class EventBus; +} + +class DLL_LINKAGE Environment +{ +public: + using BattleCb = ::IBattleInfoCallback; + using GameCb = ::IGameInfoCallback; + + virtual ~Environment() = default; + + virtual const Services * services() const = 0; + virtual const BattleCb * battle() const = 0; + virtual const GameCb * game() const = 0; + virtual vstd::CLoggerBase * logger() const = 0; + virtual events::EventBus * eventBus() const = 0; +}; diff --git a/include/vcmi/Faction.h b/include/vcmi/Faction.h new file mode 100644 index 000000000..5fc52c2fb --- /dev/null +++ b/include/vcmi/Faction.h @@ -0,0 +1,21 @@ +/* + * Faction.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 "Entity.h" + +class FactionID; + +class DLL_LINKAGE Faction : public EntityT +{ +public: + virtual bool hasTown() const = 0; +}; diff --git a/include/vcmi/FactionService.h b/include/vcmi/FactionService.h new file mode 100644 index 000000000..884d77868 --- /dev/null +++ b/include/vcmi/FactionService.h @@ -0,0 +1,21 @@ +/* + * FactionService.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 "EntityService.h" + +class FactionID; +class Faction; + +class DLL_LINKAGE FactionService : public EntityServiceT +{ +public: +}; diff --git a/include/vcmi/HeroClass.h b/include/vcmi/HeroClass.h new file mode 100644 index 000000000..713672003 --- /dev/null +++ b/include/vcmi/HeroClass.h @@ -0,0 +1,22 @@ +/* + * HeroClass.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 "Entity.h" + +class HeroClassID; + +class DLL_LINKAGE HeroClass : public EntityT +{ +public: + +}; + diff --git a/include/vcmi/HeroClassService.h b/include/vcmi/HeroClassService.h new file mode 100644 index 000000000..9fb85c00b --- /dev/null +++ b/include/vcmi/HeroClassService.h @@ -0,0 +1,21 @@ +/* + * HeroClassService.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 "EntityService.h" + +class HeroClassID; +class HeroClass; + +class DLL_LINKAGE HeroClassService : public EntityServiceT +{ +public: +}; diff --git a/include/vcmi/HeroType.h b/include/vcmi/HeroType.h new file mode 100644 index 000000000..1c29d111a --- /dev/null +++ b/include/vcmi/HeroType.h @@ -0,0 +1,22 @@ +/* + * HeroType.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 "Entity.h" + +class HeroTypeID; + +class DLL_LINKAGE HeroType : public EntityT +{ +public: + +}; + diff --git a/include/vcmi/HeroTypeService.h b/include/vcmi/HeroTypeService.h new file mode 100644 index 000000000..81f4305bb --- /dev/null +++ b/include/vcmi/HeroTypeService.h @@ -0,0 +1,21 @@ +/* + * HeroTypeService.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 "EntityService.h" + +class HeroTypeID; +class HeroType; + +class DLL_LINKAGE HeroTypeService : public EntityServiceT +{ +public: +}; diff --git a/include/vcmi/Metatype.h b/include/vcmi/Metatype.h new file mode 100644 index 000000000..e8101f4c8 --- /dev/null +++ b/include/vcmi/Metatype.h @@ -0,0 +1,30 @@ +/* + * Metatype.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 + +enum class Metatype : uint32_t +{ + UNKNOWN = 0, + ARTIFACT, + ARTIFACT_INSTANCE, + CREATURE, + CREATURE_INSTANCE, + FACTION, + HERO_CLASS, + HERO_TYPE, + HERO_INSTANCE, + MAP_OBJECT_GROUP, + MAP_OBJECT_TYPE, + MAP_OBJECT_INSTANCE, + SKILL, + SPELL +}; + diff --git a/include/vcmi/Player.h b/include/vcmi/Player.h new file mode 100644 index 000000000..ac79e94a1 --- /dev/null +++ b/include/vcmi/Player.h @@ -0,0 +1,30 @@ +/* + * Player.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 + +class PlayerColor; +class TeamID; +class IBonusBearer; + +class DLL_LINKAGE Player +{ +public: + virtual PlayerColor getColor() const = 0; + virtual TeamID getTeam() const = 0; + virtual bool isHuman() const = 0; + virtual const IBonusBearer * accessBonuses() const = 0; + virtual int getResourceAmount(int type) const = 0; +}; + + + + + diff --git a/include/vcmi/ServerCallback.h b/include/vcmi/ServerCallback.h new file mode 100644 index 000000000..a9a722d4d --- /dev/null +++ b/include/vcmi/ServerCallback.h @@ -0,0 +1,44 @@ +/* + * ServerCallback.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 + +namespace vstd +{ + class RNG; +} + +struct CPackForClient; +struct BattleLogMessage; +struct BattleStackMoved; +struct BattleUnitsChanged; +struct SetStackEffect; +struct StacksInjured; +struct BattleObstaclesChanged; +struct CatapultAttack; + +class DLL_LINKAGE ServerCallback +{ +public: + virtual void complain(const std::string & problem) = 0; + virtual bool describeChanges() const = 0; + + virtual vstd::RNG * getRNG() = 0; + + virtual void apply(CPackForClient * pack) = 0; + + virtual void apply(BattleLogMessage * pack) = 0; + virtual void apply(BattleStackMoved * pack) = 0; + virtual void apply(BattleUnitsChanged * pack) = 0; + virtual void apply(SetStackEffect * pack) = 0; + virtual void apply(StacksInjured * pack) = 0; + virtual void apply(BattleObstaclesChanged * pack) = 0; + virtual void apply(CatapultAttack * pack) = 0; +}; diff --git a/include/vcmi/Services.h b/include/vcmi/Services.h new file mode 100644 index 000000000..46c2e58a9 --- /dev/null +++ b/include/vcmi/Services.h @@ -0,0 +1,57 @@ +/* + * Services.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 "Metatype.h" + +class ArtifactService; +class CreatureService; +class FactionService; +class HeroClassService; +class HeroTypeService; +class SkillService; +class JsonNode; + +namespace spells +{ + class Service; + + namespace effects + { + class Registry; + } +} + +namespace scripting +{ + class Service; +} + +class DLL_LINKAGE Services +{ +public: + virtual ~Services() = default; + + virtual const ArtifactService * artifacts() const = 0; + virtual const CreatureService * creatures() const = 0; + virtual const FactionService * factions() const = 0; + virtual const HeroClassService * heroClasses() const = 0; + virtual const HeroTypeService * heroTypes() const = 0; + virtual const scripting::Service * scripts() const = 0; + virtual const spells::Service * spells() const = 0; + virtual const SkillService * skills() const = 0; + + virtual void updateEntity(Metatype metatype, int32_t index, const JsonNode & data) = 0; + + virtual const spells::effects::Registry * spellEffects() const = 0; + virtual spells::effects::Registry * spellEffects() = 0; + //TODO: put map object types registry access here +}; diff --git a/include/vcmi/Skill.h b/include/vcmi/Skill.h new file mode 100644 index 000000000..14b308486 --- /dev/null +++ b/include/vcmi/Skill.h @@ -0,0 +1,22 @@ +/* + * Skill.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 "Entity.h" + +class SecondarySkill; + +class DLL_LINKAGE Skill : public EntityT +{ +public: + +}; + diff --git a/include/vcmi/SkillService.h b/include/vcmi/SkillService.h new file mode 100644 index 000000000..47a9bbce9 --- /dev/null +++ b/include/vcmi/SkillService.h @@ -0,0 +1,21 @@ +/* + * SkillService.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 "EntityService.h" + +class SecondarySkill; +class Skill; + +class DLL_LINKAGE SkillService : public EntityServiceT +{ +public: +}; diff --git a/include/vcmi/Team.h b/include/vcmi/Team.h new file mode 100644 index 000000000..6db3b1569 --- /dev/null +++ b/include/vcmi/Team.h @@ -0,0 +1,15 @@ +/* + * Team.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 + + + + diff --git a/include/vcmi/events/AdventureEvents.h b/include/vcmi/events/AdventureEvents.h new file mode 100644 index 000000000..f69d34af8 --- /dev/null +++ b/include/vcmi/events/AdventureEvents.h @@ -0,0 +1,13 @@ +/* + * AdventureEvents.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 "ObjectVisitEnded.h" +#include "ObjectVisitStarted.h" diff --git a/include/vcmi/events/ApplyDamage.h b/include/vcmi/events/ApplyDamage.h new file mode 100644 index 000000000..c92e80aeb --- /dev/null +++ b/include/vcmi/events/ApplyDamage.h @@ -0,0 +1,45 @@ +/* + * ApplyDamage.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 "Event.h" +#include "SubscriptionRegistry.h" + +struct BattleStackAttacked; + +namespace battle +{ + class Unit; +} + +namespace events +{ + +class DLL_LINKAGE ApplyDamage : public Event +{ +public: + using Sub = SubscriptionRegistry; + + using PreHandler = Sub::PreHandler; + using PostHandler = Sub::PostHandler; + using ExecHandler = Sub::ExecHandler; + + static Sub * getRegistry(); + + virtual int64_t getInitalDamage() const = 0; + virtual int64_t getDamage() const = 0; + virtual void setDamage(int64_t value) = 0; + virtual const battle::Unit * getTarget() const = 0; + + friend class SubscriptionRegistry; +}; + +} diff --git a/include/vcmi/events/BattleEvents.h b/include/vcmi/events/BattleEvents.h new file mode 100644 index 000000000..07c561f84 --- /dev/null +++ b/include/vcmi/events/BattleEvents.h @@ -0,0 +1,13 @@ +/* + * BattleEvents.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 "ApplyDamage.h" diff --git a/include/vcmi/events/Event.h b/include/vcmi/events/Event.h new file mode 100644 index 000000000..3f92a30bc --- /dev/null +++ b/include/vcmi/events/Event.h @@ -0,0 +1,29 @@ +/* + * Event.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 + +namespace events +{ + +class EventBus; + +template +class SubscriptionRegistry; + +class DLL_LINKAGE Event +{ +public: + virtual bool isEnabled() const = 0; + +}; + +} + diff --git a/include/vcmi/events/EventBus.h b/include/vcmi/events/EventBus.h new file mode 100644 index 000000000..e77f53370 --- /dev/null +++ b/include/vcmi/events/EventBus.h @@ -0,0 +1,44 @@ +/* + * EventBus.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 "SubscriptionRegistry.h" + +class Environment; + +namespace events +{ + +class DLL_LINKAGE EventBus : public boost::noncopyable +{ +public: + template + std::unique_ptr subscribeBefore(typename E::PreHandler && cb) + { + auto registry = E::getRegistry(); + return registry->subscribeBefore(this, std::move(cb)); + } + + template + std::unique_ptr subscribeAfter(typename E::PostHandler && cb) + { + auto registry = E::getRegistry(); + return registry->subscribeAfter(this, std::move(cb)); + } + + template + void executeEvent(E & event, const typename E::ExecHandler & execHandler = typename E::ExecHandler()) const + { + auto registry = E::getRegistry(); + registry->executeEvent(this, event, execHandler); + } +}; +} diff --git a/include/vcmi/events/GameResumed.h b/include/vcmi/events/GameResumed.h new file mode 100644 index 000000000..33a8436f1 --- /dev/null +++ b/include/vcmi/events/GameResumed.h @@ -0,0 +1,35 @@ +/* + * GameResumed.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 "Event.h" +#include "SubscriptionRegistry.h" + +namespace events +{ + +class DLL_LINKAGE GameResumed : public Event +{ +public: + using Sub = SubscriptionRegistry; + + using PreHandler = Sub::PreHandler; + using PostHandler = Sub::PostHandler; + using ExecHandler = Sub::ExecHandler; + + static Sub * getRegistry(); + + static void defaultExecute(const EventBus * bus); + + friend class SubscriptionRegistry; +}; + +} diff --git a/include/vcmi/events/GenericEvents.h b/include/vcmi/events/GenericEvents.h new file mode 100644 index 000000000..f8e15a3a9 --- /dev/null +++ b/include/vcmi/events/GenericEvents.h @@ -0,0 +1,15 @@ +/* + * GenericEvents.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 "GameResumed.h" +#include "PlayerGotTurn.h" +#include "TurnStarted.h" diff --git a/include/vcmi/events/ObjectVisitEnded.h b/include/vcmi/events/ObjectVisitEnded.h new file mode 100644 index 000000000..b8508e260 --- /dev/null +++ b/include/vcmi/events/ObjectVisitEnded.h @@ -0,0 +1,40 @@ +/* + * ObjectVisitEnded.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 "Event.h" +#include "SubscriptionRegistry.h" + +class PlayerColor; +class ObjectInstanceID; + +namespace events +{ + +class DLL_LINKAGE ObjectVisitEnded : public Event +{ +public: + using Sub = SubscriptionRegistry; + using PreHandler = Sub::PreHandler; + using PostHandler = Sub::PostHandler; + using ExecHandler = Sub::ExecHandler; + + virtual PlayerColor getPlayer() const = 0; + virtual ObjectInstanceID getHero() const = 0; + + static Sub * getRegistry(); + static void defaultExecute(const EventBus * bus, const ExecHandler & execHandler, + const PlayerColor & player, const ObjectInstanceID & heroId); + + friend class SubscriptionRegistry; +}; + +} diff --git a/include/vcmi/events/ObjectVisitStarted.h b/include/vcmi/events/ObjectVisitStarted.h new file mode 100644 index 000000000..8e8577ca0 --- /dev/null +++ b/include/vcmi/events/ObjectVisitStarted.h @@ -0,0 +1,43 @@ +/* + * ObjectVisitStarted.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 "Event.h" +#include "SubscriptionRegistry.h" + +class PlayerColor; +class ObjectInstanceID; + +namespace events +{ + +class DLL_LINKAGE ObjectVisitStarted : public Event +{ +public: + using Sub = SubscriptionRegistry; + using PreHandler = Sub::PreHandler; + using PostHandler = Sub::PostHandler; + using ExecHandler = Sub::ExecHandler; + + virtual PlayerColor getPlayer() const = 0; + virtual ObjectInstanceID getHero() const = 0; + virtual ObjectInstanceID getObject() const = 0; + + virtual void setEnabled(bool enable) = 0; + + static Sub * getRegistry(); + static void defaultExecute(const EventBus * bus, const ExecHandler & execHandler, + const PlayerColor & player, const ObjectInstanceID & heroId, const ObjectInstanceID & objId); + + friend class SubscriptionRegistry; +}; + +} diff --git a/include/vcmi/events/PlayerGotTurn.h b/include/vcmi/events/PlayerGotTurn.h new file mode 100644 index 000000000..4b6fa009b --- /dev/null +++ b/include/vcmi/events/PlayerGotTurn.h @@ -0,0 +1,41 @@ +/* + * PlayerGotTurn.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 "Event.h" +#include "SubscriptionRegistry.h" + +class PlayerColor; + +namespace events +{ + +class DLL_LINKAGE PlayerGotTurn : public Event +{ +public: + using Sub = SubscriptionRegistry; + using PreHandler = Sub::PreHandler; + using PostHandler = Sub::PostHandler; + using ExecHandler = Sub::ExecHandler; + + static Sub * getRegistry(); + static void defaultExecute(const EventBus * bus, const ExecHandler & execHandler, PlayerColor & player); + + virtual PlayerColor getPlayer() const = 0; + virtual void setPlayer(const PlayerColor & value) = 0; + + virtual int32_t getPlayerIndex() const = 0; + virtual void setPlayerIndex(int32_t value) = 0; + + friend class SubscriptionRegistry; +}; + +} diff --git a/include/vcmi/events/SubscriptionRegistry.h b/include/vcmi/events/SubscriptionRegistry.h new file mode 100644 index 000000000..f508207a5 --- /dev/null +++ b/include/vcmi/events/SubscriptionRegistry.h @@ -0,0 +1,166 @@ +/* + * SubscriptionRegistry.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 + +class Environment; + +namespace events +{ + +class EventBus; + +class DLL_LINKAGE EventSubscription : public boost::noncopyable +{ +public: + virtual ~EventSubscription() = default; +}; + +template +class SubscriptionRegistry : public boost::noncopyable +{ +public: + using PreHandler = std::function; + using ExecHandler = std::function; + using PostHandler = std::function; + using BusTag = const void *; + + std::unique_ptr subscribeBefore(BusTag tag, PreHandler && cb) + { + boost::unique_lock lock(mutex); + + auto storage = std::make_shared(std::move(cb)); + preHandlers[tag].push_back(storage); + return make_unique(tag, storage); + } + + std::unique_ptr subscribeAfter(BusTag tag, PostHandler && cb) + { + boost::unique_lock lock(mutex); + + auto storage = std::make_shared(std::move(cb)); + postHandlers[tag].push_back(storage); + return make_unique(tag, storage); + } + + void executeEvent(const EventBus * bus, E & event, const ExecHandler & execHandler) + { + boost::shared_lock lock(mutex); + { + auto it = preHandlers.find(bus); + + if(it != std::end(preHandlers)) + { + for(auto & h : it->second) + (*h)(event); + } + } + + if(event.isEnabled()) + { + if(execHandler) + execHandler(event); + + auto it = postHandlers.find(bus); + + if(it != std::end(postHandlers)) + { + for(auto & h : it->second) + (*h)(event); + } + } + } + +private: + + template + class HandlerStorage + { + public: + explicit HandlerStorage(T && cb_) + : cb(cb_) + { + } + + STRONG_INLINE + void operator()(E & event) + { + cb(event); + } + private: + T cb; + }; + + using PreHandlerStorage = HandlerStorage; + using PostHandlerStorage = HandlerStorage; + + class PreSubscription : public EventSubscription + { + public: + PreSubscription(BusTag tag_, std::shared_ptr cb_) + : cb(cb_), + tag(tag_) + { + } + + virtual ~PreSubscription() + { + auto registry = E::getRegistry(); + registry->unsubscribe(tag, cb, registry->preHandlers); + } + private: + BusTag tag; + std::shared_ptr cb; + }; + + class PostSubscription : public EventSubscription + { + public: + PostSubscription(BusTag tag_, std::shared_ptr cb_) + : cb(cb_), + tag(tag_) + { + } + + virtual ~PostSubscription() + { + auto registry = E::getRegistry(); + registry->unsubscribe(tag, cb, registry->postHandlers); + } + private: + BusTag tag; + std::shared_ptr cb; + }; + + boost::shared_mutex mutex; + + std::map>> preHandlers; + std::map>> postHandlers; + + template + void unsubscribe(BusTag tag, T what, std::map> & from) + { + boost::unique_lock lock(mutex); + + auto it = from.find(tag); + + if(it != std::end(from)) + { + it->second -= what; + + if(it->second.empty()) + { + from.erase(tag); + } + } + + } +}; +} diff --git a/include/vcmi/events/TurnStarted.h b/include/vcmi/events/TurnStarted.h new file mode 100644 index 000000000..1caccddf6 --- /dev/null +++ b/include/vcmi/events/TurnStarted.h @@ -0,0 +1,33 @@ +/* + * TurnStarted.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 "Event.h" +#include "SubscriptionRegistry.h" + +namespace events +{ + +class DLL_LINKAGE TurnStarted : public Event +{ +public: + using Sub = SubscriptionRegistry; + using PreHandler = Sub::PreHandler; + using PostHandler = Sub::PostHandler; + using ExecHandler = Sub::ExecHandler; + + static Sub * getRegistry(); + static void defaultExecute(const EventBus * bus); + + friend class SubscriptionRegistry; +}; + +} diff --git a/include/vcmi/scripting/Service.h b/include/vcmi/scripting/Service.h new file mode 100644 index 000000000..d09882dfa --- /dev/null +++ b/include/vcmi/scripting/Service.h @@ -0,0 +1,80 @@ +/* + * scripting/Service.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 + +class Services; +class JsonNode; +class ServerCallback; + +namespace scripting +{ + +using BattleCb = ::Environment::BattleCb; +using GameCb = ::Environment::GameCb; + +class DLL_LINKAGE Context +{ +public: + virtual ~Context() = default; + + virtual void run(const JsonNode & initialState) = 0; + virtual void run(ServerCallback * server, const JsonNode & initialState) = 0; + + virtual JsonNode callGlobal(const std::string & name, const JsonNode & parameters) = 0; + virtual JsonNode callGlobal(ServerCallback * server, const std::string & name, const JsonNode & parameters) = 0; + + virtual void setGlobal(const std::string & name, int value) = 0; + virtual void setGlobal(const std::string & name, const std::string & value) = 0; + virtual void setGlobal(const std::string & name, double value) = 0; + virtual void setGlobal(const std::string & name, const JsonNode & value) = 0; + + virtual void getGlobal(const std::string & name, int & value) = 0; + virtual void getGlobal(const std::string & name, std::string & value) = 0; + virtual void getGlobal(const std::string & name, double & value) = 0; + virtual void getGlobal(const std::string & name, JsonNode & value) = 0; + + virtual JsonNode saveState() = 0; +}; + +class DLL_LINKAGE Script +{ +public: + virtual ~Script() = default; + + virtual const std::string & getName() const = 0; + virtual const std::string & getSource() const = 0; + + virtual std::shared_ptr createContext(const Environment * env) const = 0; +}; + +class DLL_LINKAGE Pool +{ +public: + virtual ~Pool() = default; + + virtual void serializeState(const bool saving, JsonNode & data) = 0; + + virtual std::shared_ptr getContext(const Script * script) = 0; +}; + +class DLL_LINKAGE Service +{ +public: + virtual ~Service() = default; + + virtual void performRegistration(Services * services) const = 0; + virtual void run(std::shared_ptr pool) const = 0; +}; + + +} diff --git a/include/vcmi/spells/Caster.h b/include/vcmi/spells/Caster.h new file mode 100644 index 000000000..5ed5ca475 --- /dev/null +++ b/include/vcmi/spells/Caster.h @@ -0,0 +1,68 @@ +/* + * Caster.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 + +class PlayerColor; +struct MetaString; +class ServerCallback; + +namespace battle +{ + class Unit; +} + +namespace spells +{ + +class Spell; + +class DLL_LINKAGE Caster +{ +public: + virtual ~Caster() = default; + + virtual int32_t getCasterUnitId() const = 0; + + /// returns level on which given spell would be cast by this(0 - none, 1 - basic etc); + /// caster may not know this spell at all + /// optionally returns number of selected school by arg - 0 - air magic, 1 - fire magic, 2 - water magic, 3 - earth magic + virtual int32_t getSpellSchoolLevel(const Spell * spell, int32_t * outSelectedSchool = nullptr) const = 0; + + ///default spell school level for effect calculation + virtual int32_t getEffectLevel(const Spell * spell) const = 0; + + ///applying sorcery secondary skill etc + virtual int64_t getSpellBonus(const Spell * spell, int64_t base, const battle::Unit * affectedStack) const = 0; + + ///only bonus for particular spell + virtual int64_t getSpecificSpellBonus(const Spell * spell, int64_t base) const = 0; + + ///default spell-power for damage/heal calculation + virtual int32_t getEffectPower(const Spell * spell) const = 0; + + ///default spell-power for timed effects duration + virtual int32_t getEnchantPower(const Spell * spell) const = 0; + + ///damage/heal override(ignores spell configuration, effect level and effect power) + virtual int64_t getEffectValue(const Spell * spell) const = 0; + + virtual PlayerColor getCasterOwner() const = 0; + + ///only name substitution + virtual void getCasterName(MetaString & text) const = 0; + + ///full default text + virtual void getCastDescription(const Spell * spell, const std::vector & attacked, MetaString & text) const = 0; + + virtual void spendMana(ServerCallback * server, const int32_t spellCost) const = 0; +}; + +} diff --git a/include/vcmi/spells/Magic.h b/include/vcmi/spells/Magic.h new file mode 100644 index 000000000..65d7f5ef2 --- /dev/null +++ b/include/vcmi/spells/Magic.h @@ -0,0 +1,69 @@ +/* + * spells/Magic.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 + +struct MetaString; + +namespace battle +{ + class Unit; + class Destination; +} + +namespace spells +{ +class Caster; +class Spell; +class Mechanics; +class BattleCast; +using Destination = ::battle::Destination; + +using Target = std::vector; + +enum class Mode +{ + //ACTIVE, //todo: use + HERO, //deprecated + MAGIC_MIRROR, + CREATURE_ACTIVE, //deprecated + ENCHANTER, + SPELL_LIKE_ATTACK, + PASSIVE//f.e. opening battle spells +}; + +enum class AimType +{ + NO_TARGET, + CREATURE, + OBSTACLE, + LOCATION +}; + +class DLL_LINKAGE Problem +{ +public: + typedef int Severity; + + enum ESeverity + { + LOWEST = std::numeric_limits::min(), + NORMAL = 0, + CRITICAL = std::numeric_limits::max() + }; + + virtual ~Problem() = default; + + virtual void add(MetaString && description, Severity severity = CRITICAL) = 0; + + virtual void getAll(std::vector & target) const = 0; +}; + +} diff --git a/include/vcmi/spells/Service.h b/include/vcmi/spells/Service.h new file mode 100644 index 000000000..badd9487a --- /dev/null +++ b/include/vcmi/spells/Service.h @@ -0,0 +1,26 @@ +/* + * spells/Service.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 "../EntityService.h" + +class SpellID; + +namespace spells +{ +class Spell; + +class DLL_LINKAGE Service : public EntityServiceT +{ +public: +}; + +} diff --git a/include/vcmi/spells/Spell.h b/include/vcmi/spells/Spell.h new file mode 100644 index 000000000..e64fb11a4 --- /dev/null +++ b/include/vcmi/spells/Spell.h @@ -0,0 +1,56 @@ +/* + * spells/Spell.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 "../Entity.h" + +class SpellID; + +namespace spells +{ +struct SchoolInfo; +class Caster; + +class DLL_LINKAGE Spell: public EntityT +{ +public: + using SchoolCallback = std::function; + + ///calculate spell damage on stack taking caster`s secondary skills into account + virtual int64_t calculateDamage(const Caster * caster) const = 0; + + virtual int32_t getLevel() const = 0; + virtual boost::logic::tribool getPositiveness() const = 0; + virtual bool isAdventure() const = 0; + virtual bool isCombat() const = 0; + virtual bool isCreatureAbility() const = 0; + virtual bool isPositive() const = 0; + virtual bool isNegative() const = 0; + virtual bool isNeutral() const = 0; + + virtual bool isDamage() const = 0; + virtual bool isOffensive() const = 0; + virtual bool isSpecial() const = 0; + + virtual void forEachSchool(const SchoolCallback & cb) const = 0; + virtual const std::string & getCastSound() const = 0; + virtual int32_t getCost(const int32_t skillLevel) const = 0; + + virtual int32_t getBasePower() const = 0; + /** + * Returns spell level power, base power ignored + */ + virtual int32_t getLevelPower(const int32_t skillLevel) const = 0; + virtual const std::string & getLevelDescription(const int32_t skillLevel) const = 0; + +}; + +} diff --git a/include/vstd/StringUtils.h b/include/vstd/StringUtils.h index d679a8704..9b90fdbe3 100644 --- a/include/vstd/StringUtils.h +++ b/include/vstd/StringUtils.h @@ -4,5 +4,6 @@ namespace vstd { DLL_LINKAGE std::vector split(std::string s, std::string separators); + DLL_LINKAGE std::pair splitStringToPair(std::string input, char separator); } diff --git a/lib/CArtHandler.cpp b/lib/CArtHandler.cpp index 6c7160148..3ea4364be 100644 --- a/lib/CArtHandler.cpp +++ b/lib/CArtHandler.cpp @@ -48,20 +48,61 @@ ART_POS(SHOULDERS) \ ART_POS(HEAD) -const std::string & CArtifact::Name() const +int32_t CArtifact::getIndex() const +{ + return id.toEnum(); +} + +int32_t CArtifact::getIconIndex() const +{ + return iconIndex; +} + +const std::string & CArtifact::getName() const { return name; } -const std::string & CArtifact::Description() const +const std::string & CArtifact::getJsonKey() const +{ + return identifier; +} + +void CArtifact::registerIcons(const IconRegistar & cb) const +{ + cb(getIconIndex(), "ARTIFACT", image); + cb(getIconIndex(), "ARTIFACTLARGE", large); +} + +ArtifactID CArtifact::getId() const +{ + return id; +} + +const IBonusBearer * CArtifact::accessBonuses() const +{ + return this; +} + +const std::string & CArtifact::getDescription() const { return description; } -const std::string & CArtifact::EventText() const +const std::string & CArtifact::getEventText() const { return eventText; } +uint32_t CArtifact::getPrice() const +{ + return price; +} + +CreatureID CArtifact::getWarMachine() const +{ + return warMachine; +} + bool CArtifact::isBig() const { return warMachine != CreatureID::NONE; @@ -117,7 +158,7 @@ int CArtifact::getArtClassSerial() const std::string CArtifact::nodeName() const { - return "Artifact: " + Name(); + return "Artifact: " + getName(); } void CArtifact::addNewBonus(const std::shared_ptr& b) @@ -128,6 +169,16 @@ void CArtifact::addNewBonus(const std::shared_ptr& b) CBonusSystemNode::addNewBonus(b); } +void CArtifact::updateFrom(const JsonNode& data) +{ + //TODO:CArtifact::updateFrom +} + +void CArtifact::serializeJson(JsonSerializeFormat & handler) +{ + +} + void CArtifact::fillWarMachine() { switch(id) @@ -178,15 +229,11 @@ CArtHandler::CArtHandler() { } -CArtHandler::~CArtHandler() -{ - for(CArtifact * art : artifacts) - delete art; -} +CArtHandler::~CArtHandler() = default; std::vector CArtHandler::loadLegacyData(size_t dataSize) { - artifacts.resize(dataSize); + objects.resize(dataSize); std::vector h3Data; h3Data.reserve(dataSize); @@ -231,82 +278,48 @@ std::vector CArtHandler::loadLegacyData(size_t dataSize) void CArtHandler::loadObject(std::string scope, std::string name, const JsonNode & data) { - auto object = loadFromJson(data, normalizeIdentifier(scope, "core", name)); - object->id = ArtifactID((si32)artifacts.size()); - object->iconIndex = object->id + 5; + auto object = loadFromJson(scope, data, normalizeIdentifier(scope, "core", name), objects.size()); - artifacts.push_back(object); + object->iconIndex = object->getIndex() + 5; - VLC->modh->identifiers.requestIdentifier(scope, "object", "artifact", [=](si32 index) - { - JsonNode conf; - conf.setMeta(scope); - - VLC->objtypeh->loadSubObject(object->identifier, conf, Obj::ARTIFACT, object->id.num); - - if (!object->advMapDef.empty()) - { - JsonNode templ; - templ.setMeta(scope); - templ["animation"].String() = object->advMapDef; - - // add new template. - // Necessary for objects added via mods that don't have any templates in H3 - VLC->objtypeh->getHandlerFor(Obj::ARTIFACT, object->id)->addTemplate(templ); - } - // object does not have any templates - this is not usable object (e.g. pseudo-art like lock) - if (VLC->objtypeh->getHandlerFor(Obj::ARTIFACT, object->id)->getTemplates().empty()) - VLC->objtypeh->removeSubObject(Obj::ARTIFACT, object->id); - }); + objects.push_back(object); registerObject(scope, "artifact", name, object->id); } void CArtHandler::loadObject(std::string scope, std::string name, const JsonNode & data, size_t index) { - auto object = loadFromJson(data, normalizeIdentifier(scope, "core", name)); - object->id = ArtifactID((si32)index); - object->iconIndex = object->id; + auto object = loadFromJson(scope, data, normalizeIdentifier(scope, "core", name), index); - assert(artifacts[index] == nullptr); // ensure that this id was not loaded before - artifacts[index] = object; + object->iconIndex = object->getIndex(); - VLC->modh->identifiers.requestIdentifier(scope, "object", "artifact", [=](si32 index) - { - JsonNode conf; - conf.setMeta(scope); + assert(objects[index] == nullptr); // ensure that this id was not loaded before + objects[index] = object; - VLC->objtypeh->loadSubObject(object->identifier, conf, Obj::ARTIFACT, object->id.num); - - if (!object->advMapDef.empty()) - { - JsonNode templ; - templ.setMeta(scope); - templ["animation"].String() = object->advMapDef; - - // add new template. - // Necessary for objects added via mods that don't have any templates in H3 - VLC->objtypeh->getHandlerFor(Obj::ARTIFACT, object->id)->addTemplate(templ); - } - // object does not have any templates - this is not usable object (e.g. pseudo-art like lock) - if (VLC->objtypeh->getHandlerFor(Obj::ARTIFACT, object->id)->getTemplates().empty()) - VLC->objtypeh->removeSubObject(Obj::ARTIFACT, object->id); - }); registerObject(scope, "artifact", name, object->id); } -CArtifact * CArtHandler::loadFromJson(const JsonNode & node, const std::string & identifier) +const std::vector & CArtHandler::getTypeNames() const +{ + static const std::vector typeNames = { "artifact" }; + return typeNames; +} + +CArtifact * CArtHandler::loadFromJson(const std::string & scope, const JsonNode & node, const std::string & identifier, size_t index) { CArtifact * art; - if (!VLC->modh->modules.COMMANDERS || node["growing"].isNull()) + if(!VLC->modh->modules.COMMANDERS || node["growing"].isNull()) + { art = new CArtifact(); + } else { auto growing = new CGrowingArtifact(); loadGrowingArt(growing, node); art = growing; } + art->id = ArtifactID(index); art->identifier = identifier; const JsonNode & text = node["text"]; art->name = text["name"].String(); @@ -316,7 +329,7 @@ CArtifact * CArtHandler::loadFromJson(const JsonNode & node, const std::string & const JsonNode & graphics = node["graphics"]; art->image = graphics["image"].String(); - if (!graphics["large"].isNull()) + if(!graphics["large"].isNull()) art->large = graphics["large"].String(); else art->large = art->image; @@ -330,7 +343,7 @@ CArtifact * CArtHandler::loadFromJson(const JsonNode & node, const std::string & loadType(art, node); loadComponents(art, node); - for (auto b : node["bonuses"].Vector()) + for(auto b : node["bonuses"].Vector()) { auto bonus = JsonUtils::parseBonus(b); art->addNewBonus(bonus); @@ -344,10 +357,32 @@ CArtifact * CArtHandler::loadFromJson(const JsonNode & node, const std::string & art->warMachine = CreatureID(id); //this assumes that creature object is stored before registration - VLC->creh->creatures.at(id)->warMachine = art->id; + VLC->creh->objects.at(id)->warMachine = art->id; }); } + VLC->modh->identifiers.requestIdentifier(scope, "object", "artifact", [=](si32 index) + { + JsonNode conf; + conf.setMeta(scope); + + VLC->objtypeh->loadSubObject(art->getJsonKey(), conf, Obj::ARTIFACT, art->getIndex()); + + if(!art->advMapDef.empty()) + { + JsonNode templ; + templ.setMeta(scope); + templ["animation"].String() = art->advMapDef; + + // add new template. + // Necessary for objects added via mods that don't have any templates in H3 + VLC->objtypeh->getHandlerFor(Obj::ARTIFACT, art->getIndex())->addTemplate(templ); + } + // object does not have any templates - this is not usable object (e.g. pseudo-art like lock) + if(VLC->objtypeh->getHandlerFor(Obj::ARTIFACT, art->getIndex())->getTemplates().empty()) + VLC->objtypeh->removeSubObject(Obj::ARTIFACT, art->getIndex()); + }); + return art; } @@ -470,8 +505,8 @@ void CArtHandler::loadComponents(CArtifact * art, const JsonNode & node) { // when this code is called both combinational art as well as component are loaded // so it is safe to access any of them - art->constituents->push_back(VLC->arth->artifacts[id]); - VLC->arth->artifacts[id]->constituentOf.push_back(art); + art->constituents->push_back(objects[id]); + objects[id]->constituentOf.push_back(art); }); } } @@ -528,7 +563,7 @@ ArtifactID CArtHandler::pickRandomArtifact(CRandomGenerator & rand, int flags, s if (!out.size()) //no arts are available at all { out.resize (64); - std::fill_n (out.begin(), 64, artifacts[2]); //Give Grail - this can't be banned (hopefully) + std::fill_n (out.begin(), 64, objects[2]); //Give Grail - this can't be banned (hopefully) } }; @@ -549,44 +584,6 @@ ArtifactID CArtHandler::pickRandomArtifact(CRandomGenerator & rand, int flags) return pickRandomArtifact(rand, flags, [](ArtifactID){ return true;}); } -std::shared_ptr createBonus(Bonus::BonusType type, int val, int subtype, Bonus::ValueType valType, std::shared_ptr limiter = std::shared_ptr(), int additionalInfo = 0) -{ - auto added = std::make_shared(Bonus::PERMANENT,type,Bonus::ARTIFACT,val,-1,subtype); - added->additionalInfo = additionalInfo; - added->valType = valType; - added->limiter = limiter; - return added; -} - -std::shared_ptr createBonus(Bonus::BonusType type, int val, int subtype, std::shared_ptr propagator = std::shared_ptr(), int additionalInfo = 0) -{ - auto added = std::make_shared(Bonus::PERMANENT,type,Bonus::ARTIFACT,val,-1,subtype); - added->additionalInfo = additionalInfo; - added->valType = Bonus::BASE_NUMBER; - added->propagator = propagator; - return added; -} - -void CArtHandler::giveArtBonus( ArtifactID aid, Bonus::BonusType type, int val, int subtype, Bonus::ValueType valType, std::shared_ptr limiter, int additionalInfo) -{ - giveArtBonus(aid, createBonus(type, val, subtype, valType, limiter, additionalInfo)); -} - -void CArtHandler::giveArtBonus(ArtifactID aid, Bonus::BonusType type, int val, int subtype, std::shared_ptr propagator, int additionalInfo) -{ - giveArtBonus(aid, createBonus(type, val, subtype, propagator, additionalInfo)); -} - -void CArtHandler::giveArtBonus(ArtifactID aid, std::shared_ptr bonus) -{ - bonus->sid = aid; - if(bonus->subtype == Bonus::MORALE || bonus->type == Bonus::LUCK) - bonus->description = artifacts[aid]->Name() + (bonus->val > 0 ? " +" : " ") + boost::lexical_cast(bonus->val); - else - bonus->description = artifacts[aid]->Name(); - - artifacts[aid]->addNewBonus(bonus); -} void CArtHandler::makeItCreatureArt(CArtifact * a, bool onlyCreature) { if (onlyCreature) @@ -597,12 +594,6 @@ void CArtHandler::makeItCreatureArt(CArtifact * a, bool onlyCreature) a->possibleSlots[ArtBearer::CREATURE].push_back(ArtifactPosition::CREATURE_SLOT); } -void CArtHandler::makeItCreatureArt(ArtifactID aid, bool onlyCreature) -{ - CArtifact *a = artifacts[aid]; - makeItCreatureArt (a, onlyCreature); -} - void CArtHandler::makeItCommanderArt(CArtifact * a, bool onlyCommander) { if (onlyCommander) @@ -614,15 +605,9 @@ void CArtHandler::makeItCommanderArt(CArtifact * a, bool onlyCommander) a->possibleSlots[ArtBearer::COMMANDER].push_back(ArtifactPosition(i)); } -void CArtHandler::makeItCommanderArt(ArtifactID aid, bool onlyCommander) -{ - CArtifact *a = artifacts[aid]; - makeItCommanderArt (a, onlyCommander); -} - bool CArtHandler::legalArtifact(ArtifactID id) { - auto art = artifacts[id]; + auto art = objects[id]; //assert ( (!art->constituents) || art->constituents->size() ); //artifacts is not combined or has some components return ((art->possibleSlots[ArtBearer::HERO].size() || (art->possibleSlots[ArtBearer::COMMANDER].size() && VLC->modh->modules.COMMANDERS) || @@ -645,12 +630,12 @@ void CArtHandler::initAllowedArtifactsList(const std::vector &allowed) //check artifacts allowed on a map //TODO: This line will be different when custom map format is implemented if (allowed[i] && legalArtifact(i)) - allowedArtifacts.push_back(artifacts[i]); + allowedArtifacts.push_back(objects[i]); } - for (ArtifactID i = ArtifactID::ART_SELECTION; i CArtHandler::getDefaultAllowed() const std::vector allowedArtifacts; allowedArtifacts.resize(127, true); allowedArtifacts.resize(141, false); - allowedArtifacts.resize(artifacts.size(), true); + allowedArtifacts.resize(size(), true); return allowedArtifacts; } void CArtHandler::erasePickedArt(ArtifactID id) { - CArtifact *art = artifacts[id]; + CArtifact *art = objects[id]; if(auto artifactList = listFromClass(art->aClass)) { @@ -679,11 +664,11 @@ void CArtHandler::erasePickedArt(ArtifactID id) artifactList->erase(itr); } else - logMod->warn("Problem: cannot erase artifact %s from list, it was not present", art->Name()); + logMod->warn("Problem: cannot erase artifact %s from list, it was not present", art->getName()); } else - logMod->warn("Problem: cannot find list for artifact %s, strange class. (special?)", art->Name()); + logMod->warn("Problem: cannot find list for artifact %s, strange class. (special?)", art->getName()); } boost::optional&> CArtHandler::listFromClass( CArtifact::EartClass artifactClass ) @@ -716,11 +701,11 @@ void CArtHandler::fillList( std::vector &listToBeFilled, CArtifact:: void CArtHandler::afterLoadFinalization() { //All artifacts have their id, so we can properly update their bonuses' source ids. - for(auto &art : artifacts) + for(auto &art : objects) { for(auto &bonus : art->getExportedBonusList()) { - assert(art == artifacts[art->id]); + assert(art == objects[art->id]); assert(bonus->source == Bonus::ARTIFACT); bonus->sid = art->id; } @@ -747,17 +732,12 @@ void CArtifactInstance::setType( CArtifact *Art ) std::string CArtifactInstance::nodeName() const { - return "Artifact instance of " + (artType ? artType->Name() : std::string("uninitialized")) + " type"; -} - -CArtifactInstance * CArtifactInstance::createScroll( const CSpell *s) -{ - return createScroll(s->id); + return "Artifact instance of " + (artType ? artType->getName() : std::string("uninitialized")) + " type"; } CArtifactInstance *CArtifactInstance::createScroll(SpellID sid) { - auto ret = new CArtifactInstance(VLC->arth->artifacts[ArtifactID::SPELL_SCROLL]); + auto ret = new CArtifactInstance(VLC->arth->objects[ArtifactID::SPELL_SCROLL]); auto b = std::make_shared(Bonus::PERMANENT, Bonus::SPELL, Bonus::ARTIFACT_INSTANCE, -1, ArtifactID::SPELL_SCROLL, sid); ret->addNewBonus(b); return ret; @@ -770,19 +750,18 @@ void CArtifactInstance::init() setNodeType(ARTIFACT_INSTANCE); } -std::string CArtifactInstance::getEffectiveDescription( - const CGHeroInstance *hero) const +std::string CArtifactInstance::getEffectiveDescription(const CGHeroInstance * hero) const { - std::string text = artType->Description(); + std::string text = artType->getDescription(); if (!vstd::contains(text, '{')) - text = '{' + artType->Name() + "}\n\n" + text; //workaround for new artifacts with single name, turns it to H3-style + text = '{' + artType->getName() + "}\n\n" + text; //workaround for new artifacts with single name, turns it to H3-style if(artType->id == ArtifactID::SPELL_SCROLL) { // we expect scroll description to be like this: This scroll contains the [spell name] spell which is added into your spell book for as long as you carry the scroll. // so we want to replace text in [...] with a spell name // however other language versions don't have name placeholder at all, so we have to be careful - int spellID = getGivenSpellID(); + int32_t spellID = getGivenSpellID(); size_t nameStart = text.find_first_of('['); size_t nameEnd = text.find_first_of(']', nameStart); if(spellID >= 0) @@ -791,16 +770,16 @@ std::string CArtifactInstance::getEffectiveDescription( text = text.replace(nameStart, nameEnd - nameStart + 1, VLC->spellh->objects[spellID]->name); } } - else if (hero && artType->constituentOf.size()) //display info about set + else if(hero && artType->constituentOf.size()) //display info about set { std::string artList; auto combinedArt = artType->constituentOf[0]; text += "\n\n"; - text += "{" + combinedArt->Name() + "}"; + text += "{" + combinedArt->getName() + "}"; int wornArtifacts = 0; - for (auto a : *combinedArt->constituents) //TODO: can the artifact be a part of more than one set? + for(auto a : *combinedArt->constituents) //TODO: can the artifact be a part of more than one set? { - artList += "\n" + a->Name(); + artList += "\n" + a->getName(); if (hero->hasArt(a->id, true)) wornArtifacts++; } @@ -855,7 +834,7 @@ bool CArtifactInstance::canBePutAt(const CArtifactSet *artSet, ArtifactPosition auto possibleSlots = artType->possibleSlots.find(artSet->bearerType()); if(possibleSlots == artType->possibleSlots.end()) { - logMod->warn("Warning: artifact %s doesn't have defined allowed slots for bearer of type %s", artType->Name(), artSet->bearerType()); + logMod->warn("Warning: artifact %s doesn't have defined allowed slots for bearer of type %s", artType->getName(), artSet->bearerType()); return false; } @@ -946,7 +925,7 @@ CArtifactInstance * CArtifactInstance::createNewArtifactInstance(CArtifact *Art) CArtifactInstance * CArtifactInstance::createNewArtifactInstance(int aid) { - return createNewArtifactInstance(VLC->arth->artifacts[aid]); + return createNewArtifactInstance(VLC->arth->objects[aid]); } CArtifactInstance * CArtifactInstance::createArtifact(CMap * map, int aid, int spellID) @@ -960,7 +939,7 @@ CArtifactInstance * CArtifactInstance::createArtifact(CMap * map, int aid, int s } else { - a = CArtifactInstance::createScroll(SpellID(spellID).toSpell()); + a = CArtifactInstance::createScroll(SpellID(spellID)); } } else //FIXME: create combined artifact instance for random combined artifacts, just in case @@ -1046,7 +1025,7 @@ bool CCombinedArtifactInstance::canBeDisassembled() const } CCombinedArtifactInstance::CCombinedArtifactInstance(CArtifact *Art) - : CArtifactInstance(Art) //TODO: seems unued, but need to be written + : CArtifactInstance(Art) //TODO: seems unused, but need to be written { } diff --git a/lib/CArtHandler.h b/lib/CArtHandler.h index ed472044e..0dc2bb6b0 100644 --- a/lib/CArtHandler.h +++ b/lib/CArtHandler.h @@ -9,9 +9,10 @@ */ #pragma once -#include "../lib/HeroBonus.h" -//#include "../lib/ConstTransitivePtr.h" -//#include "JsonNode.h" +#include +#include + +#include "HeroBonus.h" #include "GameConstants.h" #include "IHandlerBase.h" @@ -40,7 +41,7 @@ namespace ArtBearer }; } -class DLL_LINKAGE CArtifact : public CBonusSystemNode //container for artifacts +class DLL_LINKAGE CArtifact : public Artifact, public CBonusSystemNode //container for artifacts { protected: std::string name, description; //set if custom @@ -50,7 +51,7 @@ public: std::string identifier; std::string image; - std::string large; // big image for cutom artifacts, used in drag & drop + std::string large; // big image for custom artifacts, used in drag & drop std::string advMapDef; //used for adventure map object si32 iconIndex; ui32 price; @@ -61,12 +62,20 @@ public: ArtifactID id; CreatureID warMachine; - const std::string &Name() const; //getter - const std::string &Description() const; //getter - const std::string &EventText() const; + int32_t getIndex() const override; + int32_t getIconIndex() const override; + const std::string & getName() const override; + const std::string & getJsonKey() const override; + void registerIcons(const IconRegistar & cb) const override; + ArtifactID getId() const override; + virtual const IBonusBearer * accessBonuses() const override; - bool isBig () const; - bool isTradable () const; + const std::string & getEventText() const override; + const std::string & getDescription() const override; + uint32_t getPrice() const override; + CreatureID getWarMachine() const override; + bool isBig() const override; + bool isTradable() const override; int getArtClassSerial() const; //0 - treasure, 1 - minor, 2 - major, 3 - relic, 4 - spell scroll, 5 - other std::string nodeName() const override; @@ -74,6 +83,9 @@ public: virtual void levelUpArtifact (CArtifactInstance * art){}; + void updateFrom(const JsonNode & data); + void serializeJson(JsonSerializeFormat & handler); + template void serialize(Handler &h, const int version) { h & static_cast(*this); @@ -171,7 +183,6 @@ public: BONUS_TREE_DESERIALIZATION_FIX } - static CArtifactInstance *createScroll(const CSpell *s); static CArtifactInstance *createScroll(SpellID sid); static CArtifactInstance *createNewArtifactInstance(CArtifact *Art); static CArtifactInstance *createNewArtifactInstance(int aid); @@ -230,18 +241,17 @@ public: } }; -class DLL_LINKAGE CArtHandler : public IHandlerBase //handles artifacts +class DLL_LINKAGE CArtHandler : public CHandlerBase { public: std::vector treasures, minors, majors, relics; //tmp vectors!!! do not touch if you don't know what you are doing!!! - std::vector< ConstTransitivePtr > artifacts; std::vector allowedArtifacts; std::set growingArtifacts; void addBonuses(CArtifact *art, const JsonNode &bonusList); - void fillList(std::vector &listToBeFilled, CArtifact::EartClass artifactClass); //fills given empty list with allowed artifacts of gibven class. No side effects + void fillList(std::vector &listToBeFilled, CArtifact::EartClass artifactClass); //fills given empty list with allowed artifacts of given class. No side effects boost::optional&> listFromClass(CArtifact::EartClass artifactClass); @@ -256,9 +266,7 @@ public: bool legalArtifact(ArtifactID id); void initAllowedArtifactsList(const std::vector &allowed); //allowed[art_id] -> 0 if not allowed, 1 if allowed void makeItCreatureArt (CArtifact * a, bool onlyCreature = true); - void makeItCreatureArt (ArtifactID aid, bool onlyCreature = true); void makeItCommanderArt (CArtifact * a, bool onlyCommander = true); - void makeItCommanderArt (ArtifactID aid, bool onlyCommander = true); CArtHandler(); ~CArtHandler(); @@ -273,7 +281,7 @@ public: template void serialize(Handler &h, const int version) { - h & artifacts; + h & objects; h & allowedArtifacts; h & treasures; h & minors; @@ -282,8 +290,11 @@ public: h & growingArtifacts; } +protected: + const std::vector & getTypeNames() const override; + CArtifact * loadFromJson(const std::string & scope, const JsonNode & json, const std::string & identifier, size_t index) override; + private: - CArtifact * loadFromJson(const JsonNode & node, const std::string & identifier); void addSlot(CArtifact * art, const std::string & slotID); void loadSlots(CArtifact * art, const JsonNode & node); @@ -292,10 +303,6 @@ private: void loadComponents(CArtifact * art, const JsonNode & node); void loadGrowingArt(CGrowingArtifact * art, const JsonNode & node); - void giveArtBonus(ArtifactID aid, Bonus::BonusType type, int val, int subtype = -1, Bonus::ValueType valType = Bonus::BASE_NUMBER, std::shared_ptr limiter = std::shared_ptr(), int additionalinfo = 0); - void giveArtBonus(ArtifactID aid, Bonus::BonusType type, int val, int subtype, std::shared_ptr propagator, int additionalinfo = 0); - void giveArtBonus(ArtifactID aid, std::shared_ptr bonus); - void erasePickedArt(ArtifactID id); }; diff --git a/lib/CCreatureHandler.cpp b/lib/CCreatureHandler.cpp index 3b4c330d0..8ea7f8461 100644 --- a/lib/CCreatureHandler.cpp +++ b/lib/CCreatureHandler.cpp @@ -17,9 +17,157 @@ #include "CTownHandler.h" #include "CModHandler.h" #include "StringConstants.h" - +#include "serializer/JsonDeserializer.h" +#include "serializer/JsonUpdater.h" #include "mapObjects/CObjectClassesHandler.h" +int32_t CCreature::getIndex() const +{ + return idNumber.toEnum(); +} + +int32_t CCreature::getIconIndex() const +{ + return iconIndex; +} + +const std::string & CCreature::getName() const +{ + return nameSing;//??? +} + +const std::string & CCreature::getJsonKey() const +{ + return identifier; +} + +void CCreature::registerIcons(const IconRegistar & cb) const +{ + cb(getIconIndex(), "CPRSMALL", smallIconName); + cb(getIconIndex(), "TWCRPORT", largeIconName); +} + +CreatureID CCreature::getId() const +{ + return idNumber; +} + +const IBonusBearer * CCreature::accessBonuses() const +{ + return this; +} + +uint32_t CCreature::getMaxHealth() const +{ + return CBonusSystemNode::MaxHealth(); +} + +const std::string & CCreature::getPluralName() const +{ + return namePl; +} + +const std::string & CCreature::getSingularName() const +{ + return nameSing; +} + +int32_t CCreature::getAdvMapAmountMin() const +{ + return ammMin; +} + +int32_t CCreature::getAdvMapAmountMax() const +{ + return ammMax; +} + +int32_t CCreature::getAIValue() const +{ + return AIValue; +} + +int32_t CCreature::getFightValue() const +{ + return fightValue; +} + +int32_t CCreature::getLevel() const +{ + return level; +} + +int32_t CCreature::getGrowth() const +{ + return growth; +} + +int32_t CCreature::getHorde() const +{ + return hordeGrowth; +} + +int32_t CCreature::getFactionIndex() const +{ + return faction; +} + +int32_t CCreature::getBaseAttack() const +{ + static const auto SELECTOR = Selector::typeSubtype(Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK).And(Selector::sourceTypeSel(Bonus::CREATURE_ABILITY)); + return getExportedBonusList().valOfBonuses(SELECTOR); +} + +int32_t CCreature::getBaseDefense() const +{ + static const auto SELECTOR = Selector::typeSubtype(Bonus::PRIMARY_SKILL, PrimarySkill::DEFENSE).And(Selector::sourceTypeSel(Bonus::CREATURE_ABILITY)); + return getExportedBonusList().valOfBonuses(SELECTOR); +} + +int32_t CCreature::getBaseDamageMin() const +{ + static const auto SELECTOR = Selector::typeSubtype(Bonus::CREATURE_DAMAGE, 1).And(Selector::sourceTypeSel(Bonus::CREATURE_ABILITY)); + return getExportedBonusList().valOfBonuses(SELECTOR); +} + +int32_t CCreature::getBaseDamageMax() const +{ + static const auto SELECTOR = Selector::typeSubtype(Bonus::CREATURE_DAMAGE, 2).And(Selector::sourceTypeSel(Bonus::CREATURE_ABILITY)); + return getExportedBonusList().valOfBonuses(SELECTOR); +} + +int32_t CCreature::getBaseHitPoints() const +{ + static const auto SELECTOR = Selector::type()(Bonus::STACK_HEALTH).And(Selector::sourceTypeSel(Bonus::CREATURE_ABILITY)); + return getExportedBonusList().valOfBonuses(SELECTOR); +} + +int32_t CCreature::getBaseSpellPoints() const +{ + static const auto SELECTOR = Selector::type()(Bonus::CASTS).And(Selector::sourceTypeSel(Bonus::CREATURE_ABILITY)); + return getExportedBonusList().valOfBonuses(SELECTOR); +} + +int32_t CCreature::getBaseSpeed() const +{ + static const auto SELECTOR = Selector::type()(Bonus::STACKS_SPEED).And(Selector::sourceTypeSel(Bonus::CREATURE_ABILITY)); + return getExportedBonusList().valOfBonuses(SELECTOR); +} + +int32_t CCreature::getBaseShots() const +{ + static const auto SELECTOR = Selector::type()(Bonus::SHOTS).And(Selector::sourceTypeSel(Bonus::CREATURE_ABILITY)); + return getExportedBonusList().valOfBonuses(SELECTOR); +} + +int32_t CCreature::getCost(int32_t resIndex) const +{ + if(resIndex >= 0 && resIndex < cost.size()) + return cost[resIndex]; + else + return 0; +} + int CCreature::getQuantityID(const int & quantity) { if (quantity<5) @@ -59,28 +207,13 @@ bool CCreature::isDoubleWide() const return doubleWide; } -bool CCreature::isFlying() const -{ - return hasBonusOfType(Bonus::FLYING); -} - -bool CCreature::isShooting() const -{ - return hasBonusOfType(Bonus::SHOOTER); -} - -bool CCreature::isUndead() const -{ - return hasBonusOfType(Bonus::UNDEAD); -} - /** * Determines if the creature is of a good alignment. * @return true if the creture is good, false otherwise. */ bool CCreature::isGood () const { - return VLC->townh->factions[faction]->alignment == EAlignment::GOOD; + return (*VLC->townh)[faction]->alignment == EAlignment::GOOD; } /** @@ -89,7 +222,7 @@ bool CCreature::isGood () const */ bool CCreature::isEvil () const { - return VLC->townh->factions[faction]->alignment == EAlignment::EVIL; + return (*VLC->townh)[faction]->alignment == EAlignment::EVIL; } si32 CCreature::maxAmount(const std::vector &res) const //how many creatures can be bought @@ -115,8 +248,22 @@ CCreature::CCreature() void CCreature::addBonus(int val, Bonus::BonusType type, int subtype) { - auto added = std::make_shared(Bonus::PERMANENT, type, Bonus::CREATURE_ABILITY, val, idNumber, subtype, Bonus::BASE_NUMBER); - addNewBonus(added); + auto selector = Selector::typeSubtype(type, subtype).And(Selector::source(Bonus::CREATURE_ABILITY, getIndex())); + BonusList & exported = getExportedBonusList(); + + BonusList existing; + exported.getBonuses(existing, selector, Selector::all); + + if(existing.empty()) + { + auto added = std::make_shared(Bonus::PERMANENT, type, Bonus::CREATURE_ABILITY, val, getIndex(), subtype, Bonus::BASE_NUMBER); + addNewBonus(added); + } + else + { + std::shared_ptr b = existing[0]; + b->val = val; + } } bool CCreature::isMyUpgrade(const CCreature *anotherCre) const @@ -127,7 +274,7 @@ bool CCreature::isMyUpgrade(const CCreature *anotherCre) const bool CCreature::valid() const { - return this == VLC->creh->creatures[idNumber]; + return this == VLC->creh->objects[idNumber]; } std::string CCreature::nodeName() const @@ -147,18 +294,82 @@ ETerrainType::EETerrainType CCreature::getNativeTerrain() const //and in the CGHeroInstance::getNativeTerrain() to setup mevement bonuses or/and penalties. return hasBonusOfType(Bonus::NO_TERRAIN_PENALTY) ? ETerrainType::ANY_TERRAIN - : (ETerrainType::EETerrainType)VLC->townh->factions[faction]->nativeTerrain; + : (ETerrainType::EETerrainType)(*VLC->townh)[faction]->nativeTerrain; } -void CCreature::setId(CreatureID ID) +void CCreature::updateFrom(const JsonNode & data) { - idNumber = ID; - for(auto bonus : getExportedBonusList()) + JsonUpdater handler(nullptr, data); + { - if(bonus->source == Bonus::CREATURE_ABILITY) - bonus->sid = ID; + auto configScope = handler.enterStruct("config"); + + const JsonNode & configNode = handler.getCurrent(); + + serializeJson(handler); + + if(!configNode["hitPoints"].isNull()) + addBonus(configNode["hitPoints"].Integer(), Bonus::STACK_HEALTH); + + if(!configNode["speed"].isNull()) + addBonus(configNode["speed"].Integer(), Bonus::STACKS_SPEED); + + if(!configNode["attack"].isNull()) + addBonus(configNode["attack"].Integer(), Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK); + + if(!configNode["defense"].isNull()) + addBonus(configNode["defense"].Integer(), Bonus::PRIMARY_SKILL, PrimarySkill::DEFENSE); + + if(!configNode["damage"]["min"].isNull()) + addBonus(configNode["damage"]["min"].Integer(), Bonus::CREATURE_DAMAGE, 1); + + if(!configNode["damage"]["max"].isNull()) + addBonus(configNode["damage"]["max"].Integer(), Bonus::CREATURE_DAMAGE, 2); + + if(!configNode["shots"].isNull()) + addBonus(configNode["shots"].Integer(), Bonus::SHOTS); + + if(!configNode["spellPoints"].isNull()) + addBonus(configNode["spellPoints"].Integer(), Bonus::CASTS); + } + + + handler.serializeBonuses("bonuses", this); +} + +void CCreature::serializeJson(JsonSerializeFormat & handler) +{ + { + auto nameNode = handler.enterStruct("name"); + handler.serializeString("singular", nameSing); + handler.serializeString("plural", namePl); + } + + handler.serializeInt("fightValue", fightValue); + handler.serializeInt("aiValue", AIValue); + handler.serializeInt("growth", growth); + handler.serializeInt("horde", hordeGrowth);// Needed at least until configurable buildings + + { + auto advMapNode = handler.enterStruct("advMapAmount"); + handler.serializeInt("min", ammMin); + handler.serializeInt("max", ammMax); + } + + if(handler.updating) + { + cost.serializeJson(handler, "cost"); + handler.serializeInt("faction", faction);//TODO: unify with deferred resolve + } + + handler.serializeInt("level", level); + handler.serializeBool("doubleWide", doubleWide); + + if(!handler.saving) + { + if(ammMin > ammMax) + logMod->error("Invalid creature '%s' configuration, advMapAmount.min > advMapAmount.max", identifier); } - CBonusSystemNode::treeHasChanged(); } void CCreature::fillWarMachine() @@ -181,39 +392,6 @@ void CCreature::fillWarMachine() warMachine = ArtifactID::NONE; //this creature is not artifact } -static void AddAbility(CCreature *cre, const JsonVector &ability_vec) -{ - auto nsf = std::make_shared(); - std::string type = ability_vec[0].String(); - - auto it = bonusNameMap.find(type); - - if (it == bonusNameMap.end()) { - if (type == "DOUBLE_WIDE") - cre->doubleWide = true; - else if (type == "ENEMY_MORALE_DECREASING") { - cre->addBonus(-1, Bonus::MORALE); - cre->getBonusList().back()->effectRange = Bonus::ONLY_ENEMY_ARMY; - } - else if (type == "ENEMY_LUCK_DECREASING") { - cre->addBonus(-1, Bonus::LUCK); - cre->getBonusList().back()->effectRange = Bonus::ONLY_ENEMY_ARMY; - } else - logGlobal->error("Error: invalid ability type %s in creatures config", type); - - return; - } - - nsf->type = it->second; - - JsonUtils::parseTypedBonusShort(ability_vec,nsf); - - nsf->source = Bonus::CREATURE_ABILITY; - nsf->sid = cre->idNumber; - - cre->addNewBonus(nsf); -} - CCreatureHandler::CCreatureHandler() : expAfterUpgrade(0) { @@ -235,7 +413,7 @@ const CCreature * CCreatureHandler::getCreature(const std::string & scope, const if(!index) throw std::runtime_error("Creature not found "+identifier); - return creatures[*index]; + return objects[*index]; } void CCreatureHandler::loadCommanders() @@ -330,7 +508,7 @@ void CCreatureHandler::loadBonuses(JsonNode & creature, std::string bonuses) std::vector CCreatureHandler::loadLegacyData(size_t dataSize) { - creatures.resize(dataSize); + objects.resize(dataSize); std::vector h3Data; h3Data.reserve(dataSize); @@ -406,84 +584,81 @@ std::vector CCreatureHandler::loadLegacyData(size_t dataSize) return h3Data; } -void CCreatureHandler::loadObject(std::string scope, std::string name, const JsonNode & data) +CCreature * CCreatureHandler::loadFromJson(const std::string & scope, const JsonNode & node, const std::string & identifier, size_t index) { - auto object = loadFromJson(data, normalizeIdentifier(scope, "core", name)); - object->setId(CreatureID((si32)creatures.size())); - object->iconIndex = object->idNumber + 2; + auto cre = new CCreature(); - creatures.push_back(object); + if(node["hasDoubleWeek"].Bool()) + { + doubledCreatures.insert(CreatureID(index)); + } + cre->idNumber = CreatureID(index); + cre->iconIndex = cre->getIndex() + 2; + cre->identifier = identifier; + + JsonDeserializer handler(nullptr, node); + cre->serializeJson(handler); + + cre->cost = Res::ResourceSet(node["cost"]); + + cre->addBonus(node["hitPoints"].Integer(), Bonus::STACK_HEALTH); + cre->addBonus(node["speed"].Integer(), Bonus::STACKS_SPEED); + cre->addBonus(node["attack"].Integer(), Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK); + cre->addBonus(node["defense"].Integer(), Bonus::PRIMARY_SKILL, PrimarySkill::DEFENSE); + + cre->addBonus(node["damage"]["min"].Integer(), Bonus::CREATURE_DAMAGE, 1); + cre->addBonus(node["damage"]["max"].Integer(), Bonus::CREATURE_DAMAGE, 2); + + assert(node["damage"]["min"].Integer() <= node["damage"]["max"].Integer()); + + if(!node["shots"].isNull()) + cre->addBonus(node["shots"].Integer(), Bonus::SHOTS); + + if(!node["spellPoints"].isNull()) + cre->addBonus(node["spellPoints"].Integer(), Bonus::CASTS); + + loadStackExperience(cre, node["stackExperience"]); + loadJsonAnimation(cre, node["graphics"]); + loadCreatureJson(cre, node); + + for(auto & extraName : node["extraNames"].Vector()) + { + for(auto type_name : getTypeNames()) + registerObject(scope, type_name, extraName.String(), cre->getIndex()); + } VLC->modh->identifiers.requestIdentifier(scope, "object", "monster", [=](si32 index) { JsonNode conf; conf.setMeta(scope); - VLC->objtypeh->loadSubObject(object->identifier, conf, Obj::MONSTER, object->idNumber.num); - if (!object->advMapDef.empty()) + VLC->objtypeh->loadSubObject(cre->identifier, conf, Obj::MONSTER, cre->idNumber.num); + if (!cre->advMapDef.empty()) { JsonNode templ; - templ["animation"].String() = object->advMapDef; - VLC->objtypeh->getHandlerFor(Obj::MONSTER, object->idNumber.num)->addTemplate(templ); + templ["animation"].String() = cre->advMapDef; + VLC->objtypeh->getHandlerFor(Obj::MONSTER, cre->idNumber.num)->addTemplate(templ); } // object does not have any templates - this is not usable object (e.g. pseudo-creature like Arrow Tower) - if (VLC->objtypeh->getHandlerFor(Obj::MONSTER, object->idNumber.num)->getTemplates().empty()) - VLC->objtypeh->removeSubObject(Obj::MONSTER, object->idNumber.num); + if (VLC->objtypeh->getHandlerFor(Obj::MONSTER, cre->idNumber.num)->getTemplates().empty()) + VLC->objtypeh->removeSubObject(Obj::MONSTER, cre->idNumber.num); }); - registerObject(scope, "creature", name, object->idNumber); - - for(auto node : data["extraNames"].Vector()) - { - registerObject(scope, "creature", node.String(), object->idNumber); - } + return cre; } -void CCreatureHandler::loadObject(std::string scope, std::string name, const JsonNode & data, size_t index) +const std::vector & CCreatureHandler::getTypeNames() const { - auto object = loadFromJson(data, normalizeIdentifier(scope, "core", name)); - object->setId(CreatureID((si32)index)); - object->iconIndex = object->idNumber + 2; - - if(data["hasDoubleWeek"].Bool()) // - { - doubledCreatures.insert (object->idNumber); //we need to have id (or identifier) before it is inserted - } - - assert(creatures[index] == nullptr); // ensure that this id was not loaded before - creatures[index] = object; - - VLC->modh->identifiers.requestIdentifier(scope, "object", "monster", [=](si32 index) - { - JsonNode conf; - conf.setMeta(scope); - - VLC->objtypeh->loadSubObject(object->identifier, conf, Obj::MONSTER, object->idNumber.num); - if (!object->advMapDef.empty()) - { - JsonNode templ; - templ["animation"].String() = object->advMapDef; - VLC->objtypeh->getHandlerFor(Obj::MONSTER, object->idNumber.num)->addTemplate(templ); - } - - // object does not have any templates - this is not usable object (e.g. pseudo-creature like Arrow Tower) - if (VLC->objtypeh->getHandlerFor(Obj::MONSTER, object->idNumber.num)->getTemplates().empty()) - VLC->objtypeh->removeSubObject(Obj::MONSTER, object->idNumber.num); - }); - - registerObject(scope, "creature", name, object->idNumber); - for(auto & node : data["extraNames"].Vector()) - { - registerObject(scope, "creature", node.String(), object->idNumber); - } + static const std::vector typeNames = { "creature" }; + return typeNames; } std::vector CCreatureHandler::getDefaultAllowed() const { std::vector ret; - for(const CCreature * crea : creatures) + for(const CCreature * crea : objects) { ret.push_back(crea ? !crea->special : false); } @@ -546,7 +721,7 @@ void CCreatureHandler::loadCrExpBon() loadStackExp(b, bl, parser); for(auto b : bl) { - creatures[sid]->addNewBonus(b); //add directly to CCreature Node + objects[sid]->addNewBonus(b); //add directly to CCreature Node } } while (parser.endLine()); @@ -591,12 +766,12 @@ void CCreatureHandler::loadCrExpBon() expBonParser.endLine(); } //skeleton gets exp penalty - creatures[56].get()->addBonus(-50, Bonus::EXP_MULTIPLIER, -1); - creatures[57].get()->addBonus(-50, Bonus::EXP_MULTIPLIER, -1); + objects[56].get()->addBonus(-50, Bonus::EXP_MULTIPLIER, -1); + objects[57].get()->addBonus(-50, Bonus::EXP_MULTIPLIER, -1); //exp for tier >7, rank 11 - expRanks[0].push_back(147000); - expAfterUpgrade = 75; //percent - maxExpPerBattle[0] = maxExpPerBattle[7]; + expRanks[0].push_back(147000); + expAfterUpgrade = 75; //percent + maxExpPerBattle[0] = maxExpPerBattle[7]; }//end of Stack Experience } @@ -655,50 +830,6 @@ void CCreatureHandler::loadUnitAnimInfo(JsonNode & graphics, CLegacyConfigParser graphics.Struct().erase("missile"); } -CCreature * CCreatureHandler::loadFromJson(const JsonNode & node, const std::string & identifier) -{ - auto cre = new CCreature(); - - const JsonNode & name = node["name"]; - cre->identifier = identifier; - cre->nameSing = name["singular"].String(); - cre->namePl = name["plural"].String(); - - cre->cost = Res::ResourceSet(node["cost"]); - - cre->fightValue = static_cast(node["fightValue"].Float()); - cre->AIValue = static_cast(node["aiValue"].Float()); - cre->growth = static_cast(node["growth"].Float()); - cre->hordeGrowth = static_cast(node["horde"].Float()); // Needed at least until configurable buildings - - cre->addBonus((int)node["hitPoints"].Float(), Bonus::STACK_HEALTH); - cre->addBonus((int)node["speed"].Float(), Bonus::STACKS_SPEED); - cre->addBonus((int)node["attack"].Float(), Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK); - cre->addBonus((int)node["defense"].Float(), Bonus::PRIMARY_SKILL, PrimarySkill::DEFENSE); - - cre->addBonus((int)node["damage"]["min"].Float(), Bonus::CREATURE_DAMAGE, 1); - cre->addBonus((int)node["damage"]["max"].Float(), Bonus::CREATURE_DAMAGE, 2); - - assert(node["damage"]["min"].Float() <= node["damage"]["max"].Float()); - - cre->ammMin = static_cast(node["advMapAmount"]["min"].Float()); - cre->ammMax = static_cast(node["advMapAmount"]["max"].Float()); - assert(cre->ammMin <= cre->ammMax); - - if (!node["shots"].isNull()) - cre->addBonus((int)node["shots"].Float(), Bonus::SHOTS); - - if (node["spellPoints"].isNull()) - cre->addBonus((int)node["spellPoints"].Float(), Bonus::CASTS); - - cre->doubleWide = node["doubleWide"].Bool(); - - loadStackExperience(cre, node["stackExperience"]); - loadJsonAnimation(cre, node["graphics"]); - loadCreatureJson(cre, node); - return cre; -} - void CCreatureHandler::loadJsonAnimation(CCreature * cre, const JsonNode & graphics) { cre->animation.timeBetweenFidgets = graphics["timeBetweenFidgets"].Float(); @@ -729,7 +860,6 @@ void CCreatureHandler::loadJsonAnimation(CCreature * cre, const JsonNode & graph void CCreatureHandler::loadCreatureJson(CCreature * creature, const JsonNode & config) { - creature->level = static_cast(config["level"].Float()); creature->animDefName = config["graphics"]["animation"].String(); //FIXME: MOD COMPATIBILITY @@ -741,6 +871,7 @@ void CCreatureHandler::loadCreatureJson(CCreature * creature, const JsonNode & c { auto b = JsonUtils::parseBonus(ability.second); b->source = Bonus::CREATURE_ABILITY; + b->sid = creature->getIndex(); b->duration = Bonus::PERMANENT; creature->addNewBonus(b); } @@ -750,15 +881,15 @@ void CCreatureHandler::loadCreatureJson(CCreature * creature, const JsonNode & c { for(const JsonNode &ability : config["abilities"].Vector()) { - if (ability.getType() == JsonNode::JsonType::DATA_VECTOR) + if(ability.getType() == JsonNode::JsonType::DATA_VECTOR) { - assert(0); // should be unused now - AddAbility(creature, ability.Vector()); // used only for H3 creatures + logMod->error("Ignored outdated creature ability format in %s", creature->getJsonKey()); } else { auto b = JsonUtils::parseBonus(ability); b->source = Bonus::CREATURE_ABILITY; + b->sid = creature->getIndex(); b->duration = Bonus::PERMANENT; creature->addNewBonus(b); } @@ -1153,9 +1284,6 @@ int CCreatureHandler::stringToNumber(std::string & s) CCreatureHandler::~CCreatureHandler() { - for(auto & creature : creatures) - creature.dellNull(); - for(auto & p : skillRequirements) p.first = nullptr; } @@ -1167,8 +1295,8 @@ CreatureID CCreatureHandler::pickRandomMonster(CRandomGenerator & rand, int tier { do { - r = (*RandomGeneratorUtil::nextItem(creatures, rand))->idNumber; - } while (VLC->creh->creatures[r] && VLC->creh->creatures[r]->special); // find first "not special" creature + r = (*RandomGeneratorUtil::nextItem(objects, rand))->idNumber; + } while (objects[r] && objects[r]->special); // find first "not special" creature } else { @@ -1217,7 +1345,7 @@ void CCreatureHandler::restoreAllCreaturesNodeType794() void CCreatureHandler::buildBonusTreeForTiers() { - for(CCreature *c : creatures) + for(CCreature * c : objects) { if(vstd::isbetween(c->level, 0, ARRAY_COUNT(creaturesOfLevel))) c->attachTo(&creaturesOfLevel[c->level]); diff --git a/lib/CCreatureHandler.h b/lib/CCreatureHandler.h index 68328c1ab..8472fc1f4 100644 --- a/lib/CCreatureHandler.h +++ b/lib/CCreatureHandler.h @@ -9,6 +9,9 @@ */ #pragma once +#include +#include + #include "HeroBonus.h" #include "ConstTransitivePtr.h" #include "ResourceSet.h" @@ -20,8 +23,9 @@ class CLegacyConfigParser; class CCreatureHandler; class CCreature; +class JsonSerializeFormat; -class DLL_LINKAGE CCreature : public CBonusSystemNode +class DLL_LINKAGE CCreature : public Creature, public CBonusSystemNode { public: std::string identifier; @@ -122,10 +126,38 @@ public: considerBonus = false is called on Battle init and returns already prepared nativeTerrain without Bonus system calling. */ ETerrainType::EETerrainType getNativeTerrain() const; - bool isDoubleWide() const; //returns true if unit is double wide on battlefield - bool isFlying() const; //returns true if it is a flying unit - bool isShooting() const; //returns true if unit can shoot - bool isUndead() const; //returns true if unit is undead + int32_t getIndex() const override; + int32_t getIconIndex() const override; + const std::string & getName() const override; + const std::string & getJsonKey() const override; + void registerIcons(const IconRegistar & cb) const override; + CreatureID getId() const override; + virtual const IBonusBearer * accessBonuses() const override; + const std::string & getPluralName() const override; + const std::string & getSingularName() const override; + uint32_t getMaxHealth() const override; + + int32_t getAdvMapAmountMin() const override; + int32_t getAdvMapAmountMax() const override; + int32_t getAIValue() const override; + int32_t getFightValue() const override; + int32_t getLevel() const override; + int32_t getGrowth() const override; + int32_t getHorde() const override; + int32_t getFactionIndex() const override; + + int32_t getBaseAttack() const override; + int32_t getBaseDefense() const override; + int32_t getBaseDamageMin() const override; + int32_t getBaseDamageMax() const override; + int32_t getBaseHitPoints() const override; + int32_t getBaseSpellPoints() const override; + int32_t getBaseSpeed() const override; + int32_t getBaseShots() const override; + + int32_t getCost(int32_t resIndex) const override; + bool isDoubleWide() const override; //returns true if unit is double wide on battlefield + bool isGood () const; bool isEvil () const; si32 maxAmount(const std::vector &res) const; //how many creatures can be bought @@ -135,7 +167,6 @@ public: bool valid() const; - void setId(CreatureID ID); //assigns idNumber and updates bonuses to reference it void addBonus(int val, Bonus::BonusType type, int subtype = -1); std::string nodeName() const override; @@ -148,6 +179,9 @@ public: return ammMin + (ranGen() % (ammMax - ammMin)); } + void updateFrom(const JsonNode & data); + void serializeJson(JsonSerializeFormat & handler); + template void serialize(Handler &h, const int version) { h & static_cast(*this); @@ -197,21 +231,16 @@ private: void fillWarMachine(); }; -class DLL_LINKAGE CCreatureHandler : public IHandlerBase +class DLL_LINKAGE CCreatureHandler : public CHandlerBase { private: CBonusSystemNode allCreatures; CBonusSystemNode creaturesOfLevel[GameConstants::CREATURES_PER_TOWN + 1];//index 0 is used for creatures of unknown tier or outside <1-7> range - /// load one creature from json config - CCreature * loadFromJson(const JsonNode & node, const std::string & identifier); - void loadJsonAnimation(CCreature * creature, const JsonNode & graphics); void loadStackExperience(CCreature * creature, const JsonNode &input); void loadCreatureJson(CCreature * creature, const JsonNode & config); - /// loading functions - /// adding abilities from ZCRTRAIT.TXT void loadBonuses(JsonNode & creature, std::string bonuses); /// load all creatures from H3 files @@ -228,9 +257,12 @@ private: /// help function for parsing CREXPBON.txt int stringToNumber(std::string & s); +protected: + const std::vector & getTypeNames() const override; + CCreature * loadFromJson(const std::string & scope, const JsonNode & node, const std::string & identifier, size_t index) override; + public: std::set doubledCreatures; //they get double week - std::vector > creatures; //creature ID -> creature info. //stack exp std::vector > expRanks; // stack experience needed for certain rank, index 0 for other tiers (?) @@ -263,16 +295,13 @@ public: std::vector loadLegacyData(size_t dataSize) override; - void loadObject(std::string scope, std::string name, const JsonNode & data) override; - void loadObject(std::string scope, std::string name, const JsonNode & data, size_t index) override; - std::vector getDefaultAllowed() const override; template void serialize(Handler &h, const int version) { //TODO: should be optimized, not all these informations needs to be serialized (same for ccreature) h & doubledCreatures; - h & creatures; + h & objects; h & expRanks; h & maxExpPerBattle; h & expAfterUpgrade; diff --git a/lib/CCreatureSet.cpp b/lib/CCreatureSet.cpp index e1ab7cf31..f6e6e103a 100644 --- a/lib/CCreatureSet.cpp +++ b/lib/CCreatureSet.cpp @@ -64,7 +64,7 @@ bool CCreatureSet::setCreature(SlotID slot, CreatureID type, TQuantity quantity) SlotID CCreatureSet::getSlotFor(CreatureID creature, ui32 slotsAmount) const /*returns -1 if no slot available */ { - return getSlotFor(VLC->creh->creatures[creature], slotsAmount); + return getSlotFor(VLC->creh->objects[creature], slotsAmount); } SlotID CCreatureSet::getSlotFor(const CCreature *c, ui32 slotsAmount) const @@ -159,7 +159,7 @@ void CCreatureSet::sweep() void CCreatureSet::addToSlot(SlotID slot, CreatureID cre, TQuantity count, bool allowMerging) { - const CCreature *c = VLC->creh->creatures[cre]; + const CCreature *c = VLC->creh->objects[cre]; if(!hasStackAtSlot(slot)) { @@ -621,8 +621,8 @@ void CStackInstance::giveStackExp(TExpType exp) void CStackInstance::setType(CreatureID creID) { - if(creID >= 0 && creID < VLC->creh->creatures.size()) - setType(VLC->creh->creatures[creID]); + if(creID >= 0 && creID < VLC->creh->objects.size()) + setType(VLC->creh->objects[creID]); else setType((const CCreature*)nullptr); } @@ -686,7 +686,7 @@ bool CStackInstance::valid(bool allowUnrandomized) const bool isRand = (idRand != -1); if(!isRand) { - return (type && type == VLC->creh->creatures[type->idNumber]); + return (type && type == VLC->creh->objects[type->idNumber]); } else return allowUnrandomized; @@ -868,7 +868,7 @@ CStackBasicDescriptor::CStackBasicDescriptor() } CStackBasicDescriptor::CStackBasicDescriptor(CreatureID id, TQuantity Count) - : type (VLC->creh->creatures[id]), count(Count) + : type (VLC->creh->objects[id]), count(Count) { } @@ -877,6 +877,17 @@ CStackBasicDescriptor::CStackBasicDescriptor(const CCreature *c, TQuantity Count { } +const Creature * CStackBasicDescriptor::getType() const +{ + return type; +} + +TQuantity CStackBasicDescriptor::getCount() const +{ + return count; +} + + void CStackBasicDescriptor::setType(const CCreature * c) { type = c; diff --git a/lib/CCreatureSet.h b/lib/CCreatureSet.h index 065e6241d..69e05b132 100644 --- a/lib/CCreatureSet.h +++ b/lib/CCreatureSet.h @@ -31,6 +31,9 @@ public: CStackBasicDescriptor(const CCreature *c, TQuantity Count); virtual ~CStackBasicDescriptor() = default; + const Creature * getType() const; + TQuantity getCount() const; + virtual void setType(const CCreature * c); template void serialize(Handler &h, const int version) diff --git a/lib/CGameInfoCallback.cpp b/lib/CGameInfoCallback.cpp index d274b383e..3aa31a6f1 100644 --- a/lib/CGameInfoCallback.cpp +++ b/lib/CGameInfoCallback.cpp @@ -35,7 +35,7 @@ PlayerColor CGameInfoCallback::getOwner(ObjectInstanceID heroID) const int CGameInfoCallback::getResource(PlayerColor Player, Res::ERes which) const { - const PlayerState *p = getPlayer(Player); + const PlayerState *p = getPlayerState(Player); ERROR_RET_VAL_IF(!p, "No player info!", -1); ERROR_RET_VAL_IF(p->resources.size() <= which || which < 0, "No such resource!", -1); return p->resources[which]; @@ -46,7 +46,7 @@ const PlayerSettings * CGameInfoCallback::getPlayerSettings(PlayerColor color) c return &gs->scenarioOps->getIthPlayersSettings(color); } -bool CGameInfoCallback::isAllowed( int type, int id ) +bool CGameInfoCallback::isAllowed(int32_t type, int32_t id) const { switch(type) { @@ -61,7 +61,12 @@ bool CGameInfoCallback::isAllowed( int type, int id ) } } -const PlayerState * CGameInfoCallback::getPlayer(PlayerColor color, bool verbose) const +const Player * CGameInfoCallback::getPlayer(PlayerColor color) const +{ + return getPlayerState(color, false); +} + +const PlayerState * CGameInfoCallback::getPlayerState(PlayerColor color, bool verbose) const { //funtion written from scratch since it's accessed A LOT by AI @@ -93,7 +98,7 @@ const CTown * CGameInfoCallback::getNativeTown(PlayerColor color) const { const PlayerSettings *ps = getPlayerSettings(color); ERROR_RET_VAL_IF(!ps, "There is no such player!", nullptr); - return VLC->townh->factions[ps->castle]->town; + return (*VLC->townh)[ps->castle]->town; } const CGObjectInstance * CGameInfoCallback::getObjByQuestIdentifier(int identifier) const @@ -177,7 +182,7 @@ const StartInfo * CGameInfoCallback::getStartInfo(bool beforeRandomization) cons return gs->scenarioOps; } -int CGameInfoCallback::getSpellCost(const CSpell * sp, const CGHeroInstance * caster) const +int32_t CGameInfoCallback::getSpellCost(const spells::Spell * sp, const CGHeroInstance * caster) const { //boost::shared_lock lock(*gs->mx); ERROR_RET_VAL_IF(!canGetFullInfo(caster), "Cannot get info about caster!", -1); @@ -348,7 +353,7 @@ bool CGameInfoCallback::getHeroInfo(const CGObjectInstance * hero, InfoAboutHero int maxAIValue = 0; const CCreature * mostStrong = nullptr; - for(auto creature : VLC->creh->creatures) + for(auto creature : VLC->creh->objects) { if((si16)creature->faction == factionIndex && (int)creature->AIValue > maxAIValue) { @@ -551,7 +556,7 @@ EBuildingState::EBuildingState CGameInfoCallback::canBuildStructure( const CGTow if(ID == BuildingID::CAPITOL) { - const PlayerState *ps = getPlayer(t->tempOwner, false); + const PlayerState *ps = getPlayerState(t->tempOwner, false); if(ps) { for(const CGTownInstance *town : ps->towns) @@ -583,7 +588,7 @@ EBuildingState::EBuildingState CGameInfoCallback::canBuildStructure( const CGTow return EBuildingState::CANT_BUILD_TODAY; //building limit //checking resources - if(!building->resources.canBeAfforded(getPlayer(t->tempOwner)->resources)) + if(!building->resources.canBeAfforded(getPlayerState(t->tempOwner)->resources)) return EBuildingState::NO_RESOURCES; //lack of res return EBuildingState::ALLOWED; @@ -601,7 +606,7 @@ bool CGameInfoCallback::hasAccess(boost::optional playerId) const EPlayerStatus::EStatus CGameInfoCallback::getPlayerStatus(PlayerColor player, bool verbose) const { - const PlayerState *ps = gs->getPlayer(player, verbose); + const PlayerState *ps = gs->getPlayerState(player, verbose); ERROR_VERBOSE_OR_NOT_RET_VAL_IF(!ps, verbose, "No such player!", EPlayerStatus::WRONG); return ps->status; @@ -650,7 +655,7 @@ bool CGameInfoCallback::canGetFullInfo(const CGObjectInstance *obj) const int CGameInfoCallback::getHeroCount( PlayerColor player, bool includeGarrisoned ) const { int ret = 0; - const PlayerState *p = gs->getPlayer(player); + const PlayerState *p = gs->getPlayerState(player); ERROR_RET_VAL_IF(!p, "No such player!", -1); if(includeGarrisoned) @@ -789,7 +794,7 @@ std::vector < const CGDwelling * > CPlayerSpecificInfoCallback::getMyDwellings() { ASSERT_IF_CALLED_WITH_PLAYER std::vector < const CGDwelling * > ret; - for(CGDwelling * dw : gs->getPlayer(*player)->dwellings) + for(CGDwelling * dw : gs->getPlayerState(*player)->dwellings) { ret.push_back(dw); } @@ -799,7 +804,7 @@ std::vector < const CGDwelling * > CPlayerSpecificInfoCallback::getMyDwellings() std::vector CPlayerSpecificInfoCallback::getMyQuests() const { std::vector ret; - for (auto quest : gs->getPlayer(*player)->quests) + for (auto quest : gs->getPlayerState(*player)->quests) { ret.push_back (quest); } @@ -816,7 +821,7 @@ int CPlayerSpecificInfoCallback::howManyHeroes(bool includeGarrisoned) const const CGHeroInstance* CPlayerSpecificInfoCallback::getHeroBySerial(int serialId, bool includeGarrisoned) const { ASSERT_IF_CALLED_WITH_PLAYER - const PlayerState *p = getPlayer(*player); + const PlayerState *p = getPlayerState(*player); ERROR_RET_VAL_IF(!p, "No player info", nullptr); if (!includeGarrisoned) @@ -832,7 +837,7 @@ const CGHeroInstance* CPlayerSpecificInfoCallback::getHeroBySerial(int serialId, const CGTownInstance* CPlayerSpecificInfoCallback::getTownBySerial(int serialId) const { ASSERT_IF_CALLED_WITH_PLAYER - const PlayerState *p = getPlayer(*player); + const PlayerState *p = getPlayerState(*player); ERROR_RET_VAL_IF(!p, "No player info", nullptr); ERROR_RET_VAL_IF(serialId < 0 || serialId >= p->towns.size(), "No player info", nullptr); return p->towns[serialId]; @@ -893,13 +898,14 @@ const TeamState * CGameInfoCallback::getPlayerTeam( PlayerColor color ) const } } -const CGHeroInstance* CGameInfoCallback::getHeroWithSubid( int subid ) const +const CGHeroInstance * CGameInfoCallback::getHeroWithSubid( int subid ) const { - for(const CGHeroInstance *h : gs->map->heroesOnMap) - if(h->subID == subid) - return h; + if(subid<0) + return nullptr; + if(subid>= gs->map->allHeroes.size()) + return nullptr; - return nullptr; + return gs->map->allHeroes.at(subid).get(); } PlayerColor CGameInfoCallback::getLocalPlayer() const @@ -990,25 +996,3 @@ bool CGameInfoCallback::isTeleportEntrancePassable(const CGTeleport * obj, Playe { return obj && obj->isEntrance() && !isTeleportChannelImpassable(obj->channel, player); } - -void IGameEventRealizer::showInfoDialog( InfoWindow *iw ) -{ - commitPackage(iw); -} - -void IGameEventRealizer::showInfoDialog(const std::string &msg, PlayerColor player) -{ - InfoWindow iw; - iw.player = player; - iw.text << msg; - showInfoDialog(&iw); -} - -void IGameEventRealizer::setObjProperty(ObjectInstanceID objid, int prop, si64 val) -{ - SetObjectProperty sob; - sob.id = objid; - sob.what = prop; - sob.val = static_cast(val); - commitPackage(&sob); -} diff --git a/lib/CGameInfoCallback.h b/lib/CGameInfoCallback.h index 2cff4306a..b30b14a57 100644 --- a/lib/CGameInfoCallback.h +++ b/lib/CGameInfoCallback.h @@ -11,9 +11,10 @@ #include "int3.h" #include "ResourceSet.h" // for Res::ERes -#include "battle/CPlayerBattleCallback.h" +#include "battle/CCallbackBase.h" +class Player; +class Team; -class CGObjectInstance; struct InfoWindow; struct PlayerSettings; struct CPackForClient; @@ -21,11 +22,12 @@ struct TerrainTile; struct PlayerState; class CTown; struct StartInfo; + +struct InfoAboutHero; struct InfoAboutTown; + struct UpgradeInfo; struct SThievesGuildInfo; -class CGDwelling; -class CGTeleport; class CMapHeader; struct TeamState; struct QuestInfo; @@ -33,11 +35,98 @@ struct ShashInt3; class CGameState; class PathfinderConfig; +class CArmedInstance; +class CGObjectInstance; +class CGHeroInstance; +class CGDwelling; +class CGTeleport; +class CGTownInstance; -class DLL_LINKAGE CGameInfoCallback : public virtual CCallbackBase +class DLL_LINKAGE IGameInfoCallback +{ +public: + //TODO: all other public methods of CGameInfoCallback + +// //various + virtual int getDate(Date::EDateType mode=Date::DAY) const = 0; //mode=0 - total days in game, mode=1 - day of week, mode=2 - current week, mode=3 - current month +// const StartInfo * getStartInfo(bool beforeRandomization = false)const; + virtual bool isAllowed(int32_t type, int32_t id) const = 0; //type: 0 - spell; 1- artifact; 2 - secondary skill + + //player + virtual const Player * getPlayer(PlayerColor color) const = 0; +// virtual int getResource(PlayerColor Player, Res::ERes which) const = 0; +// bool isVisible(int3 pos) const; +// PlayerRelations::PlayerRelations getPlayerRelations(PlayerColor color1, PlayerColor color2) const; +// void getThievesGuildInfo(SThievesGuildInfo & thi, const CGObjectInstance * obj); //get thieves' guild info obtainable while visiting given object +// EPlayerStatus::EStatus getPlayerStatus(PlayerColor player, bool verbose = true) const; //-1 if no such player +// PlayerColor getCurrentPlayer() const; //player that currently makes move // TODO synchronous turns + virtual PlayerColor getLocalPlayer() const = 0; //player that is currently owning given client (if not a client, then returns current player) +// const PlayerSettings * getPlayerSettings(PlayerColor color) const; + + +// //armed object +// void getUpgradeInfo(const CArmedInstance *obj, SlotID stackPos, UpgradeInfo &out)const; + + //hero + virtual const CGHeroInstance * getHero(ObjectInstanceID objid) const = 0; + virtual const CGHeroInstance * getHeroWithSubid(int subid) const = 0; +// int getHeroCount(PlayerColor player, bool includeGarrisoned) const; +// bool getHeroInfo(const CGObjectInstance * hero, InfoAboutHero & dest, const CGObjectInstance * selectedObject = nullptr) const; +// int32_t getSpellCost(const spells::Spell * sp, const CGHeroInstance * caster) const; //when called during battle, takes into account creatures' spell cost reduction +// int64_t estimateSpellDamage(const CSpell * sp, const CGHeroInstance * hero) const; //estimates damage of given spell; returns 0 if spell causes no dmg +// const CArtifactInstance * getArtInstance(ArtifactInstanceID aid) const; +// const CGObjectInstance * getObjInstance(ObjectInstanceID oid) const; +// const CGObjectInstance * getArmyInstance(ObjectInstanceID oid) const; + + //objects + virtual const CGObjectInstance * getObj(ObjectInstanceID objid, bool verbose = true) const = 0; +// std::vector getBlockingObjs(int3 pos) const; + virtual std::vector getVisitableObjs(int3 pos, bool verbose = true) const = 0; +// std::vector getFlaggableObjects(int3 pos) const; +// const CGObjectInstance * getTopObj (int3 pos) const; +// PlayerColor getOwner(ObjectInstanceID heroID) const; +// const CGObjectInstance *getObjByQuestIdentifier(int identifier) const; //nullptr if object has been removed (eg. killed) + + //map +// int3 guardingCreaturePosition (int3 pos) const; +// std::vector getGuardingCreatures (int3 pos) const; +// const CMapHeader * getMapHeader()const; +// int3 getMapSize() const; //returns size of map - z is 1 for one - level map and 2 for two level map +// const TerrainTile * getTile(int3 tile, bool verbose = true) const; +// std::shared_ptr> getAllVisibleTiles() const; +// bool isInTheMap(const int3 &pos) const; +// void getVisibleTilesInRange(std::unordered_set &tiles, int3 pos, int radious, int3::EDistanceFormula distanceFormula = int3::DIST_2D) const; + + //town +// const CGTownInstance* getTown(ObjectInstanceID objid) const; +// int howManyTowns(PlayerColor Player) const; +// const CGTownInstance * getTownInfo(int val, bool mode)const; //mode = 0 -> val = player town serial; mode = 1 -> val = object id (serial) +// std::vector getAvailableHeroes(const CGObjectInstance * townOrTavern) const; //heroes that can be recruited +// std::string getTavernRumor(const CGObjectInstance * townOrTavern) const; +// EBuildingState::EBuildingState canBuildStructure(const CGTownInstance *t, BuildingID ID);//// 0 - no more than one capitol, 1 - lack of water, 2 - forbidden, 3 - Add another level to Mage Guild, 4 - already built, 5 - cannot build, 6 - cannot afford, 7 - build, 8 - lack of requirements +// virtual bool getTownInfo(const CGObjectInstance * town, InfoAboutTown & dest, const CGObjectInstance * selectedObject = nullptr) const; +// const CTown *getNativeTown(PlayerColor color) const; + + //from gs +// const TeamState *getTeam(TeamID teamID) const; +// const TeamState *getPlayerTeam(PlayerColor color) const; +// EBuildingState::EBuildingState canBuildStructure(const CGTownInstance *t, BuildingID ID) const;// 0 - no more than one capitol, 1 - lack of water, 2 - forbidden, 3 - Add another level to Mage Guild, 4 - already built, 5 - cannot build, 6 - cannot afford, 7 - build, 8 - lack of requirements + + //teleport +// std::vector getVisibleTeleportObjects(std::vector ids, PlayerColor player) const; +// std::vector getTeleportChannelEntraces(TeleportChannelID id, PlayerColor Player = PlayerColor::UNFLAGGABLE) const; +// std::vector getTeleportChannelExits(TeleportChannelID id, PlayerColor Player = PlayerColor::UNFLAGGABLE) const; +// ETeleportChannelType getTeleportChannelType(TeleportChannelID id, PlayerColor player = PlayerColor::UNFLAGGABLE) const; +// bool isTeleportChannelImpassable(TeleportChannelID id, PlayerColor player = PlayerColor::UNFLAGGABLE) const; +// bool isTeleportChannelBidirectional(TeleportChannelID id, PlayerColor player = PlayerColor::UNFLAGGABLE) const; +// bool isTeleportChannelUnidirectional(TeleportChannelID id, PlayerColor player = PlayerColor::UNFLAGGABLE) const; +// bool isTeleportEntrancePassable(const CGTeleport * obj, PlayerColor player) const; +}; + +class DLL_LINKAGE CGameInfoCallback : public virtual CCallbackBase, public IGameInfoCallback { protected: - CGameState * gs; + CGameState * gs;//todo: replace with protected const getter, only actual Server and Client objects should hold game state CGameInfoCallback(); CGameInfoCallback(CGameState *GS, boost::optional Player); @@ -51,40 +140,40 @@ protected: public: //various - virtual int getDate(Date::EDateType mode=Date::DAY)const; //mode=0 - total days in game, mode=1 - day of week, mode=2 - current week, mode=3 - current month + int getDate(Date::EDateType mode=Date::DAY)const override; //mode=0 - total days in game, mode=1 - day of week, mode=2 - current week, mode=3 - current month virtual const StartInfo * getStartInfo(bool beforeRandomization = false)const; - virtual bool isAllowed(int type, int id); //type: 0 - spell; 1- artifact; 2 - secondary skill + bool isAllowed(int32_t type, int32_t id) const override; //type: 0 - spell; 1- artifact; 2 - secondary skill //player - virtual const PlayerState * getPlayer(PlayerColor color, bool verbose = true) const; + const Player * getPlayer(PlayerColor color) const override; + virtual const PlayerState * getPlayerState(PlayerColor color, bool verbose = true) const; virtual int getResource(PlayerColor Player, Res::ERes which) const; virtual bool isVisible(int3 pos) const; virtual PlayerRelations::PlayerRelations getPlayerRelations(PlayerColor color1, PlayerColor color2) const; virtual void getThievesGuildInfo(SThievesGuildInfo & thi, const CGObjectInstance * obj); //get thieves' guild info obtainable while visiting given object virtual EPlayerStatus::EStatus getPlayerStatus(PlayerColor player, bool verbose = true) const; //-1 if no such player virtual PlayerColor getCurrentPlayer() const; //player that currently makes move // TODO synchronous turns - virtual PlayerColor getLocalPlayer() const; //player that is currently owning given client (if not a client, then returns current player) + PlayerColor getLocalPlayer() const override; //player that is currently owning given client (if not a client, then returns current player) virtual const PlayerSettings * getPlayerSettings(PlayerColor color) const; - //armed object virtual void getUpgradeInfo(const CArmedInstance *obj, SlotID stackPos, UpgradeInfo &out)const; //hero - virtual const CGHeroInstance* getHero(ObjectInstanceID objid) const; - virtual const CGHeroInstance* getHeroWithSubid(int subid) const; + virtual const CGHeroInstance * getHero(ObjectInstanceID objid) const override; + const CGHeroInstance * getHeroWithSubid(int subid) const override; virtual int getHeroCount(PlayerColor player, bool includeGarrisoned) const; virtual bool getHeroInfo(const CGObjectInstance * hero, InfoAboutHero & dest, const CGObjectInstance * selectedObject = nullptr) const; - virtual int getSpellCost(const CSpell * sp, const CGHeroInstance * caster) const; //when called during battle, takes into account creatures' spell cost reduction + virtual int32_t getSpellCost(const spells::Spell * sp, const CGHeroInstance * caster) const; //when called during battle, takes into account creatures' spell cost reduction virtual int64_t estimateSpellDamage(const CSpell * sp, const CGHeroInstance * hero) const; //estimates damage of given spell; returns 0 if spell causes no dmg virtual const CArtifactInstance * getArtInstance(ArtifactInstanceID aid) const; virtual const CGObjectInstance * getObjInstance(ObjectInstanceID oid) const; //virtual const CGObjectInstance * getArmyInstance(ObjectInstanceID oid) const; //objects - virtual const CGObjectInstance* getObj(ObjectInstanceID objid, bool verbose = true) const; + virtual const CGObjectInstance * getObj(ObjectInstanceID objid, bool verbose = true) const override; virtual std::vector getBlockingObjs(int3 pos)const; - virtual std::vector getVisitableObjs(int3 pos, bool verbose = true)const; + virtual std::vector getVisitableObjs(int3 pos, bool verbose = true) const override; virtual std::vector getFlaggableObjects(int3 pos) const; virtual const CGObjectInstance * getTopObj (int3 pos) const; virtual PlayerColor getOwner(ObjectInstanceID heroID) const; @@ -150,14 +239,3 @@ public: //virtual const PlayerSettings * getPlayerSettings(PlayerColor color) const; }; -class DLL_LINKAGE IGameEventRealizer -{ -public: - virtual void commitPackage(CPackForClient *pack) = 0; - - virtual void showInfoDialog(InfoWindow *iw); - virtual void setObjProperty(ObjectInstanceID objid, int prop, si64 val); - - - virtual void showInfoDialog(const std::string &msg, PlayerColor player); -}; diff --git a/lib/CGameInterface.cpp b/lib/CGameInterface.cpp index da64d7d3f..fc4731688 100644 --- a/lib/CGameInterface.cpp +++ b/lib/CGameInterface.cpp @@ -126,9 +126,9 @@ std::shared_ptr CDynLibHandler::getNewBattleAI(std::string return createAnyAI(dllname, "GetNewBattleAI"); } -std::shared_ptr CDynLibHandler::getNewScriptingModule(std::string dllname) +std::shared_ptr CDynLibHandler::getNewScriptingModule(const boost::filesystem::path & dllname) { - return createAny(dllname, "GetNewModule"); + return createAny(dllname, "GetNewModule"); } BattleAction CGlobalAI::activeStack(const CStack * stack) @@ -160,13 +160,13 @@ void CAdventureAI::battleStart(const CCreatureSet * army1, const CCreatureSet * assert(!battleAI); assert(cbc); battleAI = CDynLibHandler::getNewBattleAI(getBattleAIName()); - battleAI->init(cbc); + battleAI->init(env, cbc); battleAI->battleStart(army1, army2, tile, hero1, hero2, side); } -void CAdventureAI::battleStacksAttacked(const std::vector & bsa, const std::vector & battleLog) +void CAdventureAI::battleStacksAttacked(const std::vector & bsa) { - battleAI->battleStacksAttacked(bsa, battleLog); + battleAI->battleStacksAttacked(bsa); } void CAdventureAI::actionStarted(const BattleAction & action) @@ -215,9 +215,9 @@ void CAdventureAI::battleEnd(const BattleResult * br) battleAI.reset(); } -void CAdventureAI::battleUnitsChanged(const std::vector & units, const std::vector & customEffects, const std::vector & battleLog) +void CAdventureAI::battleUnitsChanged(const std::vector & units, const std::vector & customEffects) { - battleAI->battleUnitsChanged(units, customEffects, battleLog); + battleAI->battleUnitsChanged(units, customEffects); } BattleAction CAdventureAI::activeStack(const CStack * stack) @@ -233,20 +233,17 @@ void CAdventureAI::yourTacticPhase(int distance) void CAdventureAI::saveGame(BinarySerializer & h, const int version) /*saving */ { LOG_TRACE_PARAMS(logAi, "version '%i'", version); - CGlobalAI::saveGame(h, version); bool hasBattleAI = static_cast(battleAI); h & hasBattleAI; if(hasBattleAI) { - h & std::string(battleAI->dllName); - battleAI->saveGame(h, version); + h & battleAI->dllName; } } void CAdventureAI::loadGame(BinaryDeserializer & h, const int version) /*loading */ { LOG_TRACE_PARAMS(logAi, "version '%i'", version); - CGlobalAI::loadGame(h, version); bool hasBattleAI = false; h & hasBattleAI; if(hasBattleAI) @@ -255,15 +252,6 @@ void CAdventureAI::loadGame(BinaryDeserializer & h, const int version) /*loading h & dllName; battleAI = CDynLibHandler::getNewBattleAI(dllName); assert(cbc); //it should have been set by the one who new'ed us - battleAI->init(cbc); - //battleAI->loadGame(h, version); + battleAI->init(env, cbc); } } - -void CBattleGameInterface::saveGame(BinarySerializer & h, const int version) -{ -} - -void CBattleGameInterface::loadGame(BinaryDeserializer & h, const int version) -{ -} diff --git a/lib/CGameInterface.h b/lib/CGameInterface.h index 00a860d75..35b8671fe 100644 --- a/lib/CGameInterface.h +++ b/lib/CGameInterface.h @@ -18,6 +18,9 @@ #include "mapObjects/CObjectHandler.h" using boost::logic::tribool; + +class Environment; + class CCallback; class CBattleCallback; class ICallback; @@ -53,7 +56,10 @@ class CSaveFile; class BinaryDeserializer; class BinarySerializer; struct ArtifactLocation; -class CScriptingModule; +namespace scripting +{ + class Module; +} class DLL_LINKAGE CBattleGameInterface : public IBattleEventsReceiver { @@ -63,15 +69,11 @@ public: std::string dllName; virtual ~CBattleGameInterface() {}; - virtual void init(std::shared_ptr CB){}; + virtual void init(std::shared_ptr ENV, std::shared_ptr CB){}; //battle call-ins virtual BattleAction activeStack(const CStack * stack)=0; //called when it's turn of that stack virtual void yourTacticPhase(int distance){}; //called when interface has opportunity to use Tactics skill -> use cb->battleMakeTacticAction from this function - - virtual void saveGame(BinarySerializer & h, const int version); - virtual void loadGame(BinaryDeserializer & h, const int version); - }; /// Central class for managing human player / AI interface logic @@ -79,7 +81,7 @@ class DLL_LINKAGE CGameInterface : public CBattleGameInterface, public IGameEven { public: virtual ~CGameInterface() = default; - virtual void init(std::shared_ptr CB){}; + virtual void init(std::shared_ptr ENV, std::shared_ptr CB){}; virtual void yourTurn(){}; //called AFTER playerStartsTurn(player) //pskill is gained primary skill, interface has to choose one of given skills and call callback with selection id @@ -98,6 +100,9 @@ public: virtual void finish(){}; //if for some reason we want to end virtual void showWorldViewEx(const std::vector & objectPositions){}; + + virtual void saveGame(BinarySerializer & h, const int version) = 0; + virtual void loadGame(BinaryDeserializer & h, const int version) = 0; }; class DLL_LINKAGE CDynLibHandler @@ -105,12 +110,13 @@ class DLL_LINKAGE CDynLibHandler public: static std::shared_ptr getNewAI(std::string dllname); static std::shared_ptr getNewBattleAI(std::string dllname); - static std::shared_ptr getNewScriptingModule(std::string dllname); + static std::shared_ptr getNewScriptingModule(const boost::filesystem::path & dllname); }; class DLL_LINKAGE CGlobalAI : public CGameInterface // AI class (to derivate) { public: + std::shared_ptr env; CGlobalAI(); virtual BattleAction activeStack(const CStack * stack) override; }; @@ -132,7 +138,7 @@ public: virtual void battleNewRound(int round) override; virtual void battleCatapultAttacked(const CatapultAttack & ca) override; virtual void battleStart(const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool side) override; - virtual void battleStacksAttacked(const std::vector & bsa, const std::vector & battleLog) override; + virtual void battleStacksAttacked(const std::vector & bsa) override; virtual void actionStarted(const BattleAction &action) override; virtual void battleNewRoundFirst(int round) override; virtual void actionFinished(const BattleAction &action) override; @@ -142,8 +148,8 @@ public: virtual void battleAttack(const BattleAttack *ba) override; virtual void battleSpellCast(const BattleSpellCast *sc) override; virtual void battleEnd(const BattleResult *br) override; - virtual void battleUnitsChanged(const std::vector & units, const std::vector & customEffects, const std::vector & battleLog) override; + virtual void battleUnitsChanged(const std::vector & units, const std::vector & customEffects) override; - virtual void saveGame(BinarySerializer & h, const int version) override; //saving - virtual void loadGame(BinaryDeserializer & h, const int version) override; //loading + virtual void saveGame(BinarySerializer & h, const int version) override; + virtual void loadGame(BinaryDeserializer & h, const int version) override; }; diff --git a/lib/CGameState.cpp b/lib/CGameState.cpp index 848fa2068..ece3e4919 100644 --- a/lib/CGameState.cpp +++ b/lib/CGameState.cpp @@ -70,11 +70,43 @@ void MetaString::getLocalString(const std::pair &txt, std::string &dst if(type == ART_NAMES) { - dst = VLC->arth->artifacts[ser]->Name(); + auto art = ArtifactID(ser).toArtifact(VLC->artifacts()); + if(art) + dst = art->getName(); + else + dst = "#!#"; + } + else if(type == ART_DESCR) + { + auto art = ArtifactID(ser).toArtifact(VLC->artifacts()); + if(art) + dst = art->getDescription(); + else + dst = "#!#"; + } + else if (type == ART_EVNTS) + { + auto art = ArtifactID(ser).toArtifact(VLC->artifacts()); + if(art) + dst = art->getEventText(); + else + dst = "#!#"; } else if(type == CRE_PL_NAMES) { - dst = VLC->creh->creatures[ser]->namePl; + auto cre = CreatureID(ser).toCreature(VLC->creatures()); + if(cre) + dst = cre->getPluralName(); + else + dst = "#!#"; + } + else if(type == CRE_SING_NAMES) + { + auto cre = CreatureID(ser).toCreature(VLC->creatures()); + if(cre) + dst = cre->getSingularName(); + else + dst = "#!#"; } else if(type == MINE_NAMES) { @@ -86,21 +118,13 @@ void MetaString::getLocalString(const std::pair &txt, std::string &dst } else if(type == SPELL_NAME) { - dst = SpellID(ser).toSpell()->name; + auto spell = SpellID(ser).toSpell(VLC->spells()); + if(spell) + dst = spell->getName(); + else + dst = "#!#"; } - else if(type == CRE_SING_NAMES) - { - dst = VLC->creh->creatures[ser]->nameSing; - } - else if(type == ART_DESCR) - { - dst = VLC->arth->artifacts[ser]->Description(); - } - else if (type == ART_EVNTS) - { - dst = VLC->arth->artifacts[ser]->EventText(); - } - else if (type == OBJ_NAMES) + else if(type == OBJ_NAMES) { dst = VLC->objtypeh->getObjectName(ser); } @@ -258,8 +282,7 @@ DLL_LINKAGE std::string MetaString::buildList () const return lista; } - -void MetaString::addCreReplacement(CreatureID id, TQuantity count) //adds sing or plural name; +void MetaString::addCreReplacement(CreatureID id, TQuantity count) //adds sing or plural name; { if (!count) addReplacement (CRE_PL_NAMES, id); //no creatures - just empty name (eg. defeat Angels) @@ -269,7 +292,7 @@ void MetaString::addCreReplacement(CreatureID id, TQuantity count) //adds sing addReplacement (CRE_PL_NAMES, id); } -void MetaString::addReplacement(const CStackBasicDescriptor &stack) +void MetaString::addReplacement(const CStackBasicDescriptor & stack) { assert(stack.type); //valid type addCreReplacement(stack.type->idNumber, stack.count); @@ -282,7 +305,7 @@ static CGObjectInstance * createObject(Obj id, int subid, int3 pos, PlayerColor { case Obj::HERO: { - auto handler = VLC->objtypeh->getHandlerFor(id, VLC->heroh->heroes[subid]->heroClass->id); + auto handler = VLC->objtypeh->getHandlerFor(id, VLC->heroh->objects[subid]->heroClass->getIndex()); nobj = handler->create(handler->getTemplates().front()); break; } @@ -409,7 +432,7 @@ int CGameState::pickUnusedHeroTypeRandomly(PlayerColor owner) const PlayerSettings &ps = scenarioOps->getIthPlayersSettings(owner); for(HeroTypeID hid : getUnusedAllowedHeroes()) { - if(VLC->heroh->heroes[hid.getNum()]->heroClass->faction == ps.castle) + if(VLC->heroh->objects[hid.getNum()]->heroClass->faction == ps.castle) factionHeroes.push_back(hid); else otherHeroes.push_back(hid); @@ -484,9 +507,9 @@ std::pair CGameState::pickObject (CGObjectInstance *obj) { do { - f = getRandomGenerator().nextInt((int)VLC->townh->factions.size() - 1); + f = getRandomGenerator().nextInt((int)VLC->townh->size() - 1); } - while (VLC->townh->factions[f]->town == nullptr); // find playable faction + while ((*VLC->townh)[f]->town == nullptr); // find playable faction } return std::make_pair(Obj::TOWN,f); } @@ -506,7 +529,7 @@ std::pair CGameState::pickObject (CGObjectInstance *obj) //if castle alignment available if (auto info = dynamic_cast(dwl->info)) { - faction = getRandomGenerator().nextInt((int)VLC->townh->factions.size() - 1); + faction = getRandomGenerator().nextInt((int)VLC->townh->size() - 1); if(info->asCastle && info->instanceId != "") { auto iter = map->instanceNames.find(info->instanceId); @@ -583,7 +606,7 @@ std::pair CGameState::pickObject (CGObjectInstance *obj) dwl->info = nullptr; std::pair result(Obj::NO_OBJ, -1); - CreatureID cid = VLC->townh->factions[faction]->town->creatures[level][0]; + CreatureID cid = (*VLC->townh)[faction]->town->creatures[level][0]; //NOTE: this will pick last dwelling with this creature (Mantis #900) //check for block map equality is better but more complex solution @@ -594,7 +617,7 @@ std::pair CGameState::pickObject (CGObjectInstance *obj) { auto handler = dynamic_cast(VLC->objtypeh->getHandlerFor(primaryID, entry).get()); - if (handler->producesCreature(VLC->creh->creatures[cid])) + if (handler->producesCreature(VLC->creh->objects[cid])) result = std::make_pair(primaryID, entry); } }; @@ -605,7 +628,7 @@ std::pair CGameState::pickObject (CGObjectInstance *obj) if (result.first == Obj::NO_OBJ) { - logGlobal->error("Error: failed to find dwelling for %s of level %d", VLC->townh->factions[faction]->name, int(level)); + logGlobal->error("Error: failed to find dwelling for %s of level %d", (*VLC->townh)[faction]->name, int(level)); result = std::make_pair(Obj::CREATURE_GENERATOR1, *RandomGeneratorUtil::nextItem(VLC->objtypeh->knownSubObjects(Obj::CREATURE_GENERATOR1), getRandomGenerator())); } @@ -678,6 +701,7 @@ CGameState::CGameState() globalEffects.setDescription("Global effects"); globalEffects.setNodeType(CBonusSystemNode::GLOBAL_EFFECTS); day = 0; + services = nullptr; } CGameState::~CGameState() @@ -689,8 +713,14 @@ CGameState::~CGameState() ptr.second.dellNull(); } +void CGameState::preInit(Services * services) +{ + this->services = services; +} + void CGameState::init(const IMapService * mapService, StartInfo * si, bool allowSavingRandomMap) { + preInitAuto(); logGlobal->info("\tUsing random seed: %d", si->seedToBeUsed); getRandomGenerator().setSeed(si->seedToBeUsed); scenarioOps = CMemorySerializer::deepCopy(*si).release(); @@ -759,13 +789,62 @@ void CGameState::init(const IMapService * mapService, StartInfo * si, bool allow } } +void CGameState::updateEntity(Metatype metatype, int32_t index, const JsonNode & data) +{ + switch(metatype) + { + case Metatype::ARTIFACT_INSTANCE: + logGlobal->error("Artifact instance update is not implemented"); + break; + case Metatype::CREATURE_INSTANCE: + logGlobal->error("Creature instance update is not implemented"); + break; + case Metatype::HERO_INSTANCE: + //index is hero type + if(index >= 0 && index < map->allHeroes.size()) + { + CGHeroInstance * hero = map->allHeroes.at(index); + hero->updateFrom(data); + } + else + { + logGlobal->error("Update entity: hero index %s is out of range [%d,%d]", index, 0, map->allHeroes.size()); + } + break; + case Metatype::MAP_OBJECT_INSTANCE: + if(index >= 0 && index < map->objects.size()) + { + CGObjectInstance * obj = getObjInstance(ObjectInstanceID(index)); + obj->updateFrom(data); + } + else + { + logGlobal->error("Update entity: object index %s is out of range [%d,%d]", index, 0, map->objects.size()); + } + break; + default: + services->updateEntity(metatype, index, data); + break; + } +} + void CGameState::updateOnLoad(StartInfo * si) { + preInitAuto(); scenarioOps->playerInfos = si->playerInfos; for(auto & i : si->playerInfos) gs->players[i.first].human = i.second.isControlledByHuman(); } +void CGameState::preInitAuto() +{ + if(services == nullptr) + { + logGlobal->error("Game state preinit missing"); + preInit(VLC); + } +} + void CGameState::initNewGame(const IMapService * mapService, bool allowSavingRandomMap) { if(scenarioOps->createRandomMap()) @@ -1364,7 +1443,7 @@ void CGameState::initHeroes() } hero->initHero(getRandomGenerator()); - getPlayer(hero->getOwner())->heroes.push_back(hero); + getPlayerState(hero->getOwner())->heroes.push_back(hero); map->allHeroes[hero->type->ID.getNum()] = hero; } @@ -1483,7 +1562,7 @@ void CGameState::giveCampaignBonusToHero(CGHeroInstance * hero) break; case CScenarioTravel::STravelBonus::SPELL_SCROLL: { - CArtifactInstance * scroll = CArtifactInstance::createScroll(SpellID(curBonus->info2).toSpell()); + CArtifactInstance * scroll = CArtifactInstance::createScroll(SpellID(curBonus->info2)); scroll->putAt(ArtifactLocation(hero, scroll->firstAvailableSlot(hero))); } break; @@ -1561,7 +1640,7 @@ void CGameState::initStartingBonus() break; case PlayerSettings::RESOURCE: { - int res = VLC->townh->factions[scenarioOps->playerInfos[elem.first].castle]->town->primaryRes; + int res = (*VLC->townh)[scenarioOps->playerInfos[elem.first].castle]->town->primaryRes; if(res == Res::WOOD_AND_ORE) { int amount = getRandomGenerator().nextInt(5, 10); @@ -1581,11 +1660,10 @@ void CGameState::initStartingBonus() logGlobal->error("Cannot give starting artifact - no heroes!"); break; } - CArtifact *toGive; - toGive = VLC->arth->artifacts[VLC->arth->pickRandomArtifact(getRandomGenerator(), CArtifact::ART_TREASURE)]; + const Artifact * toGive = VLC->arth->pickRandomArtifact(getRandomGenerator(), CArtifact::ART_TREASURE).toArtifact(VLC->artifacts()); CGHeroInstance *hero = elem.second.heroes[0]; - giveHeroArtifact(hero, toGive->id); + giveHeroArtifact(hero, toGive->getId()); } break; } @@ -1605,7 +1683,7 @@ void CGameState::initTowns() { for (int g=0; gtowns.size(); ++g) { - PlayerState * owner = getPlayer(map->towns[g]->getOwner()); + PlayerState * owner = getPlayerState(map->towns[g]->getOwner()); if (owner) { PlayerInfo & pi = map->players[owner->color.getNum()]; @@ -1631,7 +1709,7 @@ void CGameState::initTowns() CGTownInstance * vti =(elem); if(!vti->town) { - vti->town = VLC->townh->factions[vti->subID]->town; + vti->town = (*VLC->townh)[vti->subID]->town; } if(vti->name.empty()) { @@ -1738,7 +1816,7 @@ void CGameState::initTowns() } vti->possibleSpells.clear(); if(vti->getOwner() != PlayerColor::NEUTRAL) - getPlayer(vti->getOwner())->towns.push_back(vti); + getPlayerState(vti->getOwner())->towns.push_back(vti); } } @@ -1906,7 +1984,7 @@ UpgradeInfo CGameState::getUpgradeInfo(const CStackInstance &stack) if (nid != base->idNumber) //in very specific case the upgrade is available by default (?) { ret.newID.push_back(nid); - ret.cost.push_back(VLC->creh->creatures[nid]->cost - base->cost); + ret.cost.push_back(VLC->creh->objects[nid]->cost - base->cost); } } t = h->visitedTown; @@ -1922,7 +2000,7 @@ UpgradeInfo CGameState::getUpgradeInfo(const CStackInstance &stack) if(vstd::contains(base->upgrades, upgrID)) //possible upgrade { ret.newID.push_back(upgrID); - ret.cost.push_back(VLC->creh->creatures[upgrID]->cost - base->cost); + ret.cost.push_back(VLC->creh->objects[upgrID]->cost - base->cost); } } } @@ -1938,7 +2016,7 @@ UpgradeInfo CGameState::getUpgradeInfo(const CStackInstance &stack) for(auto nid : base->upgrades) { ret.newID.push_back(nid); - ret.cost.push_back((VLC->creh->creatures[nid]->cost - base->cost) * costModifier / 100); + ret.cost.push_back((VLC->creh->objects[nid]->cost - base->cost) * costModifier / 100); } } @@ -2171,7 +2249,7 @@ EVictoryLossCheckResult CGameState::checkForVictoryAndLoss(PlayerColor player) c return this->checkForVictory(player, condition); }; - const PlayerState *p = CGameInfoCallback::getPlayer(player); + const PlayerState *p = CGameInfoCallback::getPlayerState(player); //cheater or tester, but has entered the code... if (p->enteredWinningCheatCode) @@ -2201,7 +2279,7 @@ EVictoryLossCheckResult CGameState::checkForVictoryAndLoss(PlayerColor player) c bool CGameState::checkForVictory(PlayerColor player, const EventCondition & condition) const { - const PlayerState *p = CGameInfoCallback::getPlayer(player); + const PlayerState *p = CGameInfoCallback::getPlayerState(player); switch (condition.condition) { case EventCondition::STANDARD_WIN: @@ -2377,7 +2455,7 @@ PlayerColor CGameState::checkForStandardWin() const bool CGameState::checkForStandardLoss( PlayerColor player ) const { //std loss condition is: player lost all towns and heroes - const PlayerState &p = *CGameInfoCallback::getPlayer(player); + const PlayerState &p = *CGameInfoCallback::getPlayerState(player); return !p.heroes.size() && !p.towns.size(); } @@ -2631,7 +2709,7 @@ void CGameState::obtainPlayersStats(SThievesGuildInfo & tgi, int level) for(auto it = elem->Slots().begin(); it != elem->Slots().end(); ++it) { int toCmp = it->second->type->idNumber; //ID of creature we should compare with the best one - if(bestCre == -1 || VLC->creh->creatures[bestCre]->AIValue < VLC->creh->creatures[toCmp]->AIValue) + if(bestCre == -1 || VLC->creh->objects[bestCre]->AIValue < VLC->creh->objects[toCmp]->AIValue) { bestCre = toCmp; } @@ -2683,7 +2761,7 @@ void CGameState::buildGlobalTeamPlayerTree() for(PlayerColor teamMember : k->second.players) { - PlayerState *p = getPlayer(teamMember); + PlayerState *p = getPlayerState(teamMember); assert(p); p->attachTo(t); } @@ -2701,7 +2779,7 @@ void CGameState::attachArmedObjects() void CGameState::giveHeroArtifact(CGHeroInstance *h, ArtifactID aid) { - CArtifact * const artifact = VLC->arth->artifacts[aid]; //pointer to constant object + CArtifact * const artifact = VLC->arth->objects[aid]; //pointer to constant object CArtifactInstance *ai = CArtifactInstance::createNewArtifactInstance(artifact); map->addNewArtifactInstance(ai); ai->putAt(ArtifactLocation(h, ai->firstAvailableSlot(h))); @@ -2713,7 +2791,7 @@ std::set CGameState::getUnusedAllowedHeroes(bool alsoIncludeNotAllow for(int i = 0; i < map->allowedHeroes.size(); i++) if(map->allowedHeroes[i] || alsoIncludeNotAllowed) ret.insert(HeroTypeID(i)); - + for(auto & playerSettingPair : scenarioOps->playerInfos) //remove uninitialized yet heroes picked for start by other players { if(playerSettingPair.second.hero != PlayerSettings::RANDOM) @@ -2809,14 +2887,14 @@ void CGameState::replaceHeroesPlaceholders(const std::vectorid = campaignHeroReplacement.heroPlaceholderId; heroToPlace->tempOwner = heroPlaceholder->tempOwner; heroToPlace->pos = heroPlaceholder->pos; - heroToPlace->type = VLC->heroh->heroes[heroToPlace->subID]; + heroToPlace->type = VLC->heroh->objects[heroToPlace->subID]; for(auto &&i : heroToPlace->stacks) - i.second->type = VLC->creh->creatures[i.second->getCreatureID()]; + i.second->type = VLC->creh->objects[i.second->getCreatureID()]; auto fixArtifact = [&](CArtifactInstance * art) { - art->artType = VLC->arth->artifacts[art->artType->id]; + art->artType = VLC->arth->objects[art->artType->id]; gs->map->artInstances.push_back(art); art->id = ArtifactInstanceID((si32)gs->map->artInstances.size() - 1); }; @@ -2863,38 +2941,6 @@ CGHeroInstance * CGameState::getUsedHero(HeroTypeID hid) const return nullptr; } -PlayerState::PlayerState() - : color(-1), human(false), enteredWinningCheatCode(false), - enteredLosingCheatCode(false), status(EPlayerStatus::INGAME) -{ - setNodeType(PLAYER); -} - -PlayerState::PlayerState(PlayerState && other): - CBonusSystemNode(std::move(other)), - color(other.color), - human(other.human), - team(other.team), - resources(other.resources), - enteredWinningCheatCode(other.enteredWinningCheatCode), - enteredLosingCheatCode(other.enteredLosingCheatCode), - status(other.status), - daysWithoutCastle(other.daysWithoutCastle) -{ - std::swap(visitedObjects, other.visitedObjects); - std::swap(heroes, other.heroes); - std::swap(towns, other.towns); - std::swap(availableHeroes, other.availableHeroes); - std::swap(dwellings, other.dwellings); - std::swap(quests, other.quests); -} - -std::string PlayerState::nodeName() const -{ - return "Player " + (color.getNum() < VLC->generaltexth->capColors.size() ? VLC->generaltexth->capColors[color.getNum()] : boost::lexical_cast(color)); -} - - bool RumorState::update(int id, int extra) { if(vstd::contains(last, type)) diff --git a/lib/CGameState.h b/lib/CGameState.h index affbad69b..36de7a442 100644 --- a/lib/CGameState.h +++ b/lib/CGameState.h @@ -60,6 +60,8 @@ namespace boost class shared_mutex; } + + template class CApplier; class CBaseForGSApply; @@ -155,6 +157,8 @@ public: CGameState(); virtual ~CGameState(); + void preInit(Services * services); + void init(const IMapService * mapService, StartInfo * si, bool allowSavingRandomMap = false); void updateOnLoad(StartInfo * si); @@ -170,6 +174,8 @@ public: static boost::shared_mutex mutex; + void updateEntity(Metatype metatype, int32_t index, const JsonNode & data) override; + void giveHeroArtifact(CGHeroInstance *h, ArtifactID aid); void apply(CPack *pack); @@ -178,7 +184,7 @@ public: PlayerRelations::PlayerRelations getPlayerRelations(PlayerColor color1, PlayerColor color2); bool checkForVisitableDir(const int3 & src, const int3 & dst) const; //check if src tile is visitable from dst tile void calculatePaths(const CGHeroInstance *hero, CPathsInfo &out); //calculates possible paths for hero, by default uses current hero position and movement left; returns pointer to newly allocated CPath or nullptr if path does not exists - void calculatePaths(std::shared_ptr config, const CGHeroInstance * hero); + void calculatePaths(std::shared_ptr config, const CGHeroInstance * hero) override; int3 guardingCreaturePosition (int3 pos) const; std::vector guardingCreatures (int3 pos) const; void updateRumor(); @@ -196,7 +202,7 @@ public: bool isVisible(int3 pos, PlayerColor player); bool isVisible(const CGObjectInstance *obj, boost::optional player); - int getDate(Date::EDateType mode=Date::DAY) const; //mode=0 - total days in game, mode=1 - day of week, mode=2 - current week, mode=3 - current month + int getDate(Date::EDateType mode=Date::DAY) const override; //mode=0 - total days in game, mode=1 - day of week, mode=2 - current week, mode=3 - current month // ----- getters, setters ----- @@ -249,7 +255,7 @@ private: }; // ----- initialization ----- - + void preInitAuto(); void initNewGame(const IMapService * mapService, bool allowSavingRandomMap); void initCampaign(); void checkMapChecksum(); @@ -298,6 +304,7 @@ private: // ---- data ----- std::shared_ptr> applier; CRandomGenerator rand; + Services * services; friend class CCallback; friend class CClient; diff --git a/lib/CGameStateFwd.h b/lib/CGameStateFwd.h index 5ad7b4771..11d2e5461 100644 --- a/lib/CGameStateFwd.h +++ b/lib/CGameStateFwd.h @@ -10,6 +10,7 @@ #pragma once #include "CCreatureSet.h" +#include "int3.h" class CQuest; class CGObjectInstance; diff --git a/lib/CHeroHandler.cpp b/lib/CHeroHandler.cpp index 930dc69e6..14c6a2cb6 100644 --- a/lib/CHeroHandler.cpp +++ b/lib/CHeroHandler.cpp @@ -25,6 +25,53 @@ #include "mapObjects/CObjectClassesHandler.h" +CHero::CHero() = default; +CHero::~CHero() = default; + +int32_t CHero::getIndex() const +{ + return ID.getNum(); +} + +int32_t CHero::getIconIndex() const +{ + return imageIndex; +} + +const std::string & CHero::getName() const +{ + return name; +} + +const std::string & CHero::getJsonKey() const +{ + return identifier; +} + +HeroTypeID CHero::getId() const +{ + return ID; +} + +void CHero::registerIcons(const IconRegistar & cb) const +{ + cb(getIconIndex(), "UN32", iconSpecSmall); + cb(getIconIndex(), "UN44", iconSpecLarge); + cb(getIconIndex(), "PORTRAITSLARGE", portraitLarge); + cb(getIconIndex(), "PORTRAITSSMALL", portraitSmall); +} + +void CHero::updateFrom(const JsonNode & data) +{ + //todo: CHero::updateFrom +} + +void CHero::serializeJson(JsonSerializeFormat & handler) +{ + +} + + SecondarySkill CHeroClass::chooseSecSkill(const std::set & possibles, CRandomGenerator & rand) const //picks secondary skill out from given possibilities { int totalProb = 0; @@ -55,11 +102,52 @@ bool CHeroClass::isMagicHero() const EAlignment::EAlignment CHeroClass::getAlignment() const { - return EAlignment::EAlignment(VLC->townh->factions[faction]->alignment); + return EAlignment::EAlignment((*VLC->townh)[faction]->alignment); } +int32_t CHeroClass::getIndex() const +{ + return id.getNum(); +} + +int32_t CHeroClass::getIconIndex() const +{ + return getIndex(); +} + +const std::string & CHeroClass::getName() const +{ + return name; +} + +const std::string & CHeroClass::getJsonKey() const +{ + return identifier; +} + +HeroClassID CHeroClass::getId() const +{ + return id; +} + +void CHeroClass::registerIcons(const IconRegistar & cb) const +{ + +} + +void CHeroClass::updateFrom(const JsonNode & data) +{ + //TODO: CHeroClass::updateFrom +} + +void CHeroClass::serializeJson(JsonSerializeFormat & handler) +{ + +} + + CHeroClass::CHeroClass() - : faction(0), id(0), affinity(0), defaultTavernChance(0), commander(nullptr) + : faction(0), id(), affinity(0), defaultTavernChance(0), commander(nullptr) { } @@ -114,11 +202,19 @@ void CHeroClassHandler::fillPrimarySkillData(const JsonNode & node, CHeroClass * heroClass->primarySkillHighLevel.push_back((int)node["highLevelChance"][skillName].Float()); } -CHeroClass * CHeroClassHandler::loadFromJson(const JsonNode & node, const std::string & identifier) +const std::vector & CHeroClassHandler::getTypeNames() const +{ + static const std::vector typeNames = { "heroClass" }; + return typeNames; +} + +CHeroClass * CHeroClassHandler::loadFromJson(const std::string & scope, const JsonNode & node, const std::string & identifier, size_t index) { std::string affinityStr[2] = { "might", "magic" }; - auto heroClass = new CHeroClass(); + auto heroClass = new CHeroClass(); + + heroClass->id = HeroClassID(index); heroClass->identifier = identifier; heroClass->imageBattleFemale = node["animation"]["battle"]["female"].String(); heroClass->imageBattleMale = node["animation"]["battle"]["male"].String(); @@ -148,7 +244,7 @@ CHeroClass * CHeroClassHandler::loadFromJson(const JsonNode & node, const std::s VLC->modh->identifiers.requestIdentifier ("creature", node["commander"], [=](si32 commanderID) { - heroClass->commander = VLC->creh->creatures[commanderID]; + heroClass->commander = VLC->creh->objects[commanderID]; }); heroClass->defaultTavernChance = static_cast(node["defaultTavern"].Float()); @@ -169,12 +265,20 @@ CHeroClass * CHeroClassHandler::loadFromJson(const JsonNode & node, const std::s heroClass->faction = factionID; }); + VLC->modh->identifiers.requestIdentifier(scope, "object", "hero", [=](si32 index) + { + JsonNode classConf = node["mapObject"]; + classConf["heroClass"].String() = identifier; + classConf.setMeta(scope); + VLC->objtypeh->loadSubObject(identifier, classConf, index, heroClass->getIndex()); + }); + return heroClass; } std::vector CHeroClassHandler::loadLegacyData(size_t dataSize) { - heroClasses.resize(dataSize); + objects.resize(dataSize); std::vector h3Data; h3Data.reserve(dataSize); @@ -212,49 +316,12 @@ std::vector CHeroClassHandler::loadLegacyData(size_t dataSize) return h3Data; } -void CHeroClassHandler::loadObject(std::string scope, std::string name, const JsonNode & data) -{ - auto object = loadFromJson(data, normalizeIdentifier(scope, "core", name)); - object->id = static_cast(heroClasses.size()); - - heroClasses.push_back(object); - - VLC->modh->identifiers.requestIdentifier(scope, "object", "hero", [=](si32 index) - { - JsonNode classConf = data["mapObject"]; - classConf["heroClass"].String() = name; - classConf.setMeta(scope); - VLC->objtypeh->loadSubObject(name, classConf, index, object->id); - }); - - VLC->modh->identifiers.registerObject(scope, "heroClass", name, object->id); -} - -void CHeroClassHandler::loadObject(std::string scope, std::string name, const JsonNode & data, size_t index) -{ - auto object = loadFromJson(data, normalizeIdentifier(scope, "core", name)); - object->id = static_cast(index); - - assert(heroClasses[index] == nullptr); // ensure that this id was not loaded before - heroClasses[index] = object; - - VLC->modh->identifiers.requestIdentifier(scope, "object", "hero", [=](si32 index) - { - JsonNode classConf = data["mapObject"]; - classConf["heroClass"].String() = name; - classConf.setMeta(scope); - VLC->objtypeh->loadSubObject(name, classConf, index, object->id); - }); - - VLC->modh->identifiers.registerObject(scope, "heroClass", name, object->id); -} - void CHeroClassHandler::afterLoadFinalization() { // for each pair set selection probability if it was not set before in tavern entries - for (CHeroClass * heroClass : heroClasses) + for(CHeroClass * heroClass : objects) { - for (CFaction * faction : VLC->townh->factions) + for(CFaction * faction : VLC->townh->objects) { if (!faction->town) continue; @@ -277,40 +344,28 @@ void CHeroClassHandler::afterLoadFinalization() } } - for (CHeroClass * hc : heroClasses) + for(CHeroClass * hc : objects) { if (!hc->imageMapMale.empty()) { JsonNode templ; templ["animation"].String() = hc->imageMapMale; - VLC->objtypeh->getHandlerFor(Obj::HERO, hc->id)->addTemplate(templ); + VLC->objtypeh->getHandlerFor(Obj::HERO, hc->getIndex())->addTemplate(templ); } } } std::vector CHeroClassHandler::getDefaultAllowed() const { - return std::vector(heroClasses.size(), true); + return std::vector(size(), true); } -CHeroClassHandler::~CHeroClassHandler() -{ - for(auto heroClass : heroClasses) - { - delete heroClass.get(); - } -} +CHeroClassHandler::~CHeroClassHandler() = default; -CHeroHandler::~CHeroHandler() -{ - for(auto hero : heroes) - delete hero.get(); -} +CHeroHandler::~CHeroHandler() = default; CHeroHandler::CHeroHandler() { - VLC->heroh = this; - loadObstacles(); loadTerrains(); for (int i = 0; i < GameConstants::TERRAIN_TYPES; ++i) @@ -321,9 +376,16 @@ CHeroHandler::CHeroHandler() loadExperience(); } -CHero * CHeroHandler::loadFromJson(const JsonNode & node, const std::string & identifier) +const std::vector & CHeroHandler::getTypeNames() const +{ + static const std::vector typeNames = { "hero" }; + return typeNames; +} + +CHero * CHeroHandler::loadFromJson(const std::string & scope, const JsonNode & node, const std::string & identifier, size_t index) { auto hero = new CHero(); + hero->ID = HeroTypeID(index); hero->identifier = identifier; hero->sex = node["female"].Bool(); hero->special = node["special"].Bool(); @@ -347,7 +409,7 @@ CHero * CHeroHandler::loadFromJson(const JsonNode & node, const std::string & id VLC->modh->identifiers.requestIdentifier("heroClass", node["class"], [=](si32 classID) { - hero->heroClass = classes.heroClasses[classID]; + hero->heroClass = classes[HeroClassID(classID)]; }); return hero; @@ -412,7 +474,7 @@ void CHeroHandler::loadHeroSkills(CHero * hero, const JsonNode & node) // add standard creature specialty to result void AddSpecialtyForCreature(int creatureID, std::shared_ptr bonus, std::vector> &result) { - const CCreature &specBaseCreature = *VLC->creh->creatures[creatureID]; //base creature in which we have specialty + const CCreature &specBaseCreature = *VLC->creh->objects[creatureID]; //base creature in which we have specialty bonus->limiter.reset(new CCreatureTypeLimiter(specBaseCreature, true)); bonus->type = Bonus::STACKS_SPEED; @@ -427,7 +489,7 @@ void AddSpecialtyForCreature(int creatureID, std::shared_ptr bonus, std:: for(int cid : specTargets) { - const CCreature &specCreature = *VLC->creh->creatures[cid]; + const CCreature &specCreature = *VLC->creh->objects[cid]; bonus = std::make_shared(*bonus); bonus->limiter.reset(new CCreatureTypeLimiter(specCreature, false)); bonus->type = Bonus::PRIMARY_SKILL; @@ -441,7 +503,7 @@ void AddSpecialtyForCreature(int creatureID, std::shared_ptr bonus, std:: bonus = std::make_shared(*bonus); bonus->subtype = PrimarySkill::DEFENSE; - bonus->updater.reset(new GrowsWithLevelUpdater(specCreature.getDefence(false), stepSize)); + bonus->updater.reset(new GrowsWithLevelUpdater(specCreature.getDefense(false), stepSize)); result.push_back(bonus); } } @@ -501,7 +563,7 @@ std::vector> SpecialtyInfoToBonuses(const SSpecialtyInfo return result; } bonus->valType = Bonus::ADDITIVE_VALUE; - bonus->limiter.reset(new CCreatureTypeLimiter(*VLC->creh->creatures[spec.additionalinfo], true)); + bonus->limiter.reset(new CCreatureTypeLimiter(*VLC->creh->objects[spec.additionalinfo], true)); result.push_back(bonus); break; case 5: //spell damage bonus in percent @@ -530,7 +592,7 @@ std::vector> SpecialtyInfoToBonuses(const SSpecialtyInfo break; case 9: //upgrade creatures { - const auto &creatures = VLC->creh->creatures; + const auto &creatures = VLC->creh->objects; bonus->type = Bonus::SPECIAL_UPGRADE; bonus->subtype = spec.subtype; //base id bonus->additionalInfo = spec.additionalinfo; //target id @@ -612,7 +674,7 @@ std::vector> SpecialtyBonusToBonuses(const SSpecialtyBonu if(creatureLimiter) { const CCreature * cre = creatureLimiter->creature; - int creStat = newBonus->subtype == PrimarySkill::ATTACK ? cre->getAttack(false) : cre->getDefence(false); + int creStat = newBonus->subtype == PrimarySkill::ATTACK ? cre->getAttack(false) : cre->getDefense(false); int creLevel = cre->level ? cre->level : 5; newBonus->updater = std::make_shared(creStat, creLevel); } @@ -808,7 +870,7 @@ void CHeroHandler::loadBallistics() std::vector CHeroHandler::loadLegacyData(size_t dataSize) { - heroes.resize(dataSize); + objects.resize(dataSize); std::vector h3Data; h3Data.reserve(dataSize); @@ -852,39 +914,38 @@ std::vector CHeroHandler::loadLegacyData(size_t dataSize) void CHeroHandler::loadObject(std::string scope, std::string name, const JsonNode & data) { - auto object = loadFromJson(data, normalizeIdentifier(scope, "core", name)); - object->ID = HeroTypeID((si32)heroes.size()); - object->imageIndex = (si32)heroes.size() + GameConstants::HERO_PORTRAIT_SHIFT; // 2 special frames + some extra portraits + size_t index = objects.size(); + auto object = loadFromJson(scope, data, normalizeIdentifier(scope, "core", name), index); + object->imageIndex = (si32)index + GameConstants::HERO_PORTRAIT_SHIFT; // 2 special frames + some extra portraits - heroes.push_back(object); + objects.push_back(object); - VLC->modh->identifiers.registerObject(scope, "hero", name, object->ID.getNum()); + registerObject(scope, "hero", name, object->getIndex()); } void CHeroHandler::loadObject(std::string scope, std::string name, const JsonNode & data, size_t index) { - auto object = loadFromJson(data, normalizeIdentifier(scope, "core", name)); - object->ID = HeroTypeID((si32)index); + auto object = loadFromJson(scope, data, normalizeIdentifier(scope, "core", name), index); object->imageIndex = static_cast(index); - assert(heroes[index] == nullptr); // ensure that this id was not loaded before - heroes[index] = object; + assert(objects[index] == nullptr); // ensure that this id was not loaded before + objects[index] = object; - VLC->modh->identifiers.registerObject(scope, "hero", name, object->ID.getNum()); + registerObject(scope, "hero", name, object->getIndex()); } void CHeroHandler::afterLoadFinalization() { - for(ConstTransitivePtr hero : heroes) + for(auto & hero : objects) { for(auto bonus : hero->specialty) { - bonus->sid = hero->ID.getNum(); + bonus->sid = hero->getIndex(); } if(hero->specDeprecated.size() > 0 || hero->specialtyDeprecated.size() > 0) { - logMod->debug("Converting specialty format for hero %s(%s)", hero->identifier, VLC->townh->encodeFaction(hero->heroClass->faction)); + logMod->debug("Converting specialty format for hero %s(%s)", hero->identifier, FactionID::encode(hero->heroClass->faction)); std::vector> convertedBonuses; for(const SSpecialtyInfo & spec : hero->specDeprecated) { @@ -971,26 +1032,12 @@ std::vector CHeroHandler::getDefaultAllowed() const { // Look Data/HOTRAITS.txt for reference std::vector allowedHeroes; - allowedHeroes.reserve(heroes.size()); + allowedHeroes.reserve(size()); - for(const CHero * hero : heroes) + for(const CHero * hero : objects) { allowedHeroes.push_back(!hero->special); } return allowedHeroes; } - -si32 CHeroHandler::decodeHero(const std::string & identifier) -{ - auto rawId = VLC->modh->identifiers.getIdentifier("core", "hero", identifier); - if(rawId) - return rawId.get(); - else - return -1; -} - -std::string CHeroHandler::encodeHero(const si32 index) -{ - return VLC->heroh->heroes.at(index)->identifier; -} diff --git a/lib/CHeroHandler.h b/lib/CHeroHandler.h index ca740dd7f..f0fd83031 100644 --- a/lib/CHeroHandler.h +++ b/lib/CHeroHandler.h @@ -9,6 +9,11 @@ */ #pragma once +#include +#include +#include +#include + #include "../lib/ConstTransitivePtr.h" #include "GameConstants.h" #include "HeroBonus.h" @@ -20,6 +25,7 @@ class CGHeroInstance; struct BattleHex; class JsonNode; class CRandomGenerator; +class JsonSerializeFormat; struct SSpecialtyInfo { si32 type; @@ -47,7 +53,7 @@ struct SSpecialtyBonus } }; -class DLL_LINKAGE CHero +class DLL_LINKAGE CHero : public HeroType { public: struct InitialArmyStack @@ -93,6 +99,19 @@ public: std::string portraitLarge; std::string battleImage; + CHero(); + virtual ~CHero(); + + int32_t getIndex() const override; + int32_t getIconIndex() const override; + const std::string & getName() const override; + const std::string & getJsonKey() const override; + HeroTypeID getId() const override; + void registerIcons(const IconRegistar & cb) const override; + + void updateFrom(const JsonNode & data); + void serializeJson(JsonSerializeFormat & handler); + template void serialize(Handler &h, const int version) { h & ID; @@ -137,7 +156,7 @@ public: std::vector> SpecialtyInfoToBonuses(const SSpecialtyInfo & spec, int sid = 0); std::vector> SpecialtyBonusToBonuses(const SSpecialtyBonus & spec, int sid = 0); -class DLL_LINKAGE CHeroClass +class DLL_LINKAGE CHeroClass : public HeroClass { public: enum EClassAffinity @@ -150,8 +169,8 @@ public: std::string name; // translatable //double aggression; // not used in vcmi. TFaction faction; - ui8 id; - ui8 affinity; // affility, using EClassAffinity enum + HeroClassID id; + ui8 affinity; // affinity, using EClassAffinity enum // default chance for hero of specific class to appear in tavern, if field "tavern" was not set // resulting chance = sqrt(town.chance * heroClass.chance) @@ -174,15 +193,34 @@ public: CHeroClass(); + int32_t getIndex() const override; + int32_t getIconIndex() const override; + const std::string & getName() const override; + const std::string & getJsonKey() const override; + HeroClassID getId() const override; + void registerIcons(const IconRegistar & cb) const override; + bool isMagicHero() const; SecondarySkill chooseSecSkill(const std::set & possibles, CRandomGenerator & rand) const; //picks secondary skill out from given possibilities - template void serialize(Handler &h, const int version) + void updateFrom(const JsonNode & data); + void serializeJson(JsonSerializeFormat & handler); + + template void serialize(Handler & h, const int version) { h & identifier; h & name; h & faction; - h & id; + if(version >= 800) + { + h & id; + } + else + { + ui8 old_id = 0; + h & old_id; + id = HeroClassID(old_id); + } h & defaultTavernChance; h & primarySkillInitial; h & primarySkillLowLevel; @@ -201,7 +239,7 @@ public: for(auto i = 0; i < secSkillProbability.size(); i++) if(secSkillProbability[i] < 0) secSkillProbability[i] = 0; - } + } } EAlignment::EAlignment getAlignment() const; }; @@ -234,18 +272,12 @@ struct DLL_LINKAGE CObstacleInfo } }; -class DLL_LINKAGE CHeroClassHandler : public IHandlerBase +class DLL_LINKAGE CHeroClassHandler : public CHandlerBase { void fillPrimarySkillData(const JsonNode & node, CHeroClass * heroClass, PrimarySkill::PrimarySkill pSkill); - CHeroClass *loadFromJson(const JsonNode & node, const std::string & identifier); public: - std::vector< ConstTransitivePtr > heroClasses; - std::vector loadLegacyData(size_t dataSize) override; - void loadObject(std::string scope, std::string name, const JsonNode & data) override; - void loadObject(std::string scope, std::string name, const JsonNode & data, size_t index) override; - void afterLoadFinalization() override; std::vector getDefaultAllowed() const override; @@ -254,11 +286,16 @@ public: template void serialize(Handler &h, const int version) { - h & heroClasses; + h & objects; } + +protected: + const std::vector & getTypeNames() const override; + CHeroClass * loadFromJson(const std::string & scope, const JsonNode & node, const std::string & identifier, size_t index) override; + }; -class DLL_LINKAGE CHeroHandler : public IHandlerBase +class DLL_LINKAGE CHeroHandler : public CHandlerBase { /// expPerLEvel[i] is amount of exp needed to reach level i; /// consists of 201 values. Any higher levels require experience larger that ui64 can hold @@ -274,14 +311,9 @@ class DLL_LINKAGE CHeroHandler : public IHandlerBase void loadTerrains(); void loadObstacles(); - /// Load single hero from json - CHero * loadFromJson(const JsonNode & node, const std::string & identifier); - public: CHeroClassHandler classes; - std::vector< ConstTransitivePtr > heroes; - //default costs of going through terrains. -1 means terrain is impassable std::vector terrCosts; @@ -324,20 +356,18 @@ public: std::vector getDefaultAllowed() const override; - ///json serialization helper - static si32 decodeHero(const std::string & identifier); - - ///json serialization helper - static std::string encodeHero(const si32 index); - template void serialize(Handler &h, const int version) { h & classes; - h & heroes; + h & objects; h & expPerLevel; h & ballistics; h & terrCosts; h & obstacles; h & absoluteObstacles; } + +protected: + const std::vector & getTypeNames() const override; + CHero * loadFromJson(const std::string & scope, const JsonNode & node, const std::string & identifier, size_t index) override; }; diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index fdfba6656..3dd652e3c 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -21,6 +21,13 @@ set(lib_SRCS battle/SiegeInfo.cpp battle/Unit.cpp + events/ApplyDamage.cpp + events/GameResumed.cpp + events/ObjectVisitEnded.cpp + events/ObjectVisitStarted.cpp + events/PlayerGotTurn.cpp + events/TurnStarted.cpp + filesystem/AdapterLoaders.cpp filesystem/CArchiveLoader.cpp filesystem/CBinaryReader.cpp @@ -92,6 +99,7 @@ set(lib_SRCS serializer/JsonDeserializer.cpp serializer/JsonSerializeFormat.cpp serializer/JsonSerializer.cpp + serializer/JsonUpdater.cpp spells/AbilityCaster.cpp spells/AdventureSpellMechanics.cpp @@ -138,7 +146,9 @@ set(lib_SRCS CHeroHandler.cpp CModHandler.cpp CPathfinder.cpp + CPlayerState.cpp CRandomGenerator.cpp + CScriptingModule.cpp CSkillHandler.cpp CStack.cpp CThreadHelper.cpp @@ -153,6 +163,7 @@ set(lib_SRCS NetPacksLib.cpp StartInfo.cpp ResourceSet.cpp + ScriptHandler.cpp VCMIDirs.cpp VCMI_Lib.cpp @@ -166,11 +177,34 @@ set_source_files_properties(${CMAKE_BINARY_DIR}/Version.cpp set(lib_HEADERS ../include/vstd/CLoggerBase.h + ../Global.h + StdInc.h + ../include/vstd/ContainerUtils.h ../include/vstd/RNG.h ../include/vstd/StringUtils.h - StdInc.h - ../Global.h + + ../include/vcmi/events/ApplyDamage.h + ../include/vcmi/events/Event.h + ../include/vcmi/events/EventBus.h + ../include/vcmi/events/SubscriptionRegistry.h + + ../include/vcmi/scripting/Service.h + + ../include/vcmi/spells/Caster.h + ../include/vcmi/spells/Magic.h + ../include/vcmi/spells/Service.h + ../include/vcmi/spells/Spell.h + + ../include/vcmi/Artifact.h + ../include/vcmi/ArtifactService.h + ../include/vcmi/Creature.h + ../include/vcmi/CreatureService.h + ../include/vcmi/Entity.h + ../include/vcmi/Environment.h + ../include/vcmi/Services.h + + abilities/Ability.h battle/AccessibilityInfo.h battle/BattleAction.h @@ -185,6 +219,7 @@ set(lib_HEADERS battle/CPlayerBattleCallback.h battle/CUnitState.h battle/Destination.h + battle/IBattleInfoCallback.h battle/IBattleState.h battle/IUnitInfo.h battle/ReachabilityInfo.h @@ -192,6 +227,13 @@ set(lib_HEADERS battle/SiegeInfo.h battle/Unit.h + events/ApplyDamage.h + events/GameResumed.h + events/ObjectVisitEnded.h + events/ObjectVisitStarted.h + events/PlayerGotTurn.h + events/TurnStarted.h + filesystem/AdapterLoaders.h filesystem/CArchiveLoader.h filesystem/CBinaryReader.h @@ -264,6 +306,7 @@ set(lib_HEADERS serializer/JsonDeserializer.h serializer/JsonSerializeFormat.h serializer/JsonSerializer.h + serializer/JsonUpdater.h serializer/Cast.h spells/AbilityCaster.h @@ -272,7 +315,6 @@ set(lib_HEADERS spells/BonusCaster.h spells/CSpellHandler.h spells/ISpellMechanics.h - spells/Magic.h spells/Problem.h spells/ProxyCaster.h spells/TargetCondition.h @@ -295,6 +337,7 @@ set(lib_HEADERS spells/effects/Timed.h spells/effects/RemoveObstacle.h spells/effects/Sacrifice.h + AI_Base.h CAndroidVMHelper.h CArtHandler.h @@ -340,6 +383,7 @@ set(lib_HEADERS NetPacksLobby.h PathfinderUtil.h ResourceSet.h + ScriptHandler.h ScopeGuard.h StartInfo.h StringConstants.h diff --git a/lib/CModHandler.cpp b/lib/CModHandler.cpp index b530e870c..7320dfc7d 100644 --- a/lib/CModHandler.cpp +++ b/lib/CModHandler.cpp @@ -24,6 +24,9 @@ #include "IHandlerBase.h" #include "spells/CSpellHandler.h" #include "CSkillHandler.h" +#include "ScriptHandler.h" + +#include CIdentifierStorage::CIdentifierStorage(): state(LOADING) @@ -420,7 +423,7 @@ void CContentHandler::init() handlers.insert(std::make_pair("spells", ContentTypeHandler(VLC->spellh, "spell"))); handlers.insert(std::make_pair("skills", ContentTypeHandler(VLC->skillh, "skill"))); handlers.insert(std::make_pair("templates", ContentTypeHandler((IHandlerBase *)VLC->tplh, "template"))); - + handlers.insert(std::make_pair("scripts", ContentTypeHandler(VLC->scriptHandler, "script"))); //TODO: any other types of moddables? } @@ -729,7 +732,7 @@ std::vector CModHandler::resolveDependencies(std::vector modsT { logMod->error("Mod '%s' will not work: it depends on mod '%s', which is not installed.", mod.name, dependency); res = false; //continue iterations, since we should show all errors for the current mod. - } + } } return res; }; @@ -742,19 +745,19 @@ std::vector CModHandler::resolveDependencies(std::vector modsT brokenMods.push_back(mod); } if(!brokenMods.empty()) - { - vstd::erase_if(modsToResolve, [&](TModID mid) + { + vstd::erase_if(modsToResolve, [&](TModID mid) { return brokenMods.end() != std::find(brokenMods.begin(), brokenMods.end(), mid); }); brokenMods.clear(); - continue; - } + continue; + } break; - } + } boost::range::sort(modsToResolve); return modsToResolve; -} + } std::vector CModHandler::getModList(std::string path) { @@ -950,6 +953,8 @@ void CModHandler::load() for(const TModID & modName : activeMods) content->load(allMods[modName]); + VLC->scriptHandler->performRegistration(VLC);//todo: this should be done before any other handlers load + content->loadCustom(); logMod->info("\tLoading mod data: %d ms", timer.getDiff()); diff --git a/lib/CPathfinder.cpp b/lib/CPathfinder.cpp index 90c487779..74d06eb88 100644 --- a/lib/CPathfinder.cpp +++ b/lib/CPathfinder.cpp @@ -458,7 +458,7 @@ std::vector CPathfinderHelper::getCastleGates(const PathNodeInfo & source) { std::vector allowedExits; - auto towns = getPlayer(hero->tempOwner)->towns; + auto towns = getPlayerState(hero->tempOwner)->towns; for(const auto & town : towns) { if(town->id != source.nodeObject->id && town->visitingHero == nullptr @@ -917,7 +917,7 @@ bool CPathfinder::isDestinationGuardian() const void CPathfinder::initializePatrol() { auto state = PATROL_NONE; - if(hero->patrol.patrolling && !getPlayer(hero->tempOwner)->human) + if(hero->patrol.patrolling && !getPlayerState(hero->tempOwner)->human) { if(hero->patrol.patrolRadius) { diff --git a/lib/CPlayerState.cpp b/lib/CPlayerState.cpp new file mode 100644 index 000000000..78ee6b68a --- /dev/null +++ b/lib/CPlayerState.cpp @@ -0,0 +1,69 @@ +/* + * CPlayerState.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 "CPlayerState.h" +#include "CGameStateFwd.h" + +PlayerState::PlayerState() + : color(-1), human(false), enteredWinningCheatCode(false), + enteredLosingCheatCode(false), status(EPlayerStatus::INGAME) +{ + setNodeType(PLAYER); +} + +PlayerState::PlayerState(PlayerState && other): + CBonusSystemNode(std::move(other)), + color(other.color), + human(other.human), + team(other.team), + resources(other.resources), + enteredWinningCheatCode(other.enteredWinningCheatCode), + enteredLosingCheatCode(other.enteredLosingCheatCode), + status(other.status), + daysWithoutCastle(other.daysWithoutCastle) +{ + std::swap(visitedObjects, other.visitedObjects); + std::swap(heroes, other.heroes); + std::swap(towns, other.towns); + std::swap(availableHeroes, other.availableHeroes); + std::swap(dwellings, other.dwellings); + std::swap(quests, other.quests); +} + +std::string PlayerState::nodeName() const +{ + return "Player " + color.getStrCap(false); +} + +PlayerColor PlayerState::getColor() const +{ + return color; +} + +TeamID PlayerState::getTeam() const +{ + return team; +} + +bool PlayerState::isHuman() const +{ + return human; +} + +const IBonusBearer * PlayerState::accessBonuses() const +{ + return this; +} + +int PlayerState::getResourceAmount(int type) const +{ + return vstd::atOrDefault(resources, static_cast(type), 0); +} diff --git a/lib/CPlayerState.h b/lib/CPlayerState.h index 9aecefa05..adbeb7c82 100644 --- a/lib/CPlayerState.h +++ b/lib/CPlayerState.h @@ -9,13 +9,18 @@ */ #pragma once +#include +#include + #include "HeroBonus.h" +#include "ResourceSet.h" class CGHeroInstance; class CGTownInstance; class CGDwelling; +class QuestInfo; -struct DLL_LINKAGE PlayerState : public CBonusSystemNode +struct DLL_LINKAGE PlayerState : public CBonusSystemNode, public Player { public: PlayerColor color; @@ -38,6 +43,12 @@ public: std::string nodeName() const override; + PlayerColor getColor() const override; + TeamID getTeam() const override; + bool isHuman() const override; + const IBonusBearer * accessBonuses() const override; + int getResourceAmount(int type) const override; + template void serialize(Handler &h, const int version) { h & color; diff --git a/lib/CScriptingModule.cpp b/lib/CScriptingModule.cpp new file mode 100644 index 000000000..e85e3c72e --- /dev/null +++ b/lib/CScriptingModule.cpp @@ -0,0 +1,32 @@ +/* + * CScriptingModule.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 "CScriptingModule.h" + +namespace scripting +{ + +ContextBase::ContextBase(vstd::CLoggerBase * logger_) + : logger(logger_) +{ + +} + +ContextBase::~ContextBase() = default; + + +Module::Module() +{ +} + +Module::~Module() = default; + +} diff --git a/lib/CScriptingModule.h b/lib/CScriptingModule.h index 098b6ef1e..eacf0c599 100644 --- a/lib/CScriptingModule.h +++ b/lib/CScriptingModule.h @@ -9,20 +9,39 @@ */ #pragma once -#include "IGameEventsReceiver.h" +#include -class IGameEventRealizer; -class CPrivilegedInfoCallback; +namespace spells +{ + namespace effects + { + class Registry; + } +} -class CScriptingModule : public IGameEventsReceiver, public IBattleEventsReceiver +namespace scripting +{ + +class DLL_LINKAGE ContextBase : public Context { public: - virtual void executeUserCommand(const std::string &cmd){}; - virtual void init(){}; //called upon the start of game (after map randomization, before first turn) - virtual void giveActionCB(IGameEventRealizer *cb){}; - virtual void giveInfoCB(CPrivilegedInfoCallback *cb){}; - - CScriptingModule(){} - virtual ~CScriptingModule(){} + ContextBase(vstd::CLoggerBase * logger_); + virtual ~ContextBase(); +protected: + vstd::CLoggerBase * logger; }; +class DLL_LINKAGE Module +{ +public: + Module(); + virtual ~Module(); + + virtual std::string compile(const std::string & name, const std::string & source, vstd::CLoggerBase * logger) const = 0; + + virtual std::shared_ptr createContextFor(const Script * source, const Environment * env) const = 0; + + virtual void registerSpellEffect(spells::effects::Registry * registry, const Script * source) const = 0; +}; + +} diff --git a/lib/CSkillHandler.cpp b/lib/CSkillHandler.cpp index 826e085f3..feea6b009 100644 --- a/lib/CSkillHandler.cpp +++ b/lib/CSkillHandler.cpp @@ -42,6 +42,43 @@ CSkill::~CSkill() { } +int32_t CSkill::getIndex() const +{ + return id.num; +} + +int32_t CSkill::getIconIndex() const +{ + return getIndex(); //TODO: actual value with skill level +} + +const std::string & CSkill::getName() const +{ + return name; +} + +const std::string & CSkill::getJsonKey() const +{ + return identifier; +} + +void CSkill::registerIcons(const IconRegistar & cb) const +{ + for(int level = 1; level <= 3; level++) + { + int frame = 2 + level + 3 * id; + const LevelInfo & skillAtLevel = at(level); + cb(frame, "SECSK32", skillAtLevel.iconSmall); + cb(frame, "SECSKILL", skillAtLevel.iconMedium); + cb(frame, "SECSK82", skillAtLevel.iconLarge); + } +} + +SecondarySkill CSkill::getId() const +{ + return id; +} + void CSkill::addNewBonus(const std::shared_ptr & b, int level) { b->source = Bonus::SECONDARY_SKILL; @@ -86,6 +123,17 @@ std::string CSkill::toString() const return ss.str(); } +void CSkill::updateFrom(const JsonNode & data) +{ + +} + +void CSkill::serializeJson(JsonSerializeFormat & handler) +{ + +} + + ///CSkillHandler CSkillHandler::CSkillHandler() { @@ -147,7 +195,7 @@ const std::string & CSkillHandler::skillName(int skill) const return objects[skill]->name; } -CSkill * CSkillHandler::loadFromJson(const JsonNode & json, const std::string & identifier, size_t index) +CSkill * CSkillHandler::loadFromJson(const std::string & scope, const JsonNode & json, const std::string & identifier, size_t index) { CSkill * skill = new CSkill(SecondarySkill((si32)index), identifier); diff --git a/lib/CSkillHandler.h b/lib/CSkillHandler.h index c3a3519c1..425eafc38 100644 --- a/lib/CSkillHandler.h +++ b/lib/CSkillHandler.h @@ -9,11 +9,16 @@ */ #pragma once +#include +#include + #include "../lib/HeroBonus.h" #include "GameConstants.h" #include "IHandlerBase.h" -class DLL_LINKAGE CSkill // secondary skill +class JsonSerializeFormat; + +class DLL_LINKAGE CSkill : public Skill { public: struct LevelInfo @@ -48,6 +53,13 @@ public: CSkill(SecondarySkill id = SecondarySkill::DEFAULT, std::string identifier = "default"); ~CSkill(); + int32_t getIndex() const override; + int32_t getIconIndex() const override; + const std::string & getName() const override; + const std::string & getJsonKey() const override; + void registerIcons(const IconRegistar & cb) const override; + SecondarySkill getId() const override; + const LevelInfo & at(int level) const; LevelInfo & at(int level); @@ -58,6 +70,9 @@ public: std::string name; //as displayed in GUI std::array gainChance; // gainChance[0/1] = default gain chance on level-up for might/magic heroes + void updateFrom(const JsonNode & data); + void serializeJson(JsonSerializeFormat & handler); + template void serialize(Handler & h, const int version) { h & id; @@ -75,7 +90,7 @@ public: friend DLL_LINKAGE std::ostream & operator<<(std::ostream & out, const CSkill::LevelInfo & info); }; -class DLL_LINKAGE CSkillHandler: public CHandlerBase +class DLL_LINKAGE CSkillHandler: public CHandlerBase { public: CSkillHandler(); @@ -87,7 +102,6 @@ public: void beforeValidate(JsonNode & object) override; std::vector getDefaultAllowed() const override; - const std::vector & getTypeNames() const override; const std::string & skillInfo(int skill, int level) const; const std::string & skillName(int skill) const; @@ -103,5 +117,6 @@ public: } protected: - CSkill * loadFromJson(const JsonNode & json, const std::string & identifier, size_t index) override; + const std::vector & getTypeNames() const override; + CSkill * loadFromJson(const std::string & scope, const JsonNode & json, const std::string & identifier, size_t index) override; }; diff --git a/lib/CStack.cpp b/lib/CStack.cpp index 716cbc777..f1d9d3c27 100644 --- a/lib/CStack.cpp +++ b/lib/CStack.cpp @@ -12,6 +12,8 @@ #include +#include + #include "CGeneralTextHandler.h" #include "battle/BattleInfo.h" #include "spells/CSpellHandler.h" @@ -396,7 +398,7 @@ std::string CStack::getDescription() const return nodeName(); } -void CStack::spendMana(const spells::PacketSender * server, const int spellCost) const +void CStack::spendMana(ServerCallback * server, const int spellCost) const { if(spellCost != 1) logGlobal->warn("Unexpected spell cost %d for creature", spellCost); @@ -406,5 +408,5 @@ void CStack::spendMana(const spells::PacketSender * server, const int spellCost) ssp.which = BattleSetStackProperty::CASTS; ssp.val = -spellCost; ssp.absolute = false; - server->sendAndApply(&ssp); + server->apply(&ssp); } diff --git a/lib/CStack.h b/lib/CStack.h index 2ca5244d1..8914aaefc 100644 --- a/lib/CStack.h +++ b/lib/CStack.h @@ -77,7 +77,7 @@ public: bool unitHasAmmoCart(const battle::Unit * unit) const override; PlayerColor unitEffectiveOwner(const battle::Unit * unit) const override; - void spendMana(const spells::PacketSender * server, const int spellCost) const override; + void spendMana(ServerCallback * server, const int spellCost) const override; template void serialize(Handler & h, const int version) { diff --git a/lib/CThreadHelper.h b/lib/CThreadHelper.h index 1594d4021..78205de22 100644 --- a/lib/CThreadHelper.h +++ b/lib/CThreadHelper.h @@ -10,25 +10,74 @@ #pragma once -typedef std::function Task; - +///DEPRECATED /// Can assign CPU work to other threads/cores class DLL_LINKAGE CThreadHelper { +public: + typedef std::function Task; + CThreadHelper(std::vector > *Tasks, int Threads); + void run(); +private: boost::mutex rtinm; int currentTask, amount, threads; std::vector *tasks; void processTasks(); -public: - CThreadHelper(std::vector > *Tasks, int Threads); - void run(); }; -template inline void setData(T * data, std::function func) +template +class ThreadPool { - *data = func(); -} +public: + using Task = std::function)>; + using Tasks = std::vector; + + ThreadPool(Tasks * tasks_, std::vector> context_) + : currentTask(0), + amount(tasks_->size()), + threads(context_.size()), + tasks(tasks_), + context(context_) + {} + + void run() + { + boost::thread_group grupa; + for(size_t i=0; i payload = context.at(i); + + grupa.create_thread(std::bind(&ThreadPool::processTasks, this, payload)); + } + + grupa.join_all(); + + //thread group deletes threads, do not free manually + } +private: + boost::mutex rtinm; + size_t currentTask, amount, threads; + Tasks * tasks; + std::vector> context; + + void processTasks(std::shared_ptr payload) + { + while(true) + { + size_t pom; + { + boost::unique_lock lock(rtinm); + if((pom = currentTask) >= amount) + break; + else + ++currentTask; + } + (*tasks)[pom](payload); + } + } +}; + void DLL_LINKAGE setThreadName(const std::string &name); diff --git a/lib/CTownHandler.cpp b/lib/CTownHandler.cpp index af44b6ab1..902a84c28 100644 --- a/lib/CTownHandler.cpp +++ b/lib/CTownHandler.cpp @@ -199,10 +199,10 @@ void CBuilding::update794() auto scope = town->getBuildingScope(); - for(auto b : overriddenBids.Vector()) - { - auto bid = BuildingID(VLC->modh->identifiers.getIdentifier(scope, b).get()); - overrideBids.insert(bid); + for(auto b : overriddenBids.Vector()) + { + auto bid = BuildingID(VLC->modh->identifiers.getIdentifier(scope, b).get()); + overrideBids.insert(bid); } } @@ -218,6 +218,64 @@ CFaction::~CFaction() delete town; } +int32_t CFaction::getIndex() const +{ + return index; +} + +int32_t CFaction::getIconIndex() const +{ + return index; //??? +} + +const std::string & CFaction::getName() const +{ + return name; +} + +const std::string & CFaction::getJsonKey() const +{ + return identifier; +} + +void CFaction::registerIcons(const IconRegistar & cb) const +{ + if(town) + { + auto & info = town->clientInfo; + cb(info.icons[0][0], "ITPT", info.iconLarge[0][0]); + cb(info.icons[0][1], "ITPT", info.iconLarge[0][1]); + cb(info.icons[1][0], "ITPT", info.iconLarge[1][0]); + cb(info.icons[1][1], "ITPT", info.iconLarge[1][1]); + + cb(info.icons[0][0] + 2, "ITPA", info.iconSmall[0][0]); + cb(info.icons[0][1] + 2, "ITPA", info.iconSmall[0][1]); + cb(info.icons[1][0] + 2, "ITPA", info.iconSmall[1][0]); + cb(info.icons[1][1] + 2, "ITPA", info.iconSmall[1][1]); + } +} + +FactionID CFaction::getId() const +{ + return FactionID(index); +} + +bool CFaction::hasTown() const +{ + return town != nullptr; +} + +void CFaction::updateFrom(const JsonNode & data) +{ + +} + +void CFaction::serializeJson(JsonSerializeFormat & handler) +{ + +} + + CTown::CTown() : faction(nullptr), mageLevel(0), primaryRes(0), moatDamage(0), defaultTavernChance(0) { @@ -295,17 +353,12 @@ void CTown::setGreeting(BuildingSubID::EBuildingSubID subID, const std::string m CTownHandler::CTownHandler() { - VLC->townh = this; - randomTown = new CTown(); } CTownHandler::~CTownHandler() { delete randomTown; - - for(auto faction : factions) - faction.dellNull(); } JsonNode readBuilding(CLegacyConfigParser & parser) @@ -329,7 +382,7 @@ TPropagatorPtr CTownHandler::emptyPropagator = std::make_shared CTownHandler::loadLegacyData(size_t dataSize) { std::vector dest(dataSize); - factions.resize(dataSize); + objects.resize(dataSize); auto getBuild = [&](size_t town, size_t building) -> JsonNode & { @@ -507,22 +560,22 @@ R CTownHandler::getMappedValue(const K key, const R defval, const std::map template R CTownHandler::getMappedValue(const JsonNode & node, const R defval, const std::map & map, bool required) -{ + { if(!node.isNull() && node.getType() == JsonNode::JsonType::DATA_STRING) return getMappedValue(node.String(), defval, map, required); return defval; } void CTownHandler::addBonusesForVanilaBuilding(CBuilding * building) -{ + { std::shared_ptr b; static TPropagatorPtr playerPropagator = std::make_shared(CBonusSystemNode::ENodeTypes::PLAYER); if(building->subId == BuildingSubID::NONE) - { + { if(building->bid == BuildingID::TAVERN) b = createBonus(building, Bonus::MORALE, +1); - else if(building->bid == BuildingID::GRAIL + else if(building->bid == BuildingID::GRAIL && building->town->faction != nullptr && boost::algorithm::ends_with(building->town->faction->identifier, ":cove")) { @@ -600,7 +653,7 @@ void CTownHandler::loadSpecialBuildingBonuses(const JsonNode & source, BonusList limPtr->faction = building->town->faction->index; } //JsonUtils::parseBuildingBonus produces UNKNOWN type propagator instead of empty. - if(bonus->propagator != nullptr + if(bonus->propagator != nullptr && bonus->propagator->getPropagatorType() == CBonusSystemNode::ENodeTypes::UNKNOWN) bonus->addPropagator(emptyPropagator); building->addNewBonus(bonus, bonusList); @@ -625,8 +678,8 @@ void CTownHandler::loadBuilding(CTown * town, const std::string & stringID, cons ret->subId = getMappedValue(source["type"], BuildingSubID::NONE, MappedKeys::SPECIAL_BUILDINGS); ret->height = CBuilding::HEIGHT_NO_TOWER; - if(ret->subId == BuildingSubID::LOOKOUT_TOWER - || ret->bid == BuildingID::GRAIL) + if(ret->subId == BuildingSubID::LOOKOUT_TOWER + || ret->bid == BuildingID::GRAIL) ret->height = getMappedValue(source["height"], CBuilding::HEIGHT_NO_TOWER, CBuilding::TOWER_TYPES); ret->identifier = stringID; @@ -707,7 +760,7 @@ void CTownHandler::loadBuilding(CTown * town, const std::string & stringID, cons ret->town->buildings[ret->bid] = ret; - VLC->modh->identifiers.registerObject(source.meta, ret->town->getBuildingScope(), ret->identifier, ret->bid); + registerObject(source.meta, ret->town->getBuildingScope(), ret->identifier, ret->bid); } void CTownHandler::loadBuildings(CTown * town, const JsonNode & source) @@ -816,10 +869,10 @@ void CTownHandler::loadSiegeScreen(CTown &town, const JsonNode & source) VLC->modh->identifiers.requestIdentifier("creature", source["shooter"], [&town](si32 creature) { auto crId = CreatureID(creature); - if(!VLC->creh->creatures[crId]->animation.missleFrameAngles.size()) + if(!(*VLC->creh)[crId]->animation.missleFrameAngles.size()) logMod->error("Mod '%s' error: Creature '%s' on the Archer's tower is not a shooter. Mod should be fixed. Siege will not work properly!" , town.faction->name - , VLC->creh->creatures[crId]->nameSing); + , (*VLC->creh)[crId]->nameSing); town.clientInfo.siegeShooter = crId; }); @@ -952,7 +1005,7 @@ void CTownHandler::loadTown(CTown * town, const JsonNode & source) VLC->modh->identifiers.requestIdentifier(node.second.meta, "heroClass",node.first, [=](si32 classID) { - VLC->heroh->classes.heroClasses[classID]->selectionProbability[town->faction->index] = chance; + VLC->heroh->classes[HeroClassID(classID)]->selectionProbability[town->faction->index] = chance; }); } @@ -1018,11 +1071,12 @@ ETerrainType::EETerrainType CTownHandler::getDefaultTerrainForAlignment(EAlignme return terrain; } -CFaction * CTownHandler::loadFromJson(const JsonNode &source, const std::string & identifier, TFaction index) +CFaction * CTownHandler::loadFromJson(const std::string & scope, const JsonNode & source, const std::string & identifier, size_t index) { - auto faction = new CFaction(); + auto faction = new CFaction(); faction->index = index; + faction->index = static_cast(index); faction->name = source["name"].String(); faction->identifier = identifier; @@ -1062,10 +1116,9 @@ CFaction * CTownHandler::loadFromJson(const JsonNode &source, const std::string void CTownHandler::loadObject(std::string scope, std::string name, const JsonNode & data) { - auto index = static_cast(factions.size()); - auto object = loadFromJson(data, normalizeIdentifier(scope, "core", name), index); + auto object = loadFromJson(scope, data, normalizeIdentifier(scope, "core", name), objects.size()); - factions.push_back(object); + objects.push_back(object); if (object->town) { @@ -1097,18 +1150,18 @@ void CTownHandler::loadObject(std::string scope, std::string name, const JsonNod }); } - VLC->modh->identifiers.registerObject(scope, "faction", name, object->index); + registerObject(scope, "faction", name, object->index); } void CTownHandler::loadObject(std::string scope, std::string name, const JsonNode & data, size_t index) { - auto object = loadFromJson(data, normalizeIdentifier(scope, "core", name), static_cast(index)); - - if (factions.size() > index) - assert(factions[index] == nullptr); // ensure that this id was not loaded before + auto object = loadFromJson(scope, data, normalizeIdentifier(scope, "core", name), index); + + if (objects.size() > index) + assert(objects[index] == nullptr); // ensure that this id was not loaded before else - factions.resize(index + 1); - factions[index] = object; + objects.resize(index + 1); + objects[index] = object; if (object->town) { @@ -1128,7 +1181,7 @@ void CTownHandler::loadObject(std::string scope, std::string name, const JsonNod }); } - VLC->modh->identifiers.registerObject(scope, "faction", name, object->index); + registerObject(scope, "faction", name, object->index); } void CTownHandler::loadRandomFaction() @@ -1142,7 +1195,7 @@ void CTownHandler::loadRandomFaction() void CTownHandler::loadCustom() { - loadRandomFaction(); + loadRandomFaction(); } void CTownHandler::afterLoadFinalization() @@ -1211,7 +1264,7 @@ void CTownHandler::initializeWarMachines() std::vector CTownHandler::getDefaultAllowed() const { std::vector allowedFactions; - for(auto town : factions) + for(auto town : objects) { allowedFactions.push_back(town->town != nullptr); } @@ -1225,7 +1278,7 @@ std::set CTownHandler::getAllowedFactions(bool withTown) const if (withTown) allowed = getDefaultAllowed(); else - allowed.resize( factions.size(), true); + allowed.resize( objects.size(), true); for (size_t i=0; i CTownHandler::getAllowedFactions(bool withTown) const return allowedFactions; } -si32 CTownHandler::decodeFaction(const std::string & identifier) +const std::vector & CTownHandler::getTypeNames() const { - auto rawId = VLC->modh->identifiers.getIdentifier("core", "faction", identifier); - if(rawId) - return rawId.get(); - else - return -1; + static const std::vector typeNames = { "faction", "town" }; + return typeNames; } -std::string CTownHandler::encodeFaction(const si32 index) -{ - return VLC->townh->factions[index]->identifier; -} diff --git a/lib/CTownHandler.h b/lib/CTownHandler.h index 539b0261d..a77fad63e 100644 --- a/lib/CTownHandler.h +++ b/lib/CTownHandler.h @@ -9,6 +9,9 @@ */ #pragma once +#include +#include + #include "ConstTransitivePtr.h" #include "ResourceSet.h" #include "int3.h" @@ -23,6 +26,7 @@ class JsonNode; class CTown; class CFaction; struct BattleHex; +class JsonSerializeFormat; /// a typical building encountered in every castle ;] /// this is structure available to both client and server @@ -193,12 +197,9 @@ struct DLL_LINKAGE SPuzzleInfo } }; -class DLL_LINKAGE CFaction +class DLL_LINKAGE CFaction : public Faction { public: - CFaction(); - ~CFaction(); - std::string name; //town name, by default - from TownName.txt std::string identifier; @@ -212,10 +213,23 @@ public: std::string creatureBg120; std::string creatureBg130; - - std::vector puzzleMap; + CFaction(); + ~CFaction(); + + int32_t getIndex() const override; + int32_t getIconIndex() const override; + const std::string & getName() const override; + const std::string & getJsonKey() const override; + void registerIcons(const IconRegistar & cb) const override; + FactionID getId() const override; + + bool hasTown() const override; + + void updateFrom(const JsonNode & data); + void serializeJson(JsonSerializeFormat & handler); + template void serialize(Handler &h, const int version) { h & name; @@ -357,7 +371,7 @@ private: mutable std::map specialMessages; //may be changed by CGTownBuilding::getVisitingBonusGreeting() const }; -class DLL_LINKAGE CTownHandler : public IHandlerBase +class DLL_LINKAGE CTownHandler : public CHandlerBase { struct BuildingRequirementsHelper { @@ -404,22 +418,18 @@ class DLL_LINKAGE CTownHandler : public IHandlerBase void loadPuzzle(CFaction & faction, const JsonNode & source); ETerrainType::EETerrainType getDefaultTerrainForAlignment(EAlignment::EAlignment aligment) const; - - CFaction * loadFromJson(const JsonNode & data, const std::string & identifier, TFaction index); - void loadRandomFaction(); + public: template static R getMappedValue(const K key, const R defval, const std::map & map, bool required = true); template static R getMappedValue(const JsonNode & node, const R defval, const std::map & map, bool required = true); - std::vector > factions; - CTown * randomTown; - CTownHandler(); //c-tor, set pointer in VLC to this + CTownHandler(); ~CTownHandler(); std::vector loadLegacyData(size_t dataSize) override; @@ -434,16 +444,11 @@ public: std::vector getDefaultAllowed() const override; std::set getAllowedFactions(bool withTown = true) const; - //json serialization helper - static si32 decodeFaction(const std::string & identifier); - - //json serialization helper - static std::string encodeFaction(const si32 index); static void loadSpecialBuildingBonuses(const JsonNode & source, BonusList & bonusList, CBuilding * building); template void serialize(Handler &h, const int version) { - h & factions; + h & objects; if(version >= 770) { @@ -454,4 +459,8 @@ public: loadRandomFaction(); } } + +protected: + const std::vector & getTypeNames() const override; + CFaction * loadFromJson(const std::string & scope, const JsonNode & data, const std::string & identifier, size_t index) override; }; diff --git a/lib/GameConstants.cpp b/lib/GameConstants.cpp index 1290dd08d..ff39e5d82 100644 --- a/lib/GameConstants.cpp +++ b/lib/GameConstants.cpp @@ -15,16 +15,25 @@ #ifndef VCMI_NO_EXTRA_VERSION #include "../Version.h" #endif +#include +#include +#include +#include +#include +#include + +#include +#include #include "VCMI_Lib.h" -#include "mapObjects/CObjectClassesHandler.h" -#include "CArtHandler.h" -#include "CCreatureHandler.h" -#include "spells/CSpellHandler.h" -#include "CSkillHandler.h" +#include "mapObjects/CObjectClassesHandler.h"//todo: remove +#include "CArtHandler.h"//todo: remove +#include "CCreatureHandler.h"//todo: remove +#include "spells/CSpellHandler.h" //todo: remove +#include "CSkillHandler.h"//todo: remove #include "StringConstants.h" #include "CGeneralTextHandler.h" -#include "CModHandler.h" +#include "CModHandler.h"//todo: remove const SlotID SlotID::COMMANDER_SLOT_PLACEHOLDER = SlotID(-2); const SlotID SlotID::SUMMONED_SLOT_PLACEHOLDER = SlotID(-3); @@ -47,9 +56,28 @@ namespace GameConstants #endif } +si32 HeroTypeID::decode(const std::string & identifier) +{ + auto rawId = VLC->modh->identifiers.getIdentifier("core", "hero", identifier); + if(rawId) + return rawId.get(); + else + return -1; +} + +std::string HeroTypeID::encode(const si32 index) +{ + return VLC->heroTypes()->getByIndex(index)->getJsonKey(); +} + const CArtifact * ArtifactID::toArtifact() const { - return VLC->arth->artifacts.at(*this); + return VLC->arth->objects.at(*this); +} + +const Artifact * ArtifactID::toArtifact(const ArtifactService * service) const +{ + return service->getById(*this); } si32 ArtifactID::decode(const std::string & identifier) @@ -63,12 +91,17 @@ si32 ArtifactID::decode(const std::string & identifier) std::string ArtifactID::encode(const si32 index) { - return VLC->arth->artifacts.at(index)->identifier; + return VLC->artifacts()->getByIndex(index)->getJsonKey(); } const CCreature * CreatureID::toCreature() const { - return VLC->creh->creatures.at(*this); + return VLC->creh->objects.at(*this); +} + +const Creature * CreatureID::toCreature(const CreatureService * creatures) const +{ + return creatures->getById(*this); } si32 CreatureID::decode(const std::string & identifier) @@ -82,7 +115,7 @@ si32 CreatureID::decode(const std::string & identifier) std::string CreatureID::encode(const si32 index) { - return VLC->creh->creatures.at(index)->identifier; + return VLC->creatures()->getById(CreatureID(index))->getJsonKey(); } const CSpell * SpellID::toSpell() const @@ -95,6 +128,11 @@ const CSpell * SpellID::toSpell() const return VLC->spellh->objects[*this]; } +const spells::Spell * SpellID::toSpell(const spells::Service * service) const +{ + return service->getById(*this); +} + si32 SpellID::decode(const std::string & identifier) { auto rawId = VLC->modh->identifiers.getIdentifier("core", "spell", identifier); @@ -106,17 +144,9 @@ si32 SpellID::decode(const std::string & identifier) std::string SpellID::encode(const si32 index) { - return VLC->spellh->objects.at(index)->identifier; + return VLC->spells()->getByIndex(index)->getJsonKey(); } -const CSkill * SecondarySkill::toSkill() const -{ - return VLC->skillh->objects.at(*this); -} - -//template std::ostream & operator << (std::ostream & os, BaseForID id); -//template std::ostream & operator << (std::ostream & os, BaseForID id); - bool PlayerColor::isValidPlayer() const { return num < PLAYER_LIMIT_I; @@ -153,6 +183,32 @@ std::string PlayerColor::getStrCap(bool L10n) const return ret; } +const FactionID FactionID::ANY = FactionID(-1); +const FactionID FactionID::CASTLE = FactionID(0); +const FactionID FactionID::RAMPART = FactionID(1); +const FactionID FactionID::TOWER = FactionID(2); +const FactionID FactionID::INFERNO = FactionID(3); +const FactionID FactionID::NECROPOLIS = FactionID(4); +const FactionID FactionID::DUNGEON = FactionID(5); +const FactionID FactionID::STRONGHOLD = FactionID(6); +const FactionID FactionID::FORTRESS = FactionID(7); +const FactionID FactionID::CONFLUX = FactionID(8); +const FactionID FactionID::NEUTRAL = FactionID(9); + +si32 FactionID::decode(const std::string & identifier) +{ + auto rawId = VLC->modh->identifiers.getIdentifier("core", "faction", identifier); + if(rawId) + return rawId.get(); + else + return -1; +} + +std::string FactionID::encode(const si32 index) +{ + return VLC->factions()->getByIndex(index)->getJsonKey(); +} + std::ostream & operator<<(std::ostream & os, const EActionType actionType) { static const std::map actionTypeToString = diff --git a/lib/GameConstants.h b/lib/GameConstants.h index 67fb55ff9..a15c9e04b 100644 --- a/lib/GameConstants.h +++ b/lib/GameConstants.h @@ -11,6 +11,29 @@ #include "ConstTransitivePtr.h" +class Artifact; +class ArtifactService; +class Creature; +class CreatureService; + +namespace spells +{ + class Spell; + class Service; +} + +class CArtifact; +class CArtifactInstance; +class CCreature; +class CHero; +class CSpell; +class CSkill; +class CGameInfoCallback; +class CNonConstInfoCallback; + +struct IdTag +{}; + namespace GameConstants { DLL_LINKAGE extern const std::string VCMI_VERSION; @@ -55,15 +78,6 @@ namespace GameConstants const std::array POSSIBLE_TURNTIME = {1, 2, 4, 6, 8, 10, 15, 20, 25, 30, 0}; } -class CArtifact; -class CArtifactInstance; -class CCreature; -class CHero; -class CSpell; -class CSkill; -class CGameInfoCallback; -class CNonConstInfoCallback; - #define ID_LIKE_CLASS_COMMON(CLASS_NAME, ENUM_NAME) \ CLASS_NAME(const CLASS_NAME & other) \ { \ @@ -81,6 +95,10 @@ operator ENUM_NAME() const \ { \ return num; \ } \ +si32 getNum() const \ +{ \ + return static_cast(num); \ +} \ ENUM_NAME toEnum() const \ { \ return num; \ @@ -152,15 +170,23 @@ explicit CLASS_NAME(si32 id) \ {} template < typename Derived, typename NumericType> -class BaseForID +class BaseForID : public IdTag { protected: NumericType num; + public: NumericType getNum() const { return num; } + + //to make it more similar to IDLIKE + NumericType toEnum() const + { + return num; + } + template void serialize(Handler &h, const int version) { h & num; @@ -223,9 +249,18 @@ class ObjectInstanceID : public BaseForID friend class CNonConstInfoCallback; }; +class HeroClassID : public BaseForID +{ + INSTID_LIKE_CLASS_COMMON(HeroClassID, si32) +}; + class HeroTypeID : public BaseForID { INSTID_LIKE_CLASS_COMMON(HeroTypeID, si32) + + ///json serialization helpers + static si32 decode(const std::string & identifier); + static std::string encode(const si32 index); }; class SlotID : public BaseForID @@ -319,8 +354,6 @@ public: SecondarySkill(ESecondarySkill _num = WRONG) : num(_num) {} - DLL_LINKAGE const CSkill * toSkill() const; - ID_LIKE_CLASS_COMMON(SecondarySkill, ESecondarySkill) ESecondarySkill num; @@ -333,7 +366,7 @@ namespace EAlignment enum EAlignment { GOOD, EVIL, NEUTRAL }; } -namespace ETownType +namespace ETownType//deprecated { enum ETownType { @@ -342,6 +375,28 @@ namespace ETownType }; } +class FactionID : public BaseForID +{ + INSTID_LIKE_CLASS_COMMON(FactionID, si32) + + DLL_LINKAGE static const FactionID ANY; + DLL_LINKAGE static const FactionID CASTLE; + DLL_LINKAGE static const FactionID RAMPART; + DLL_LINKAGE static const FactionID TOWER; + DLL_LINKAGE static const FactionID INFERNO; + DLL_LINKAGE static const FactionID NECROPOLIS; + DLL_LINKAGE static const FactionID DUNGEON; + DLL_LINKAGE static const FactionID STRONGHOLD; + DLL_LINKAGE static const FactionID FORTRESS; + DLL_LINKAGE static const FactionID CONFLUX; + DLL_LINKAGE static const FactionID NEUTRAL; + + ///json serialization helpers + static si32 decode(const std::string & identifier); + static std::string encode(const si32 index); +}; + + class BuildingID { public: @@ -1028,6 +1083,7 @@ public: {} DLL_LINKAGE const CArtifact * toArtifact() const; + DLL_LINKAGE const Artifact * toArtifact(const ArtifactService * service) const; ///json serialization helpers static si32 decode(const std::string & identifier); @@ -1078,6 +1134,7 @@ public: {} DLL_LINKAGE const CCreature * toCreature() const; + DLL_LINKAGE const Creature * toCreature(const CreatureService * creatures) const; ID_LIKE_CLASS_COMMON(CreatureID, ECreatureID) @@ -1124,7 +1181,8 @@ public: SpellID(ESpellID _num = NONE) : num(_num) {} - DLL_LINKAGE const CSpell * toSpell() const; + DLL_LINKAGE const CSpell * toSpell() const; //deprecated + DLL_LINKAGE const spells::Spell * toSpell(const spells::Service * service) const; ID_LIKE_CLASS_COMMON(SpellID, ESpellID) diff --git a/lib/HeroBonus.cpp b/lib/HeroBonus.cpp index 3d6e03f24..24d3eaab0 100644 --- a/lib/HeroBonus.cpp +++ b/lib/HeroBonus.cpp @@ -21,6 +21,7 @@ #include "CSkillHandler.h" #include "CStack.h" #include "CArtHandler.h" +#include "CModHandler.h" #include "StringConstants.h" #include "battle/BattleInfo.h" @@ -752,7 +753,7 @@ int IBonusBearer::getAttack(bool ranged) const return getBonuses(selector, nullptr, cachingStr)->totalValue(); } -int IBonusBearer::getDefence(bool ranged) const +int IBonusBearer::getDefense(bool ranged) const { const std::string cachingStr = "type_PRIMARY_SKILLs_DEFENSE"; @@ -1328,7 +1329,12 @@ void CBonusSystemNode::setNodeType(CBonusSystemNode::ENodeTypes type) nodeType = type; } -BonusList& CBonusSystemNode::getExportedBonusList() +BonusList & CBonusSystemNode::getExportedBonusList() +{ + return exportedBonuses; +} + +const BonusList & CBonusSystemNode::getExportedBonusList() const { return exportedBonuses; } @@ -1421,19 +1427,19 @@ std::string Bonus::Description() const switch(source) { case ARTIFACT: - str << VLC->arth->artifacts[sid]->Name(); + str << ArtifactID(sid).toArtifact(VLC->artifacts())->getName(); break; case SPELL_EFFECT: - str << SpellID(sid).toSpell()->name; + str << SpellID(sid).toSpell(VLC->spells())->getName(); break; case CREATURE_ABILITY: - str << VLC->creh->creatures[sid]->namePl; + str << VLC->creh->objects[sid]->namePl; break; case SECONDARY_SKILL: str << VLC->skillh->skillName(sid); break; case HERO_SPECIAL: - str << VLC->heroh->heroes[sid]->name; + str << VLC->heroh->objects[sid]->name; break; default: //todo: handle all possible sources @@ -1470,10 +1476,10 @@ JsonNode subtypeToJson(Bonus::BonusType type, int subtype) case Bonus::SPECIAL_PECULIAR_ENCHANT: case Bonus::SPECIAL_ADD_VALUE_ENCHANT: case Bonus::SPECIAL_FIXED_VALUE_ENCHANT: - return JsonUtils::stringNode("spell." + (*VLC->spellh)[SpellID::ESpellID(subtype)]->identifier); + return JsonUtils::stringNode(CModHandler::makeFullIdentifier("", "spell", SpellID::encode(subtype))); case Bonus::IMPROVED_NECROMANCY: case Bonus::SPECIAL_UPGRADE: - return JsonUtils::stringNode("creature." + CreatureID::encode(subtype)); + return JsonUtils::stringNode(CModHandler::makeFullIdentifier("", "creature", CreatureID::encode(subtype))); case Bonus::GENERATE_RESOURCE: return JsonUtils::stringNode("resource." + GameConstants::RESOURCE_NAMES[subtype]); default: @@ -1486,7 +1492,7 @@ JsonNode additionalInfoToJson(Bonus::BonusType type, CAddInfo addInfo) switch(type) { case Bonus::SPECIAL_UPGRADE: - return JsonUtils::stringNode("creature." + CreatureID::encode(addInfo[0])); + return JsonUtils::stringNode(CModHandler::makeFullIdentifier("", "creature", CreatureID::encode(addInfo[0]))); default: return addInfo.toJsonNode(); } @@ -1522,6 +1528,22 @@ JsonNode Bonus::toJsonNode() const root["subtype"] = subtypeToJson(type, subtype); if(additionalInfo != CAddInfo::NONE) root["addInfo"] = additionalInfoToJson(type, additionalInfo); + if(duration != 0) + { + JsonNode durationVec(JsonNode::JsonType::DATA_VECTOR); + for(auto & kv : bonusDurationMap) + { + if(duration & kv.second) + durationVec.Vector().push_back(JsonUtils::stringNode(kv.first)); + } + root["duration"] = durationVec; + } + if(turnsRemain != 0) + root["turns"].Integer() = turnsRemain; + if(source != OTHER) + root["source"].String() = vstd::findKey(bonusSourceMap, source); + if(sid != 0) + root["sourceID"].Integer() = sid; if(val != 0) root["val"].Integer() = val; if(valType != ADDITIVE_VALUE) @@ -1822,8 +1844,8 @@ int CCreatureTypeLimiter::limit(const BonusLimitationContext &context) const //drop bonus if it's not our creature and (we don`t check upgrades or its not our upgrade) } -CCreatureTypeLimiter::CCreatureTypeLimiter(const CCreature &Creature, bool IncludeUpgrades) - :creature(&Creature), includeUpgrades(IncludeUpgrades) +CCreatureTypeLimiter::CCreatureTypeLimiter(const CCreature & creature_, bool IncludeUpgrades) + : creature(&creature_), includeUpgrades(IncludeUpgrades) { } @@ -1835,7 +1857,7 @@ CCreatureTypeLimiter::CCreatureTypeLimiter() void CCreatureTypeLimiter::setCreature (CreatureID id) { - creature = VLC->creh->creatures[id]; + creature = VLC->creh->objects[id]; } std::string CCreatureTypeLimiter::toString() const @@ -2011,7 +2033,7 @@ int CreatureFactionLimiter::limit(const BonusLimitationContext &context) const std::string CreatureFactionLimiter::toString() const { boost::format fmt("CreatureFactionLimiter(faction=%s)"); - fmt % VLC->townh->factions[faction]->identifier; + fmt % VLC->factions()->getByIndex(faction)->getJsonKey(); return fmt.str(); } @@ -2020,7 +2042,7 @@ JsonNode CreatureFactionLimiter::toJsonNode() const JsonNode root(JsonNode::JsonType::DATA_STRUCT); root["type"].String() = "CREATURE_FACTION_LIMITER"; - root["parameters"].Vector().push_back(JsonUtils::stringNode(VLC->townh->factions[faction]->identifier)); + root["parameters"].Vector().push_back(JsonUtils::stringNode(VLC->factions()->getByIndex(faction)->getJsonKey())); return root; } diff --git a/lib/HeroBonus.h b/lib/HeroBonus.h index 6f0c37aaa..d47971302 100644 --- a/lib/HeroBonus.h +++ b/lib/HeroBonus.h @@ -555,8 +555,8 @@ public: void clear(); bool empty() const { return bonuses.empty(); } void resize(TInternalContainer::size_type sz, std::shared_ptr c = nullptr ); - std::shared_ptr &operator[] (TInternalContainer::size_type n) { return bonuses[n]; } - const std::shared_ptr &operator[] (TInternalContainer::size_type n) const { return bonuses[n]; } + STRONG_INLINE std::shared_ptr &operator[] (TInternalContainer::size_type n) { return bonuses[n]; } + STRONG_INLINE const std::shared_ptr &operator[] (TInternalContainer::size_type n) const { return bonuses[n]; } std::shared_ptr &back() { return bonuses.back(); } std::shared_ptr &front() { return bonuses.front(); } const std::shared_ptr &back() const { return bonuses.back(); } @@ -710,7 +710,7 @@ public: virtual int getMinDamage(bool ranged) const; virtual int getMaxDamage(bool ranged) const; virtual int getAttack(bool ranged) const; - virtual int getDefence(bool ranged) const; + virtual int getDefense(bool ranged) const; int MoraleVal() const; //range [-3, +3] int LuckVal() const; //range [-3, +3] @@ -815,7 +815,8 @@ public: void exportBonuses(); const BonusList &getBonusList() const; - BonusList &getExportedBonusList(); + BonusList & getExportedBonusList(); + const BonusList & getExportedBonusList() const; CBonusSystemNode::ENodeTypes getNodeType() const; void setNodeType(CBonusSystemNode::ENodeTypes type); const TNodesVector &getParentNodes() const; @@ -1012,7 +1013,7 @@ public: bool includeUpgrades; CCreatureTypeLimiter(); - CCreatureTypeLimiter(const CCreature &Creature, bool IncludeUpgrades = true); + CCreatureTypeLimiter(const CCreature & creature_, bool IncludeUpgrades = true); void setCreature (CreatureID id); int limit(const BonusLimitationContext &context) const override; diff --git a/lib/IGameCallback.cpp b/lib/IGameCallback.cpp index 25aa67a27..2cc8482c2 100644 --- a/lib/IGameCallback.cpp +++ b/lib/IGameCallback.cpp @@ -29,6 +29,7 @@ #include "mapping/CMap.h" #include "CPlayerState.h" #include "CSkillHandler.h" +#include "ScriptHandler.h" #include "serializer/Connection.h" @@ -126,11 +127,11 @@ void CPrivilegedInfoCallback::getAllTiles(std::unordered_set & void CPrivilegedInfoCallback::pickAllowedArtsSet(std::vector & out, CRandomGenerator & rand) { for (int j = 0; j < 3 ; j++) - out.push_back(VLC->arth->artifacts[VLC->arth->pickRandomArtifact(rand, CArtifact::ART_TREASURE)]); + out.push_back(VLC->arth->objects[VLC->arth->pickRandomArtifact(rand, CArtifact::ART_TREASURE)]); for (int j = 0; j < 3 ; j++) - out.push_back(VLC->arth->artifacts[VLC->arth->pickRandomArtifact(rand, CArtifact::ART_MINOR)]); + out.push_back(VLC->arth->objects[VLC->arth->pickRandomArtifact(rand, CArtifact::ART_MINOR)]); - out.push_back(VLC->arth->artifacts[VLC->arth->pickRandomArtifact(rand, CArtifact::ART_MAJOR)]); + out.push_back(VLC->arth->objects[VLC->arth->pickRandomArtifact(rand, CArtifact::ART_MAJOR)]); } void CPrivilegedInfoCallback::getAllowedSpells(std::vector & out, ui16 level) @@ -138,10 +139,10 @@ void CPrivilegedInfoCallback::getAllowedSpells(std::vector & out, ui16 for (ui32 i = 0; i < gs->map->allowedSpell.size(); i++) //spellh size appears to be greater (?) { - const CSpell *spell = SpellID(i).toSpell(); - if (isAllowed (0, spell->id) && spell->level == level) + const spells::Spell * spell = SpellID(i).toSpell(); + if(isAllowed(0, spell->getIndex()) && spell->getLevel() == level) { - out.push_back(spell->id); + out.push_back(spell->getId()); } } } @@ -220,9 +221,9 @@ TeamState *CNonConstInfoCallback::getPlayerTeam(PlayerColor color) return const_cast(CGameInfoCallback::getPlayerTeam(color)); } -PlayerState * CNonConstInfoCallback::getPlayer( PlayerColor color, bool verbose ) +PlayerState * CNonConstInfoCallback::getPlayerState( PlayerColor color, bool verbose ) { - return const_cast(CGameInfoCallback::getPlayer(color, verbose)); + return const_cast(CGameInfoCallback::getPlayerState(color, verbose)); } CArtifactInstance * CNonConstInfoCallback::getArtInstance( ArtifactInstanceID aid ) @@ -240,27 +241,9 @@ CArmedInstance * CNonConstInfoCallback::getArmyInstance(ObjectInstanceID oid) return dynamic_cast(getObjInstance(oid)); } -const CGObjectInstance * IGameCallback::putNewObject(Obj ID, int subID, int3 pos) -{ - NewObject no; - no.ID = ID; //creature - no.subID= subID; - no.pos = pos; - commitPackage(&no); - return getObj(no.id); //id field will be filled during applying on gs -} - -const CGCreature * IGameCallback::putNewMonster(CreatureID creID, int count, int3 pos) -{ - const CGObjectInstance *m = putNewObject(Obj::MONSTER, creID, pos); - setObjProperty(m->id, ObjProperty::MONSTER_COUNT, count); - setObjProperty(m->id, ObjProperty::MONSTER_POWER, (si64)1000*count); - return dynamic_cast(m); -} - bool IGameCallback::isVisitCoveredByAnotherQuery(const CGObjectInstance *obj, const CGHeroInstance *hero) { //only server knows - assert(0); + logGlobal->error("isVisitCoveredByAnotherQuery call on client side"); return false; } diff --git a/lib/IGameCallback.h b/lib/IGameCallback.h index 40f6f0c4e..64196ec4d 100644 --- a/lib/IGameCallback.h +++ b/lib/IGameCallback.h @@ -9,6 +9,8 @@ */ #pragma once +#include + #include "CGameInfoCallback.h" // for CGameInfoCallback #include "CRandomGenerator.h" @@ -25,6 +27,13 @@ class CStackBasicDescriptor; class CGCreature; struct ShashInt3; +namespace scripting +{ + class Context; + class Pool; + class Script; +} + class DLL_LINKAGE CPrivilegedInfoCallback : public CGameInfoCallback { public: @@ -42,12 +51,16 @@ public: void loadCommonState(Loader &in); //loads GS and VLC }; -class DLL_LINKAGE IGameEventCallback : public IGameEventRealizer +class DLL_LINKAGE IGameEventCallback { public: + virtual void setObjProperty(ObjectInstanceID objid, int prop, si64 val) = 0; + + virtual void showInfoDialog(InfoWindow * iw) = 0; + virtual void showInfoDialog(const std::string & msg, PlayerColor player) = 0; + virtual void changeSpells(const CGHeroInstance * hero, bool give, const std::set &spells)=0; virtual bool removeObject(const CGObjectInstance * obj)=0; - virtual void setBlockVis(ObjectInstanceID objid, bool bv)=0; virtual void setOwner(const CGObjectInstance * objid, PlayerColor owner)=0; virtual void changePrimSkill(const CGHeroInstance * hero, PrimarySkill::PrimarySkill which, si64 val, bool abs=false)=0; virtual void changeSecSkill(const CGHeroInstance * hero, SecondarySkill which, int val, bool abs=false)=0; @@ -76,7 +89,6 @@ public: virtual void putArtifact(const ArtifactLocation &al, const CArtifactInstance *a) = 0; virtual void removeArtifact(const ArtifactLocation &al) = 0; virtual bool moveArtifact(const ArtifactLocation &al1, const ArtifactLocation &al2) = 0; - virtual void synchronizeArtifactHandlerLists() = 0; virtual void showCompInfo(ShowInInfobox * comp)=0; virtual void heroVisitCastle(const CGTownInstance * obj, const CGHeroInstance * hero)=0; @@ -85,7 +97,6 @@ public: virtual void startBattlePrimary(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool creatureBank = false, const CGTownInstance *town = nullptr)=0; //use hero=nullptr for no hero virtual void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, bool creatureBank = false)=0; //if any of armies is hero, hero will be used virtual void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, bool creatureBank = false)=0; //if any of armies is hero, hero will be used, visitable tile of second obj is place of battle - virtual void setAmount(ObjectInstanceID objid, ui32 val)=0; virtual bool moveHero(ObjectInstanceID hid, int3 dst, ui8 teleporting, bool transit = false, PlayerColor asker = PlayerColor::NEUTRAL)=0; virtual void giveHeroBonus(GiveBonus * bonus)=0; virtual void setMovePoints(SetMovePoints * smp)=0; @@ -101,7 +112,7 @@ public: class DLL_LINKAGE CNonConstInfoCallback : public CPrivilegedInfoCallback { public: - PlayerState *getPlayer(PlayerColor color, bool verbose = true); + PlayerState * getPlayerState(PlayerColor color, bool verbose = true); TeamState *getTeam(TeamID teamID);//get team by team ID TeamState *getPlayerTeam(PlayerColor color);// get team by player color CGHeroInstance *getHero(ObjectInstanceID objid); @@ -110,6 +121,8 @@ public: CArtifactInstance * getArtInstance(ArtifactInstanceID aid); CGObjectInstance * getObjInstance(ObjectInstanceID oid); CArmedInstance * getArmyInstance(ObjectInstanceID oid); + + virtual void updateEntity(Metatype metatype, int32_t index, const JsonNode & data) = 0; }; /// Interface class for handling general game logic and actions @@ -118,9 +131,7 @@ class DLL_LINKAGE IGameCallback : public CPrivilegedInfoCallback, public IGameEv public: virtual ~IGameCallback(){}; - //do sth - const CGObjectInstance *putNewObject(Obj ID, int subID, int3 pos); - const CGCreature *putNewMonster(CreatureID creID, int count, int3 pos); + virtual scripting::Pool * getGlobalContextPool() const = 0; //get info virtual bool isVisitCoveredByAnotherQuery(const CGObjectInstance *obj, const CGHeroInstance *hero); diff --git a/lib/IGameEventsReceiver.h b/lib/IGameEventsReceiver.h index a68d2d19a..93877401d 100644 --- a/lib/IGameEventsReceiver.h +++ b/lib/IGameEventsReceiver.h @@ -57,17 +57,18 @@ public: virtual void actionFinished(const BattleAction &action){};//occurs AFTER every action taken by any stack or by the hero virtual void actionStarted(const BattleAction &action){};//occurs BEFORE every action taken by any stack or by the hero virtual void battleAttack(const BattleAttack *ba){}; //called when stack is performing attack - virtual void battleStacksAttacked(const std::vector & bsa, const std::vector & battleLog){}; //called when stack receives damage (after battleAttack()) + virtual void battleStacksAttacked(const std::vector & bsa){}; //called when stack receives damage (after battleAttack()) virtual void battleEnd(const BattleResult *br){}; virtual void battleNewRoundFirst(int round){}; //called at the beginning of each turn before changes are applied; virtual void battleNewRound(int round){}; //called at the beginning of each turn, round=-1 is the tactic phase, round=0 is the first "normal" turn + virtual void battleLogMessage(const std::vector & lines){}; virtual void battleStackMoved(const CStack * stack, std::vector dest, int distance){}; virtual void battleSpellCast(const BattleSpellCast *sc){}; virtual void battleStacksEffectsSet(const SetStackEffect & sse){};//called when a specific effect is set to stacks virtual void battleTriggerEffect(const BattleTriggerEffect & bte){}; //called for various one-shot effects virtual void battleStartBefore(const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2) {}; //called just before battle start virtual void battleStart(const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool side){}; //called by engine when battle starts; side=0 - left, side=1 - right - virtual void battleUnitsChanged(const std::vector & units, const std::vector & customEffects, const std::vector & battleLog){}; + virtual void battleUnitsChanged(const std::vector & units, const std::vector & customEffects){}; virtual void battleObstaclesChanged(const std::vector & obstacles){}; virtual void battleCatapultAttacked(const CatapultAttack & ca){}; //called when catapult makes an attack virtual void battleGateStateChanged(const EGateState state){}; diff --git a/lib/IHandlerBase.h b/lib/IHandlerBase.h index 74496689f..18566f294 100644 --- a/lib/IHandlerBase.h +++ b/lib/IHandlerBase.h @@ -9,18 +9,15 @@ */ #pragma once - #include "../lib/ConstTransitivePtr.h" - #include "VCMI_Lib.h" +#include "../lib/ConstTransitivePtr.h" +#include "VCMI_Lib.h" class JsonNode; +class Entity; /// base class for all handlers that can be accessed from mod system class DLL_LINKAGE IHandlerBase { - // there also should be private member with such signature: - // Object * loadFromJson(const JsonNode & json); - // where Object is type of data loaded by handler - // primary used in loadObject methods protected: /// Calls modhandler. Mostly needed to avoid large number of includes in headers void registerObject(std::string scope, std::string type_name, std::string name, si32 index); @@ -54,7 +51,7 @@ public: virtual ~IHandlerBase(){} }; -template class CHandlerBase: public IHandlerBase +template class CHandlerBase : public _ServiceBase, public IHandlerBase { public: virtual ~CHandlerBase() @@ -65,45 +62,104 @@ public: } } + + const Entity * getBaseByIndex(const int32_t index) const override + { + return getByIndex(index); + } + + const _ObjectBase * getById(const _ObjectID & id) const override + { + return (*this)[id].get(); + } + + const _ObjectBase * getByIndex(const int32_t index) const override + { + return (*this)[_ObjectID(index)].get(); + } + + void forEachBase(const std::function & cb) const override + { + forEachT(cb); + } + + void forEach(const std::function & cb) const override + { + forEachT(cb); + } + void loadObject(std::string scope, std::string name, const JsonNode & data) override { - auto object = loadFromJson(data, normalizeIdentifier(scope, "core", name), objects.size()); + auto object = loadFromJson(scope, data, normalizeIdentifier(scope, "core", name), objects.size()); objects.push_back(object); for(auto type_name : getTypeNames()) - registerObject(scope, type_name, name, object->id); + registerObject(scope, type_name, name, object->getIndex()); } + void loadObject(std::string scope, std::string name, const JsonNode & data, size_t index) override { - auto object = loadFromJson(data, normalizeIdentifier(scope, "core", name), index); + auto object = loadFromJson(scope, data, normalizeIdentifier(scope, "core", name), index); assert(objects[index] == nullptr); // ensure that this id was not loaded before objects[index] = object; for(auto type_name : getTypeNames()) - registerObject(scope, type_name, name, object->id); + registerObject(scope, type_name, name, object->getIndex()); } ConstTransitivePtr<_Object> operator[] (const _ObjectID id) const { - const auto raw_id = id.toEnum(); + const int32_t raw_id = id.getNum(); + return operator[](raw_id); + } - if (raw_id < 0 || raw_id >= objects.size()) + ConstTransitivePtr<_Object> operator[] (int32_t index) const + { + if(index < 0 || index >= objects.size()) { - logMod->error("%s id %d is invalid", getTypeNames()[0], static_cast(raw_id)); + logMod->error("%s id %d is invalid", getTypeNames()[0], index); throw std::runtime_error("internal error"); } - return objects[raw_id]; + return objects[index]; } + + void updateEntity(int32_t index, const JsonNode & data) + { + if(index < 0 || index >= objects.size()) + { + logMod->error("%s id %d is invalid", getTypeNames()[0], index); + } + else + { + objects.at(index)->updateFrom(data); + } + } + size_t size() const { return objects.size(); } + protected: - virtual _Object * loadFromJson(const JsonNode & json, const std::string & identifier, size_t index) = 0; + virtual _Object * loadFromJson(const std::string & scope, const JsonNode & json, const std::string & identifier, size_t index) = 0; virtual const std::vector & getTypeNames() const = 0; + + template + void forEachT(const std::function & cb) const + { + bool stop = false; + + for(auto & object : objects) + { + cb(object.get(), stop); + if(stop) + break; + } + } + public: //todo: make private std::vector> objects; }; diff --git a/lib/JsonNode.cpp b/lib/JsonNode.cpp index 7af159dc6..1b702d750 100644 --- a/lib/JsonNode.cpp +++ b/lib/JsonNode.cpp @@ -433,8 +433,7 @@ void JsonUtils::parseTypedBonusShort(const JsonVector& source, std::shared_ptrturnsRemain = 0; } - -std::shared_ptr JsonUtils::parseBonus (const JsonVector &ability_vec) //TODO: merge with AddAbility, create universal parser for all bonus properties +std::shared_ptr JsonUtils::parseBonus(const JsonVector & ability_vec) { auto b = std::make_shared(); std::string type = ability_vec[0].String(); @@ -449,8 +448,9 @@ std::shared_ptr JsonUtils::parseBonus (const JsonVector &ability_vec) //T parseTypedBonusShort(ability_vec, b); return b; } + template -const T & parseByMap(const std::map & map, const JsonNode * val, std::string err) +const T parseByMap(const std::map & map, const JsonNode * val, std::string err) { static T defaultValue = T(); if (!val->isNull()) @@ -471,6 +471,15 @@ const T & parseByMap(const std::map & map, const JsonNode * val, return defaultValue; } +template +const T parseByMapN(const std::map & map, const JsonNode * val, std::string err) +{ + if(val->isNumber()) + return static_cast(val->Integer()); + else + return parseByMap(map, val, err); +} + void JsonUtils::resolveIdentifier(si32 &var, const JsonNode &node, std::string name) { const JsonNode &value = node[name]; @@ -729,7 +738,7 @@ bool JsonUtils::parseBonus(const JsonNode &ability, Bonus *b) value = &ability["valueType"]; if (!value->isNull()) - b->valType = static_cast(parseByMap(bonusValueMap, value, "value type ")); + b->valType = static_cast(parseByMapN(bonusValueMap, value, "value type ")); b->stacking = ability["stacking"].String(); @@ -744,7 +753,7 @@ bool JsonUtils::parseBonus(const JsonNode &ability, Bonus *b) value = &ability["effectRange"]; if (!value->isNull()) - b->effectRange = static_cast(parseByMap(bonusLimitEffect, value, "effect range ")); + b->effectRange = static_cast(parseByMapN(bonusLimitEffect, value, "effect range ")); value = &ability["duration"]; if (!value->isNull()) @@ -759,7 +768,7 @@ bool JsonUtils::parseBonus(const JsonNode &ability, Bonus *b) ui16 dur = 0; for (const JsonNode & d : value->Vector()) { - dur |= parseByMap(bonusDurationMap, &d, "duration type "); + dur |= parseByMapN(bonusDurationMap, &d, "duration type "); } b->duration = (Bonus::BonusDuration)dur; } diff --git a/lib/NetPacks.h b/lib/NetPacks.h index 20defab38..c7bd85c0d 100644 --- a/lib/NetPacks.h +++ b/lib/NetPacks.h @@ -157,6 +157,20 @@ struct YourTurn : public CPackForClient } }; +struct EntitiesChanged: public CPackForClient +{ + std::vector changes; + + EntitiesChanged(){}; + void applyCl(CClient * cl); + DLL_LINKAGE void applyGs(CGameState * gs); + + template void serialize(Handler &h, const int version) + { + h & changes; + } +}; + struct SetResources : public CPackForClient { SetResources():abs(true){}; @@ -447,8 +461,7 @@ struct SetCommanderProperty : public CPackForClient void applyCl(CClient *cl){}; DLL_LINKAGE void applyGs(CGameState *gs); - ObjectInstanceID heroid; //for commander attached to hero - StackLocation sl; //for commander not on the hero? + ObjectInstanceID heroid; ECommanderProperty which; TExpType amount; //0 for dead, >0 for alive @@ -458,7 +471,6 @@ struct SetCommanderProperty : public CPackForClient template void serialize(Handler &h, const int version) { h & heroid; - h & sl; h & which; h & amount; h & additionalInfo; @@ -715,7 +727,7 @@ struct OpenWindow : public CPackForClient } }; -struct NewObject : public CPackForClient +struct NewObject : public CPackForClient { NewObject():subID(0){} void applyCl(CClient *cl); @@ -735,7 +747,7 @@ struct NewObject : public CPackForClient } }; -struct SetAvailableArtifacts : public CPackForClient +struct SetAvailableArtifacts : public CPackForClient { SetAvailableArtifacts():id(0){} void applyCl(CClient *cl); @@ -769,10 +781,6 @@ struct CGarrisonOperationPack : CPackForClient { }; -struct CArtifactOperationPack : CPackForClient -{ -}; - struct ChangeStackCount : CGarrisonOperationPack { ObjectInstanceID army; @@ -904,6 +912,10 @@ struct GetEngagedHeroIds : boost::static_visitor lines; + + BattleLogMessage(){} + + void applyCl(CClient * cl); + DLL_LINKAGE void applyGs(CGameState * gs); + DLL_LINKAGE void applyBattle(IBattleState * battleState); + + template void serialize(Handler & h, const int version) + { + h & lines; + } +}; + struct BattleStackMoved : public CPackForClient { ui32 stack; @@ -1435,13 +1463,11 @@ struct BattleUnitsChanged : public CPackForClient void applyCl(CClient *cl); std::vector changedStacks; - std::vector battleLog; std::vector customEffects; template void serialize(Handler & h, const int version) { h & changedStacks; - h & battleLog; h & customEffects; } }; @@ -1527,7 +1553,6 @@ struct BattleAttack : public CPackForClient SpellID spellID; //for SPELL_LIKE - std::vector battleLog; std::vector customEffects; bool shot() const//distance attack - decrease number of shots @@ -1564,7 +1589,6 @@ struct BattleAttack : public CPackForClient h & stackAttacking; h & flags; h & spellID; - h & battleLog; h & customEffects; h & attackerChanges; } @@ -1617,7 +1641,6 @@ struct BattleSpellCast : public CPackForClient si32 casterStack;// -1 if not cated by creature, >=0 caster stack ID bool castByHero; //if true - spell has been cast by hero, otherwise by a creature - std::vector battleLog; template void serialize(Handler &h, const int version) { h & side; @@ -1628,7 +1651,6 @@ struct BattleSpellCast : public CPackForClient h & affectedCres; h & casterStack; h & castByHero; - h & battleLog; h & activeCast; } }; @@ -1644,13 +1666,11 @@ struct SetStackEffect : public CPackForClient std::vector>> toUpdate; std::vector>> toRemove; - std::vector battleLog; template void serialize(Handler & h, const int version) { h & toAdd; h & toUpdate; h & toRemove; - h & battleLog; } }; @@ -1663,12 +1683,10 @@ struct StacksInjured : public CPackForClient void applyCl(CClient * cl); std::vector stacks; - std::vector battleLog; template void serialize(Handler & h, const int version) { h & stacks; - h & battleLog; } }; @@ -1847,30 +1865,6 @@ struct ShowWorldViewEx : public CPackForClient /***********************************************************************************************************/ -struct CommitPackage : public CPackForServer -{ - bool freePack; //for local usage, DO NOT serialize - bool applyGh(CGameHandler *gh); - CPackForClient *packToCommit; - - CommitPackage() - { - freePack = true; - packToCommit = nullptr; - } - ~CommitPackage() - { - if(freePack) - delete packToCommit; - } - - template void serialize(Handler &h, const int version) - { - h & static_cast(*this); - h & packToCommit; - } -}; - struct EndTurn : public CPackForServer { bool applyGh(CGameHandler *gh); diff --git a/lib/NetPacksBase.h b/lib/NetPacksBase.h index 1404fc3f8..868e50245 100644 --- a/lib/NetPacksBase.h +++ b/lib/NetPacksBase.h @@ -21,6 +21,8 @@ class CArtifactSet; class CBonusSystemNode; struct ArtSlotInfo; +#include + #include "ConstTransitivePtr.h" #include "GameConstants.h" #include "JsonNode.h" @@ -98,9 +100,9 @@ public: std::vector > localStrings; std::vector exactStrings; - std::vector numbers; + std::vector numbers; - template void serialize(Handler &h, const int version) + template void serialize(Handler & h, const int version) { h & exactStrings; h & localStrings; @@ -124,7 +126,7 @@ public: exactStrings.push_back(txt); return *this; } - MetaString& operator<<(int txt) + MetaString& operator<<(int64_t txt) { message.push_back(TNUMBER); numbers.push_back(txt); @@ -140,12 +142,12 @@ public: message.push_back(TREPLACE_ESTRING); exactStrings.push_back(txt); } - void addReplacement(int txt) + void addReplacement(int64_t txt) { message.push_back(TREPLACE_NUMBER); numbers.push_back(txt); } - void addReplacement2(int txt) + void addReplacement2(int64_t txt) { message.push_back(TREPLACE_PLUSNUMBER); numbers.push_back(txt); @@ -266,6 +268,26 @@ struct CustomEffectInfo } }; +class EntityChanges +{ +public: + Metatype metatype; + int32_t entityIndex; + JsonNode data; + EntityChanges() + : metatype(Metatype::UNKNOWN), + entityIndex(0), + data() + { + } + template void serialize(Handler & h, const int version) + { + h & metatype; + h & entityIndex; + h & data; + } +}; + class BattleChanges { public: diff --git a/lib/NetPacksLib.cpp b/lib/NetPacksLib.cpp index fad0f1eab..f5ad799e8 100644 --- a/lib/NetPacksLib.cpp +++ b/lib/NetPacksLib.cpp @@ -33,14 +33,14 @@ DLL_LINKAGE void SetResources::applyGs(CGameState *gs) { assert(player < PlayerColor::PLAYER_LIMIT); if(abs) - gs->getPlayer(player)->resources = res; + gs->getPlayerState(player)->resources = res; else - gs->getPlayer(player)->resources += res; + gs->getPlayerState(player)->resources += res; //just ensure that player resources are not negative //server is responsible to check if player can afford deal //but events on server side are allowed to take more than player have - gs->getPlayer(player)->resources.positive(); + gs->getPlayerState(player)->resources.positive(); } DLL_LINKAGE void SetPrimSkill::applyGs(CGameState *gs) @@ -206,7 +206,7 @@ DLL_LINKAGE void FoWChange::applyGs(CGameState *gs) DLL_LINKAGE void SetAvailableHeroes::applyGs(CGameState *gs) { - PlayerState *p = gs->getPlayer(player); + PlayerState *p = gs->getPlayerState(player); p->availableHeroes.clear(); for (int i = 0; i < GameConstants::AVAILABLE_HEROES_PER_PLAYER; i++) @@ -227,7 +227,7 @@ DLL_LINKAGE void GiveBonus::applyGs(CGameState *gs) cbsn = gs->getHero(ObjectInstanceID(id)); break; case PLAYER: - cbsn = gs->getPlayer(PlayerColor(id)); + cbsn = gs->getPlayerState(PlayerColor(id)); break; case TOWN: cbsn = gs->getTown(ObjectInstanceID(id)); @@ -288,14 +288,14 @@ DLL_LINKAGE void ChangeObjectVisitors::applyGs(CGameState *gs) switch (mode) { case VISITOR_ADD: gs->getHero(hero)->visitedObjects.insert(object); - gs->getPlayer(gs->getHero(hero)->tempOwner)->visitedObjects.insert(object); + gs->getPlayerState(gs->getHero(hero)->tempOwner)->visitedObjects.insert(object); break; case VISITOR_ADD_TEAM: { TeamState *ts = gs->getPlayerTeam(gs->getHero(hero)->tempOwner); for (auto & color : ts->players) { - gs->getPlayer(color)->visitedObjects.insert(object); + gs->getPlayerState(color)->visitedObjects.insert(object); } } break; @@ -322,7 +322,7 @@ DLL_LINKAGE void ChangeObjectVisitors::applyGs(CGameState *gs) DLL_LINKAGE void PlayerEndsGame::applyGs(CGameState *gs) { - PlayerState *p = gs->getPlayer(player); + PlayerState *p = gs->getPlayerState(player); if(victoryLossCheckResult.victory()) { p->status = EPlayerStatus::WINNER; @@ -368,7 +368,7 @@ DLL_LINKAGE void RemoveBonus::applyGs(CGameState *gs) if (who == HERO) node = gs->getHero(ObjectInstanceID(whoID)); else - node = gs->getPlayer(PlayerColor(whoID)); + node = gs->getPlayerState(PlayerColor(whoID)); BonusList &bonuses = node->getExportedBonusList(); @@ -395,7 +395,7 @@ DLL_LINKAGE void RemoveObject::applyGs(CGameState *gs) if(obj->ID==Obj::HERO) { CGHeroInstance *h = static_cast(obj); - PlayerState *p = gs->getPlayer(h->tempOwner); + PlayerState *p = gs->getPlayerState(h->tempOwner); gs->map->heroesOnMap -= h; p->heroes -= h; h->detachFrom(h->whereShouldBeAttached(gs)); @@ -642,7 +642,7 @@ DLL_LINKAGE void HeroRecruited::applyGs(CGameState *gs) assert(vstd::contains(gs->hpool.heroesPool, hid)); CGHeroInstance *h = gs->hpool.heroesPool[hid]; CGTownInstance *t = gs->getTown(tid); - PlayerState *p = gs->getPlayer(player); + PlayerState *p = gs->getPlayerState(player); assert(!h->boat); @@ -684,14 +684,14 @@ DLL_LINKAGE void GiveHero::applyGs(CGameState *gs) //bonus system h->detachFrom(&gs->globalEffects); - h->attachTo(gs->getPlayer(player)); - h->appearance = VLC->objtypeh->getHandlerFor(Obj::HERO, h->type->heroClass->id)->getTemplates().front(); + h->attachTo(gs->getPlayerState(player)); + h->appearance = VLC->objtypeh->getHandlerFor(Obj::HERO, h->type->heroClass->getIndex())->getTemplates().front(); gs->map->removeBlockVisTiles(h,true); h->setOwner(player); h->movement = h->maxMovePoints(true); gs->map->heroesOnMap.push_back(h); - gs->getPlayer(h->getOwner())->heroes.push_back(h); + gs->getPlayerState(h->getOwner())->heroes.push_back(h); gs->map->addBlockVisTiles(h); h->inTownGarrison = false; } @@ -707,7 +707,7 @@ DLL_LINKAGE void NewObject::applyGs(CGameState *gs) testObject.appearance = VLC->objtypeh->getHandlerFor(ID, subID)->getTemplates(ETerrainType::WATER).front(); const int3 previousXAxisTile = int3(pos.x - 1, pos.y, pos.z); - assert(gs->isInTheMap(previousXAxisTile) && (testObject.visitablePos() == previousXAxisTile)); + assert(gs->isInTheMap(previousXAxisTile) && (testObject.visitablePos() == previousXAxisTile)); } else { @@ -1031,7 +1031,7 @@ DLL_LINKAGE void EraseArtifact::applyGs(CGameState *gs) auto slot = al.getSlot(); if(slot->locked) { - logGlobal->debug("Erasing locked artifact: %s", slot->artifact->artType->Name()); + logGlobal->debug("Erasing locked artifact: %s", slot->artifact->artType->getName()); DisassembledArtifact dis; dis.al.artHolder = al.artHolder; auto aset = al.getHolderArtSet(); @@ -1051,12 +1051,12 @@ DLL_LINKAGE void EraseArtifact::applyGs(CGameState *gs) } } assert(found && "Failed to determine the assembly this locked artifact belongs to"); - logGlobal->debug("Found the corresponding assembly: %s", dis.al.getSlot()->artifact->artType->Name()); + logGlobal->debug("Found the corresponding assembly: %s", dis.al.getSlot()->artifact->artType->getName()); dis.applyGs(gs); } else { - logGlobal->debug("Erasing artifact %s", slot->artifact->artType->Name()); + logGlobal->debug("Erasing artifact %s", slot->artifact->artType->getName()); } al.removeArtifact(); } @@ -1188,7 +1188,7 @@ DLL_LINKAGE void NewTurn::applyGs(CGameState *gs) for(auto i = res.cbegin(); i != res.cend(); i++) { assert(i->first < PlayerColor::PLAYER_LIMIT); - gs->getPlayer(i->first)->resources = i->second; + gs->getPlayerState(i->first)->resources = i->second; } for(auto creatureSet : cres) //set available creatures in towns @@ -1237,10 +1237,10 @@ DLL_LINKAGE void SetObjectProperty::applyGs(CGameState *gs) { CGTownInstance *t = static_cast(obj); if(t->tempOwner < PlayerColor::PLAYER_LIMIT) - gs->getPlayer(t->tempOwner)->towns -= t; + gs->getPlayerState(t->tempOwner)->towns -= t; if(val < PlayerColor::PLAYER_LIMIT_I) { - PlayerState * p = gs->getPlayer(PlayerColor(val)); + PlayerState * p = gs->getPlayerState(PlayerColor(val)); p->towns.push_back(t); //reset counter before NewTurn to avoid no town message if game loaded at turn when one already captured @@ -1389,6 +1389,16 @@ void BattleResult::applyGs(CGameState *gs) gs->curB.dellNull(); } +DLL_LINKAGE void BattleLogMessage::applyGs(CGameState *gs) +{ + //nothing +} + +DLL_LINKAGE void BattleLogMessage::applyBattle(IBattleState * battleState) +{ + //nothing +} + DLL_LINKAGE void BattleStackMoved::applyGs(CGameState *gs) { applyBattle(gs->curB); @@ -1445,7 +1455,7 @@ DLL_LINKAGE void StartAction::applyGs(CGameState *gs) } else { - gs->curB->sides[ba.side].usedSpellsHistory.push_back(SpellID(ba.actionSubtype).toSpell()); + gs->curB->sides[ba.side].usedSpellsHistory.push_back(SpellID(ba.actionSubtype)); } switch(ba.actionType) @@ -1532,6 +1542,9 @@ DLL_LINKAGE void BattleUnitsChanged::applyBattle(IBattleState * battleState) case BattleChanges::EOperation::ADD: battleState->addUnit(elem.id, elem.data); break; + case BattleChanges::EOperation::UPDATE: + battleState->updateUnit(elem.id, elem.data); + break; default: logNetwork->error("Unknown unit operation %d", (int)elem.operation); break; @@ -1644,8 +1657,8 @@ DLL_LINKAGE void PlayerCheated::applyGs(CGameState *gs) if(!player.isValidPlayer()) return; - gs->getPlayer(player)->enteredLosingCheatCode = losingCheatCode; - gs->getPlayer(player)->enteredWinningCheatCode = winningCheatCode; + gs->getPlayerState(player)->enteredLosingCheatCode = losingCheatCode; + gs->getPlayerState(player)->enteredWinningCheatCode = winningCheatCode; } DLL_LINKAGE void YourTurn::applyGs(CGameState *gs) @@ -1660,3 +1673,9 @@ DLL_LINKAGE Component::Component(const CStackBasicDescriptor &stack) : id(CREATURE), subtype(stack.type->idNumber), val(stack.count), when(0) { } + +DLL_LINKAGE void EntitiesChanged::applyGs(CGameState * gs) +{ + for(const auto & change : changes) + gs->updateEntity(change.metatype, change.entityIndex, change.data); +} diff --git a/lib/ScriptHandler.cpp b/lib/ScriptHandler.cpp new file mode 100644 index 000000000..28c5d7efe --- /dev/null +++ b/lib/ScriptHandler.cpp @@ -0,0 +1,303 @@ +/* + * ScriptHandler.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 "ScriptHandler.h" + +#include +#include + +#include "CGameInterface.h" +#include "CScriptingModule.h" + +#include "VCMIDirs.h" +#include "serializer/JsonDeserializer.h" +#include "serializer/JsonSerializer.h" +#include "filesystem/Filesystem.h" + +static const std::vector IMPLEMENTS_MAP = +{ + "ANYTHING", + "BATTLE_EFFECT" +}; + +namespace scripting +{ + +ScriptImpl::ScriptImpl(const ScriptHandler * owner_) + :owner(owner_), + host(), + implements(Implements::ANYTHING) +{ + +} + +ScriptImpl::~ScriptImpl() = default; + +void ScriptImpl::compile(vstd::CLoggerBase * logger) +{ + code = host->compile(sourcePath, sourceText, logger); + + if(host == owner->erm) + { + host = owner->lua; + sourceText = code; + code = host->compile(getName(), getSource(), logger); + } +} + +std::shared_ptr ScriptImpl::createContext(const Environment * env) const +{ + return host->createContextFor(this, env); +} + +const std::string & ScriptImpl::getName() const +{ + return identifier; +} + +const std::string & ScriptImpl::getSource() const +{ + return sourceText; +} + +void ScriptImpl::performRegistration(::Services * services) const +{ + switch(implements) + { + case Implements::ANYTHING: + break; + case Implements::BATTLE_EFFECT: + host->registerSpellEffect(services->spellEffects(), this); + break; + } +} + +void ScriptImpl::serializeJson(vstd::CLoggerBase * logger, JsonSerializeFormat & handler) +{ + handler.serializeString("source", sourcePath); + handler.serializeEnum("implements", implements, Implements::ANYTHING, IMPLEMENTS_MAP); + + if(!handler.saving) + { + resolveHost(); + + ResourceID sourcePathId("SCRIPTS/"+sourcePath); + + auto rawData = CResourceHandler::get()->load(sourcePathId)->readAll(); + + sourceText = std::string((char *)rawData.first.get(), rawData.second); + + compile(logger); + } +} + +void ScriptImpl::serializeJsonState(JsonSerializeFormat & handler) +{ + handler.serializeString("sourcePath", sourcePath); + handler.serializeString("sourceText", sourceText); + handler.serializeString("code", code); + handler.serializeEnum("implements", implements, Implements::ANYTHING, IMPLEMENTS_MAP); + + if(!handler.saving) + { + host = owner->lua; + } +} + +void ScriptImpl::resolveHost() +{ + ResourceID sourcePathId(sourcePath); + + if(sourcePathId.getType() == EResType::ERM) + host = owner->erm; + else if(sourcePathId.getType() == EResType::LUA) + host = owner->lua; + else + throw std::runtime_error("Unknown script language in:"+sourcePath); +} + +PoolImpl::PoolImpl(const Environment * ENV) + : env(ENV), + srv(nullptr) +{ +} + +PoolImpl::PoolImpl(const Environment * ENV, ServerCallback * SRV) + : env(ENV), + srv(SRV) +{ +} + +std::shared_ptr PoolImpl::getContext(const Script * script) +{ + auto iter = cache.find(script); + + if(iter == cache.end()) + { + auto context = script->createContext(env); + cache[script] = context; + + auto & key = script->getName(); + const JsonNode & scriptState = state[key]; + + if(srv) + context->run(srv, scriptState); + else + context->run(scriptState); + + return context; + } + else + { + return iter->second; + } +} + +void PoolImpl::serializeState(const bool saving, JsonNode & data) +{ + if(saving) + { + for(auto & scriptAndContext : cache) + { + auto script = scriptAndContext.first; + auto context = scriptAndContext.second; + + state[script->getName()] = context->saveState(); + + data = state; + } + } + else + { + state = data; + } +} + +ScriptHandler::ScriptHandler() +{ + boost::filesystem::path filePath = VCMIDirs::get().fullLibraryPath("scripting", "vcmiERM"); + erm = CDynLibHandler::getNewScriptingModule(filePath); + + filePath = VCMIDirs::get().fullLibraryPath("scripting", "vcmiLua"); + lua = CDynLibHandler::getNewScriptingModule(filePath); +} + +ScriptHandler::~ScriptHandler() = default; + +const Script * ScriptHandler::resolveScript(const std::string & name) const +{ + auto iter = objects.find(name); + + if(iter == objects.end()) + { + logMod->error("Unknown script id '%s'", name); + return nullptr; + } + else + { + return iter->second.get(); + } +} + +std::vector ScriptHandler::getDefaultAllowed() const +{ + return std::vector(); +} + +std::vector ScriptHandler::loadLegacyData(size_t dataSize) +{ + return std::vector(); +} + +ScriptPtr ScriptHandler::loadFromJson(vstd::CLoggerBase * logger, const std::string & scope, const JsonNode & json, const std::string & identifier) const +{ + ScriptPtr ret = std::make_shared(this); + + JsonDeserializer handler(nullptr, json); + ret->identifier = identifier; + ret->serializeJson(logger, handler); + return ret; +} + +void ScriptHandler::loadObject(std::string scope, std::string name, const JsonNode & data) +{ + auto object = loadFromJson(logMod, scope, data, normalizeIdentifier(scope, "core", name)); + objects[object->identifier] = object; +} + +void ScriptHandler::loadObject(std::string scope, std::string name, const JsonNode & data, size_t index) +{ + throw std::runtime_error("No legacy data load allowed for scripts"); +} + +void ScriptHandler::performRegistration(Services * services) const +{ + for(auto & keyValue : objects) + { + auto script = keyValue.second; + script->performRegistration(services); + } +} + +void ScriptHandler::run(std::shared_ptr pool) const +{ + for(auto & keyValue : objects) + { + auto script = keyValue.second; + + if(script->implements == ScriptImpl::Implements::ANYTHING) + { + auto context = pool->getContext(script.get()); + //todo: consider explicit run for generic scripts + } + } +} + +void ScriptHandler::loadState(const JsonNode & state) +{ + objects.clear(); + + const JsonNode & scriptsData = state["scripts"]; + + for(auto & keyValue : scriptsData.Struct()) + { + std::string name = keyValue.first; + + const JsonNode & scriptData = keyValue.second; + + ScriptPtr script = std::make_shared(this); + + JsonDeserializer handler(nullptr, scriptData); + script->serializeJsonState(handler); + objects[name] = script; + } +} + +void ScriptHandler::saveState(JsonNode & state) +{ + JsonNode & scriptsData = state["scripts"]; + + for(auto & keyValue : objects) + { + std::string name = keyValue.first; + + ScriptPtr script = keyValue.second; + JsonNode scriptData; + JsonSerializer handler(nullptr, scriptData); + script->serializeJsonState(handler); + + scriptsData[name] = std::move(scriptData); + } + +} + + +} diff --git a/lib/ScriptHandler.h b/lib/ScriptHandler.h new file mode 100644 index 000000000..37da9e975 --- /dev/null +++ b/lib/ScriptHandler.h @@ -0,0 +1,133 @@ +/* + * ScriptHandler.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 +#include "IHandlerBase.h" +#include "JsonNode.h" + +class JsonNode; +class JsonSerializeFormat; +class Services; + +namespace scripting +{ +class Module; +class ScriptImpl; +class ScriptHandler; + +using ModulePtr = std::shared_ptr; +using ScriptPtr = std::shared_ptr; +using ScriptMap = std::map; + +class DLL_LINKAGE ScriptImpl : public Script +{ +public: + enum class Implements + { + ANYTHING, + BATTLE_EFFECT + //todo: adventure effect, map object(unified with building), server query, client query(with gui), may be smth else + }; + + Implements implements; + + std::string identifier; + std::string sourcePath; + std::string sourceText; + + std::string code; + + ModulePtr host; + + ScriptImpl(const ScriptHandler * owner_); + virtual ~ScriptImpl(); + + void compile(vstd::CLoggerBase * logger); + + void serializeJson(vstd::CLoggerBase * logger, JsonSerializeFormat & handler); + void serializeJsonState(JsonSerializeFormat & handler); + + std::shared_ptr createContext(const Environment * env) const override; + const std::string & getName() const override; + const std::string & getSource() const override; + + void performRegistration(::Services * services) const; +private: + const ScriptHandler * owner; + + void resolveHost(); +}; + +class DLL_LINKAGE PoolImpl : public Pool +{ +public: + PoolImpl(const Environment * ENV); + PoolImpl(const Environment * ENV, ServerCallback * SRV); + std::shared_ptr getContext(const Script * script) override; + + void serializeState(const bool saving, JsonNode & data) override; +private: + std::map> cache; + + JsonNode state; + + const Environment * env; + ServerCallback * srv; +}; + +class DLL_LINKAGE ScriptHandler : public ::IHandlerBase, public Service +{ +public: + ScriptMap objects; + + ScriptHandler(); + virtual ~ScriptHandler(); + + const Script * resolveScript(const std::string & name) const; + + std::vector getDefaultAllowed() const override; + std::vector loadLegacyData(size_t dataSize) override; + + ScriptPtr loadFromJson(vstd::CLoggerBase * logger, const std::string & scope, const JsonNode & json, const std::string & identifier) const; + + void loadObject(std::string scope, std::string name, const JsonNode & data) override; + void loadObject(std::string scope, std::string name, const JsonNode & data, size_t index) override; + + void performRegistration(Services * services) const override; + + void run(std::shared_ptr pool) const override; + + template void serialize(Handler & h, const int version) + { + JsonNode state; + if(h.saving) + saveState(state); + + h & state; + + if(!h.saving) + loadState(state); + } + + ModulePtr erm; + ModulePtr lua; + +protected: + +private: + friend class ScriptImpl; + + void loadState(const JsonNode & state); + void saveState(JsonNode & state); +}; + +} diff --git a/lib/VCMI_Lib.cpp b/lib/VCMI_Lib.cpp index a8f506bea..a558dcf0c 100644 --- a/lib/VCMI_Lib.cpp +++ b/lib/VCMI_Lib.cpp @@ -20,6 +20,7 @@ #include "CTownHandler.h" #include "CBuildingHandler.h" #include "spells/CSpellHandler.h" +#include "spells/effects/Registry.h" #include "CSkillHandler.h" #include "CGeneralTextHandler.h" #include "CModHandler.h" @@ -30,6 +31,7 @@ #include "CConsoleHandler.h" #include "rmg/CRmgTemplateStorage.h" #include "mapping/CMapEditManager.h" +#include "ScriptHandler.h" LibClasses * VLC = nullptr; @@ -53,11 +55,92 @@ DLL_LINKAGE void loadDLLClasses(bool onlyEssential) VLC->init(onlyEssential); } +const ArtifactService * LibClasses::artifacts() const +{ + return arth; +} + +const CreatureService * LibClasses::creatures() const +{ + return creh; +} + +const FactionService * LibClasses::factions() const +{ + return townh; +} + +const HeroClassService * LibClasses::heroClasses() const +{ + return &heroh->classes; +} + +const HeroTypeService * LibClasses::heroTypes() const +{ + return heroh; +} + +const scripting::Service * LibClasses::scripts() const +{ + return scriptHandler; +} + +const spells::Service * LibClasses::spells() const +{ + return spellh; +} + +const SkillService * LibClasses::skills() const +{ + return skillh; +} + const IBonusTypeHandler * LibClasses::getBth() const { return bth; } +const spells::effects::Registry * LibClasses::spellEffects() const +{ + return spells::effects::GlobalRegistry::get(); +} + +spells::effects::Registry * LibClasses::spellEffects() +{ + return spells::effects::GlobalRegistry::get(); +} + +void LibClasses::updateEntity(Metatype metatype, int32_t index, const JsonNode & data) +{ + switch(metatype) + { + case Metatype::ARTIFACT: + arth->updateEntity(index, data); + break; + case Metatype::CREATURE: + creh->updateEntity(index, data); + break; + case Metatype::FACTION: + townh->updateEntity(index, data); + break; + case Metatype::HERO_CLASS: + heroh->classes.updateEntity(index, data); + break; + case Metatype::HERO_TYPE: + heroh->updateEntity(index, data); + break; + case Metatype::SKILL: + skillh->updateEntity(index, data); + break; + case Metatype::SPELL: + spellh->updateEntity(index, data); + break; + default: + logGlobal->error("Invalid Metatype id %d", static_cast(metatype)); + break; + } +} + void LibClasses::loadFilesystem(bool onlyEssential) { CStopWatch totalTime; @@ -81,7 +164,7 @@ void LibClasses::loadFilesystem(bool onlyEssential) static void logHandlerLoaded(const std::string & name, CStopWatch & timer) { - logGlobal->info("\t\t %s handler: %d ms", name, timer.getDiff()); + logGlobal->info("\t\t %s handler: %d ms", name, timer.getDiff()); } template void createHandler(Handler *&handler, const std::string &name, CStopWatch &timer) @@ -120,6 +203,8 @@ void LibClasses::init(bool onlyEssential) createHandler(tplh, "Template", pomtime); //templates need already resolved identifiers (refactor?) + createHandler(scriptHandler, "Script", pomtime); + logGlobal->info("\tInitializing handlers: %d ms", totalTime.getDiff()); modh->load(); @@ -145,6 +230,7 @@ void LibClasses::clear() delete bth; delete tplh; delete terviewh; + delete scriptHandler; makeNull(); } @@ -163,6 +249,7 @@ void LibClasses::makeNull() bth = nullptr; tplh = nullptr; terviewh = nullptr; + scriptHandler = nullptr; } LibClasses::LibClasses() @@ -182,6 +269,11 @@ void LibClasses::callWhenDeserializing() //modh->loadConfigFromFile ("defaultMods"); //TODO: remember last saved config } +void LibClasses::scriptsLoaded() +{ + scriptHandler->performRegistration(this); +} + LibClasses::~LibClasses() { clear(); @@ -201,3 +293,10 @@ void LibClasses::restoreAllCreaturesNodeType794() { creh->restoreAllCreaturesNodeType794(); } + +void LibClasses::update800() +{ + vstd::clear_pointer(scriptHandler); + scriptHandler = new scripting::ScriptHandler(); +} + diff --git a/lib/VCMI_Lib.h b/lib/VCMI_Lib.h index ff4d6a104..38b4a6bf5 100644 --- a/lib/VCMI_Lib.h +++ b/lib/VCMI_Lib.h @@ -9,6 +9,8 @@ */ #pragma once +#include + class CConsoleHandler; class CArtHandler; class CHeroHandler; @@ -26,9 +28,15 @@ class IBonusTypeHandler; class CBonusTypeHandler; class CTerrainViewPatternConfig; class CRmgTemplateStorage; +class IHandlerBase; + +namespace scripting +{ + class ScriptHandler; +} /// Loads and constructs several handlers -class DLL_LINKAGE LibClasses +class DLL_LINKAGE LibClasses : public Services { CBonusTypeHandler * bth; @@ -41,7 +49,21 @@ class DLL_LINKAGE LibClasses public: bool IS_AI_ENABLED; //unused? - const IBonusTypeHandler * getBth() const; + const ArtifactService * artifacts() const override; + const CreatureService * creatures() const override; + const FactionService * factions() const override; + const HeroClassService * heroClasses() const override; + const HeroTypeService * heroTypes() const override; + const scripting::Service * scripts() const override; + const spells::Service * spells() const override; + const SkillService * skills() const override; + + void updateEntity(Metatype metatype, int32_t index, const JsonNode & data) override; + + const spells::effects::Registry * spellEffects() const override; + spells::effects::Registry * spellEffects() override; + + const IBonusTypeHandler * getBth() const; //deprecated CArtHandler * arth; CHeroHandler * heroh; @@ -55,6 +77,7 @@ public: CModHandler * modh; CTerrainViewPatternConfig * terviewh; CRmgTemplateStorage * tplh; + scripting::ScriptHandler * scriptHandler; LibClasses(); //c-tor, loads .lods and NULLs handlers ~LibClasses(); @@ -64,9 +87,23 @@ public: void loadFilesystem(bool onlyEssential);// basic initialization. should be called before init() + void scriptsLoaded(); template void serialize(Handler &h, const int version) { + if(version >= 800) + { + h & scriptHandler;//must be first (or second after modh), it can modify factories other handlers depends on + if(!h.saving) + { + scriptsLoaded(); + } + } + else if(!h.saving) + { + update800(); + } + h & heroh; h & arth; h & creh; @@ -93,11 +130,15 @@ public: h & IS_AI_ENABLED; h & bth; + if(!h.saving) { callWhenDeserializing(); } } + +private: + void update800(); }; extern DLL_LINKAGE LibClasses * VLC; diff --git a/lib/VCMI_lib.cbp b/lib/VCMI_lib.cbp index 2f163268a..98bf37af0 100644 --- a/lib/VCMI_lib.cbp +++ b/lib/VCMI_lib.cbp @@ -20,7 +20,6 @@ - @@ -50,7 +49,6 @@ - @@ -82,7 +80,6 @@ - @@ -118,7 +115,7 @@ - + @@ -131,10 +128,69 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -166,9 +222,11 @@ + + @@ -208,6 +266,8 @@ + + @@ -220,6 +280,7 @@ + @@ -246,6 +307,7 @@ + @@ -257,6 +319,18 @@ + + + + + + + + + + + + @@ -380,6 +454,7 @@ + @@ -389,6 +464,8 @@ + + @@ -401,7 +478,6 @@ - @@ -446,9 +522,6 @@ - - - diff --git a/lib/abilities/Ability.h b/lib/abilities/Ability.h new file mode 100644 index 000000000..30f10fb67 --- /dev/null +++ b/lib/abilities/Ability.h @@ -0,0 +1,18 @@ +/* + * Ability.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 + * + */ +namespace abilities +{ + +class DLL_LINKAGE Ability +{ + +}; + +} diff --git a/lib/battle/BattleInfo.cpp b/lib/battle/BattleInfo.cpp index 6590ce55c..73a64db58 100644 --- a/lib/battle/BattleInfo.cpp +++ b/lib/battle/BattleInfo.cpp @@ -16,6 +16,9 @@ #include "../mapObjects/CGTownInstance.h" #include "../CGeneralTextHandler.h" +//TODO: remove +#include "../IGameCallback.h" + ///BattleInfo std::pair< std::vector, int > BattleInfo::getPath(BattleHex start, BattleHex dest, const CStack * stack) { @@ -202,7 +205,7 @@ BattleInfo * BattleInfo::setupBattle(int3 tile, ETerrainType terrain, BFieldType if(town) { curB->town = town; - curB->terrainType = VLC->townh->factions[town->subID]->nativeTerrain; + curB->terrainType = (*VLC->townh)[town->subID]->nativeTerrain; } else { @@ -734,7 +737,6 @@ const IBonusBearer * BattleInfo::asBearer() const int64_t BattleInfo::getActualDamage(const TDmgRange & damage, int32_t attackerCount, vstd::RNG & rng) const { - if(damage.first != damage.second) { int64_t sum = 0; @@ -920,6 +922,11 @@ void BattleInfo::removeUnit(uint32_t id) } } +void BattleInfo::updateUnit(uint32_t id, const JsonNode & data) +{ + //TODO +} + void BattleInfo::addUnitBonus(uint32_t id, const std::vector & bonus) { CStack * sta = getStack(id, false); @@ -1032,7 +1039,7 @@ void BattleInfo::updateObstacle(const ObstacleChanges& changes) // Currently we only support to update the "revealed" property spellObstacle->revealed = changedObstacle->revealed; - + break; } } @@ -1060,6 +1067,13 @@ CGHeroInstance * BattleInfo::battleGetFightingHero(ui8 side) const return const_cast(CBattleInfoEssentials::battleGetFightingHero(side)); } +scripting::Pool * BattleInfo::getContextPool() const +{ + //this is real battle, use global scripting context pool + //TODO: make this line not ugly + return IObjectInterface::cb->getGlobalContextPool(); +} + bool CMP_stack::operator()(const battle::Unit * a, const battle::Unit * b) { switch(phase) @@ -1071,17 +1085,17 @@ bool CMP_stack::operator()(const battle::Unit * a, const battle::Unit * b) case 3: { int as = a->getInitiative(turn), bs = b->getInitiative(turn); - + if(as != bs) return as > bs; if(a->unitSide() == b->unitSide()) return a->unitSlot() < b->unitSlot(); - + return (a->unitSide() == side || b->unitSide() == side) ? a->unitSide() != side : a->unitSide() < b->unitSide(); - } + } default: assert(false); return false; diff --git a/lib/battle/BattleInfo.h b/lib/battle/BattleInfo.h index b32b09953..c265f457b 100644 --- a/lib/battle/BattleInfo.h +++ b/lib/battle/BattleInfo.h @@ -101,6 +101,7 @@ public: void moveUnit(uint32_t id, BattleHex destination) override; void setUnitState(uint32_t id, const JsonNode & data, int64_t healthDelta) override; void removeUnit(uint32_t id) override; + void updateUnit(uint32_t id, const JsonNode & data) override; void addUnitBonus(uint32_t id, const std::vector & bonus) override; void updateUnitBonus(uint32_t id, const std::vector & bonus) override; @@ -137,7 +138,9 @@ public: ui8 whatSide(PlayerColor player) const; static BattlefieldBI::BattlefieldBI battlefieldTypeToBI(BFieldType bfieldType); //converts above to ERM BI format - static int battlefieldTypeToTerrain(int bfieldType); //converts above to ERM BI format + +protected: + scripting::Pool * getContextPool() const override; }; diff --git a/lib/battle/CBattleInfoCallback.cpp b/lib/battle/CBattleInfoCallback.cpp index b12b993d9..168950a1d 100644 --- a/lib/battle/CBattleInfoCallback.cpp +++ b/lib/battle/CBattleInfoCallback.cpp @@ -9,6 +9,9 @@ */ #include "StdInc.h" #include "CBattleInfoCallback.h" + +#include + #include "../CStack.h" #include "BattleInfo.h" #include "../NetPacks.h" @@ -122,7 +125,7 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleCanCastSpell(con logGlobal->error("CBattleInfoCallback::battleCanCastSpell: no spellcaster."); return ESpellCastProblem::INVALID; } - const PlayerColor player = caster->getOwner(); + const PlayerColor player = caster->getCasterOwner(); const auto side = playerToSide(player); if(!side) return ESpellCastProblem::INVALID; @@ -501,7 +504,7 @@ void CBattleInfoCallback::battleGetTurnOrder(std::vector & out, c return; for(int i = 1; i < 4; i++) - boost::sort(phase[i], CMP_stack(i, actualTurn, lastMoved)); + boost::sort(phase[i], CMP_stack(i, actualTurn, lastMoved)); int pi = 1; while(!outputFull() && pi < 4) @@ -519,7 +522,7 @@ void CBattleInfoCallback::battleGetTurnOrder(std::vector & out, c else { out.back().push_back(current); - lastMoved = current->unitSide(); + lastMoved = current->unitSide(); } } } @@ -531,15 +534,6 @@ void CBattleInfoCallback::battleGetTurnOrder(std::vector & out, c battleGetTurnOrder(out, maxUnits, maxTurns, actualTurn + 1, lastMoved); } -void CBattleInfoCallback::battleGetStackCountOutsideHexes(bool *ac) const -{ - RETURN_IF_NOT_BATTLE(); - auto accessibility = getAccesibility(); - - for(int i = 0; i < accessibility.size(); i++) - ac[i] = (accessibility[i] == EAccessibility::ACCESSIBLE); -} - std::vector CBattleInfoCallback::battleGetAvailableHexes(const battle::Unit * unit) const { @@ -663,7 +657,7 @@ bool CBattleInfoCallback::battleCanAttack(const CStack * stack, const CStack * t auto &id = stack->getCreature()->idNumber; if (id == CreatureID::FIRST_AID_TENT || id == CreatureID::CATAPULT) return false; - + return target->alive(); } @@ -671,7 +665,7 @@ bool CBattleInfoCallback::battleCanShoot(const battle::Unit * attacker) const { RETURN_IF_NOT_BATTLE(false); - if (battleTacticDist()) //no shooting during tactics + if(battleTacticDist()) //no shooting during tactics return false; if (!attacker) @@ -681,12 +675,12 @@ bool CBattleInfoCallback::battleCanShoot(const battle::Unit * attacker) const //forgetfulness TConstBonusListPtr forgetfulList = attacker->getBonuses(Selector::type()(Bonus::FORGETFULL)); - if (!forgetfulList->empty()) + if(!forgetfulList->empty()) { int forgetful = forgetfulList->valOfBonuses(Selector::type()(Bonus::FORGETFULL)); //advanced+ level - if (forgetful > 1) + if(forgetful > 1) return false; } @@ -764,7 +758,7 @@ TDmgRange CBattleInfoCallback::calculateDmgRange(const BattleAttackInfo & info) attackDefenceDifference += info.attacker->getAttack(info.shooting) * multAttackReduction; double multDefenceReduction = 1.0 - battleBonusValue(attackerBonuses, Selector::type()(Bonus::ENEMY_DEFENCE_REDUCTION)) / 100.0; - attackDefenceDifference -= info.defender->getDefence(info.shooting) * multDefenceReduction; + attackDefenceDifference -= info.defender->getDefense(info.shooting) * multDefenceReduction; const std::string cachingStrSlayer = "type_SLAYER"; static const auto selectorSlayer = Selector::type()(Bonus::SLAYER); @@ -792,13 +786,13 @@ TDmgRange CBattleInfoCallback::calculateDmgRange(const BattleAttackInfo & info) if(isAffected) { - attackDefenceDifference += SpellID(SpellID::SLAYER).toSpell()->getPower(spLevel); + attackDefenceDifference += SpellID(SpellID::SLAYER).toSpell()->getLevelPower(spLevel); if(info.attacker->hasBonusOfType(Bonus::SPECIAL_PECULIAR_ENCHANT, SpellID::SLAYER)) { ui8 attackerTier = info.attacker->unitType()->level; ui8 specialtyBonus = std::max(5 - attackerTier, 0); attackDefenceDifference += specialtyBonus; - } + } } } @@ -1027,21 +1021,21 @@ std::vector> CBattleInfoCallback::battl return obstacles; } -std::vector> CBattleInfoCallback::getAllAffectedObstaclesByStack(const CStack * stack) const +std::vector> CBattleInfoCallback::getAllAffectedObstaclesByStack(const battle::Unit * unit) const { std::vector> affectedObstacles = std::vector>(); RETURN_IF_NOT_BATTLE(affectedObstacles); - if(stack->alive()) + if(unit->alive()) { - affectedObstacles = battleGetAllObstaclesOnPos(stack->getPosition(), false); - if(stack->doubleWide()) + affectedObstacles = battleGetAllObstaclesOnPos(unit->getPosition(), false); + if(unit->doubleWide()) { - BattleHex otherHex = stack->occupiedHex(stack->getPosition()); + BattleHex otherHex = unit->occupiedHex(unit->getPosition()); if(otherHex.isValid()) for(auto & i : battleGetAllObstaclesOnPos(otherHex, false)) affectedObstacles.push_back(i); } - for(auto hex : stack->getHexes()) + for(auto hex : unit->getHexes()) if(hex == ESiegeHex::GATE_BRIDGE) if(battleGetGateState() == EGateState::OPENED || battleGetGateState() == EGateState::DESTROYED) for(int i=0; i CBattleInfoCallback::getNearestStack( BattleHex CBattleInfoCallback::getAvaliableHex(CreatureID creID, ui8 side, int initialPos) const { - bool twoHex = VLC->creh->creatures[creID]->isDoubleWide(); - //bool flying = VLC->creh->creatures[creID]->isFlying(); + bool twoHex = VLC->creh->objects[creID]->isDoubleWide(); int pos; if (initialPos > -1) @@ -1437,7 +1430,7 @@ AttackableTiles CBattleInfoCallback::getPotentiallyAttackableHexes (const battl auto st = battleGetUnitByPos(tile, true); if(st && st != attacker) { - at.friendlyCreaturePositions.insert(tile); + at.friendlyCreaturePositions.insert(tile); } } } @@ -1446,34 +1439,34 @@ AttackableTiles CBattleInfoCallback::getPotentiallyAttackableHexes (const battl int pos = BattleHex::mutualPosition(destinationTile, hex); if (pos > -1) //only adjacent hexes are subject of dragon breath calculation { - std::vector hexes; //only one, in fact - int pseudoVector = destinationTile.hex - hex; - switch (pseudoVector) - { - case 1: - case -1: - BattleHex::checkAndPush(destinationTile.hex + pseudoVector, hexes); - break; - case WN: //17 //left-down or right-down - case -WN: //-17 //left-up or right-up - case WN + 1: //18 //right-down - case -WN + 1: //-16 //right-up - BattleHex::checkAndPush(destinationTile.hex + pseudoVector + (((hex / WN) % 2) ? 1 : -1), hexes); - break; - case WN - 1: //16 //left-down - case -WN - 1: //-18 //left-up - BattleHex::checkAndPush(destinationTile.hex + pseudoVector + (((hex / WN) % 2) ? 1 : 0), hexes); - break; - } - for (BattleHex tile : hexes) - { - //friendly stacks can also be damaged by Dragon Breath + std::vector hexes; //only one, in fact + int pseudoVector = destinationTile.hex - hex; + switch(pseudoVector) + { + case 1: + case -1: + BattleHex::checkAndPush(destinationTile.hex + pseudoVector, hexes); + break; + case WN: //17 //left-down or right-down + case -WN: //-17 //left-up or right-up + case WN + 1: //18 //right-down + case -WN + 1: //-16 //right-up + BattleHex::checkAndPush(destinationTile.hex + pseudoVector + (((hex / WN) % 2) ? 1 : -1), hexes); + break; + case WN - 1: //16 //left-down + case -WN - 1: //-18 //left-up + BattleHex::checkAndPush(destinationTile.hex + pseudoVector + (((hex / WN) % 2) ? 1 : 0), hexes); + break; + } + for(BattleHex tile : hexes) + { + //friendly stacks can also be damaged by Dragon Breath auto st = battleGetUnitByPos(tile, true); if (st != nullptr) - at.friendlyCreaturePositions.insert(tile); - } + at.friendlyCreaturePositions.insert(tile); } } + } return at; } @@ -1689,17 +1682,17 @@ std::vector CBattleInfoCallback::getAttackableBattleHexes() const return attackableBattleHexes; } -ui32 CBattleInfoCallback::battleGetSpellCost(const CSpell * sp, const CGHeroInstance * caster) const +int32_t CBattleInfoCallback::battleGetSpellCost(const spells::Spell * sp, const CGHeroInstance * caster) const { RETURN_IF_NOT_BATTLE(-1); //TODO should be replaced using bonus system facilities (propagation onto battle node) - ui32 ret = caster->getSpellCost(sp); + int32_t ret = caster->getSpellCost(sp); //checking for friendly stacks reducing cost of the spell and //enemy stacks increasing it - si32 manaReduction = 0; - si32 manaIncrease = 0; + int32_t manaReduction = 0; + int32_t manaIncrease = 0; for(auto unit : battleAliveUnits()) { diff --git a/lib/battle/CBattleInfoCallback.h b/lib/battle/CBattleInfoCallback.h index 319b0186f..146cf038d 100644 --- a/lib/battle/CBattleInfoCallback.h +++ b/lib/battle/CBattleInfoCallback.h @@ -8,10 +8,12 @@ * */ #pragma once + +#include + #include "CCallbackBase.h" #include "ReachabilityInfo.h" #include "BattleAttackInfo.h" -#include "../spells/Magic.h" class CGHeroInstance; class CStack; @@ -21,6 +23,19 @@ struct CObstacleInstance; class IBonusBearer; class CRandomGenerator; +namespace scripting +{ + class Context; + class Pool; + class Script; +} + +namespace spells +{ + class Caster; + class Spell; +} + struct DLL_LINKAGE AttackableTiles { std::set hostileCreaturePositions; @@ -57,14 +72,14 @@ public: RANDOM_GENIE, RANDOM_AIMED }; - boost::optional battleIsFinished() const; //return none if battle is ongoing; otherwise the victorious side (0/1) or 2 if it is a draw + boost::optional battleIsFinished() const override; //return none if battle is ongoing; otherwise the victorious side (0/1) or 2 if it is a draw - std::vector> battleGetAllObstaclesOnPos(BattleHex tile, bool onlyBlocking = true) const; //blocking obstacles makes tile inaccessible, others cause special effects (like Land Mines, Moat, Quicksands) - std::vector> getAllAffectedObstaclesByStack(const CStack * stack) const; + std::vector> battleGetAllObstaclesOnPos(BattleHex tile, bool onlyBlocking = true) const override; + std::vector> getAllAffectedObstaclesByStack(const battle::Unit * unit) const override; const CStack * battleGetStackByPos(BattleHex pos, bool onlyAlive = true) const; - const battle::Unit * battleGetUnitByPos(BattleHex pos, bool onlyAlive = true) const; + const battle::Unit * battleGetUnitByPos(BattleHex pos, bool onlyAlive = true) const override; ///returns all alive units excluding turrets battle::Units battleAliveUnits() const; @@ -73,8 +88,6 @@ public: void battleGetTurnOrder(std::vector & out, const size_t maxUnits, const int maxTurns, const int turn = 0, int8_t lastMoved = -1) const; - void battleGetStackCountOutsideHexes(bool *ac) const; // returns hexes which when in front of a stack cause us to move the amount box back - ///returns reachable hexes (valid movement destinations), DOES contain stack current position std::vector battleGetAvailableHexes(const battle::Unit * unit, bool addOccupiable, std::vector * attackable) const; @@ -109,7 +122,7 @@ public: si8 battleMinSpellLevel(ui8 side) const; //calculates maximum spell level possible to be cast on battlefield - takes into account artifacts of both heroes; if no effects are set, 0 is returned si8 battleMaxSpellLevel(ui8 side) const; //calculates minimum spell level possible to be cast on battlefield - takes into account artifacts of both heroes; if no effects are set, 0 is returned - ui32 battleGetSpellCost(const CSpell * sp, const CGHeroInstance * caster) const; //returns cost of given spell + int32_t battleGetSpellCost(const spells::Spell * sp, const CGHeroInstance * caster) const; //returns cost of given spell ESpellCastProblem::ESpellCastProblem battleCanCastSpell(const spells::Caster * caster, spells::Mode mode) const; //returns true if there are no general issues preventing from casting a spell SpellID battleGetRandomStackSpell(CRandomGenerator & rand, const CStack * stack, ERandomSpell mode) const; diff --git a/lib/battle/CBattleInfoEssentials.h b/lib/battle/CBattleInfoEssentials.h index 7a14375ae..ab8fc1d4b 100644 --- a/lib/battle/CBattleInfoEssentials.h +++ b/lib/battle/CBattleInfoEssentials.h @@ -9,12 +9,11 @@ */ #pragma once #include "CCallbackBase.h" -#include "BattleHex.h" +#include "IBattleInfoCallback.h" class CGTownInstance; class CGHeroInstance; class CStack; -struct CObstacleInstance; class IBonusBearer; struct InfoAboutHero; class CArmedInstance; @@ -22,14 +21,6 @@ class CArmedInstance; typedef std::vector TStacks; typedef std::function TStackFilter; -namespace battle -{ - class IUnitInfo; - class Unit; - using Units = std::vector; - using UnitFilter = std::function; -} - namespace BattlePerspective { enum BattlePerspective @@ -41,7 +32,7 @@ namespace BattlePerspective }; } -class DLL_LINKAGE CBattleInfoEssentials : public virtual CCallbackBase +class DLL_LINKAGE CBattleInfoEssentials : public virtual CCallbackBase, public IBattleInfoCallback { protected: bool battleDoWeKnowAbout(ui8 side) const; @@ -55,8 +46,8 @@ public: BattlePerspective::BattlePerspective battleGetMySide() const; const IBonusBearer * getBattleNode() const; - ETerrainType battleTerrainType() const; - BFieldType battleGetBattlefieldType() const; + ETerrainType battleTerrainType() const override; + BFieldType battleGetBattlefieldType() const override; int32_t battleGetEnchanterCounter(ui8 side) const; std::vector > battleGetAllObstacles(boost::optional perspective = boost::none) const; //returns all obstacles on the battlefield @@ -70,18 +61,19 @@ public: * */ TStacks battleGetStacksIf(TStackFilter predicate) const; //deprecated - battle::Units battleGetUnitsIf(battle::UnitFilter predicate) const; + battle::Units battleGetUnitsIf(battle::UnitFilter predicate) const override; - const battle::Unit * battleGetUnitByID(uint32_t ID) const; - const battle::Unit * battleActiveUnit() const; + const battle::Unit * battleGetUnitByID(uint32_t ID) const override; + const battle::Unit * battleActiveUnit() const override; - uint32_t battleNextUnitId() const; + uint32_t battleNextUnitId() const override; bool battleHasNativeStack(ui8 side) const; const CGTownInstance * battleGetDefendedTown() const; //returns defended town if current battle is a siege, nullptr instead - si8 battleTacticDist() const; //returns tactic distance in current tactics phase; 0 if not in tactics phase - si8 battleGetTacticsSide() const; //returns which side is in tactics phase, undefined if none (?) + si8 battleTacticDist() const override; //returns tactic distance in current tactics phase; 0 if not in tactics phase + si8 battleGetTacticsSide() const override; //returns which side is in tactics phase, undefined if none (?) + bool battleCanFlee(PlayerColor player) const; bool battleCanSurrender(PlayerColor player) const; diff --git a/lib/battle/CCallbackBase.h b/lib/battle/CCallbackBase.h index 30119b840..43ff7cfc8 100644 --- a/lib/battle/CCallbackBase.h +++ b/lib/battle/CCallbackBase.h @@ -11,6 +11,7 @@ #include "../GameConstants.h" #define RETURN_IF_NOT_BATTLE(...) if(!duringBattle()) {logGlobal->error("%s called when no battle!", __FUNCTION__); return __VA_ARGS__; } +#define ASSERT_IF_CALLED_WITH_PLAYER if(!player) {logGlobal->error(BOOST_CURRENT_FUNCTION); assert(0);} class IBattleInfo; class BattleInfo; diff --git a/lib/battle/CObstacleInstance.cpp b/lib/battle/CObstacleInstance.cpp index e276a15e5..8be8fa090 100644 --- a/lib/battle/CObstacleInstance.cpp +++ b/lib/battle/CObstacleInstance.cpp @@ -218,5 +218,5 @@ int SpellCreatedObstacle::getAnimationYOffset(int imageHeight) const std::vector MoatObstacle::getAffectedTiles() const { - return VLC->townh->factions[ID]->town->moatHexes; + return (*VLC->townh)[ID]->town->moatHexes; } diff --git a/lib/battle/CPlayerBattleCallback.h b/lib/battle/CPlayerBattleCallback.h index 80eefc633..38ac80baa 100644 --- a/lib/battle/CPlayerBattleCallback.h +++ b/lib/battle/CPlayerBattleCallback.h @@ -10,8 +10,6 @@ #pragma once #include "CBattleInfoCallback.h" -#define ASSERT_IF_CALLED_WITH_PLAYER if(!player) {logGlobal->error(BOOST_CURRENT_FUNCTION); assert(0);} - class CGHeroInstance; class DLL_LINKAGE CPlayerBattleCallback : public CBattleInfoCallback diff --git a/lib/battle/CUnitState.cpp b/lib/battle/CUnitState.cpp index 0a6f4315a..248dcfd10 100644 --- a/lib/battle/CUnitState.cpp +++ b/lib/battle/CUnitState.cpp @@ -11,6 +11,8 @@ #include "CUnitState.h" +#include + #include "../NetPacks.h" #include "../CCreatureHandler.h" @@ -368,7 +370,6 @@ void CHealth::serializeJson(JsonSerializeFormat & handler) handler.serializeInt("resurrected", resurrected, 0); } - ///CUnitState CUnitState::CUnitState() : env(nullptr), @@ -468,9 +469,9 @@ int32_t CUnitState::getCasterUnitId() const return static_cast(unitId()); } -ui8 CUnitState::getSpellSchoolLevel(const spells::Spell * spell, int * outSelectedSchool) const +int32_t CUnitState::getSpellSchoolLevel(const spells::Spell * spell, int32_t * outSelectedSchool) const { - int skill = valOfBonuses(Selector::typeSubtype(Bonus::SPELLCASTER, spell->getIndex())); + int32_t skill = valOfBonuses(Selector::typeSubtype(Bonus::SPELLCASTER, spell->getIndex())); vstd::abetween(skill, 0, 3); return skill; } @@ -486,19 +487,19 @@ int64_t CUnitState::getSpecificSpellBonus(const spells::Spell * spell, int64_t b return base; } -int CUnitState::getEffectLevel(const spells::Spell * spell) const +int32_t CUnitState::getEffectLevel(const spells::Spell * spell) const { return getSpellSchoolLevel(spell); } -int CUnitState::getEffectPower(const spells::Spell * spell) const +int32_t CUnitState::getEffectPower(const spells::Spell * spell) const { return valOfBonuses(Bonus::CREATURE_SPELL_POWER) * getCount() / 100; } -int CUnitState::getEnchantPower(const spells::Spell * spell) const +int32_t CUnitState::getEnchantPower(const spells::Spell * spell) const { - int res = valOfBonuses(Bonus::CREATURE_ENCHANT_POWER); + int32_t res = valOfBonuses(Bonus::CREATURE_ENCHANT_POWER); if(res <= 0) res = 3;//default for creatures return res; @@ -509,7 +510,7 @@ int64_t CUnitState::getEffectValue(const spells::Spell * spell) const return static_cast(getCount()) * valOfBonuses(Bonus::SPECIFIC_SPELL_POWER, spell->getIndex()); } -const PlayerColor CUnitState::getOwner() const +PlayerColor CUnitState::getCasterOwner() const { return env->unitEffectiveOwner(this); } @@ -703,7 +704,7 @@ int CUnitState::getAttack(bool ranged) const return ret; } -int CUnitState::getDefence(bool ranged) const +int CUnitState::getDefense(bool ranged) const { if(!inFrenzy->empty()) { @@ -735,9 +736,6 @@ std::shared_ptr CUnitState::acquireState() const void CUnitState::serializeJson(JsonSerializeFormat & handler) { - if(!handler.saving) - reset(); - handler.serializeBool("cloned", cloned); handler.serializeBool("defending", defending); handler.serializeBool("defendingAnim", defendingAnim); @@ -807,8 +805,8 @@ void CUnitState::load(const JsonNode & data) { //TODO: use instance resolver reset(); - JsonDeserializer deser(nullptr, data); - deser.serializeStruct("state", *this); + JsonDeserializer deser(nullptr, data); + deser.serializeStruct("state", *this); } void CUnitState::damage(int64_t & amount) @@ -942,7 +940,7 @@ int32_t CUnitStateDetached::unitBaseAmount() const return unit->unitBaseAmount(); } -void CUnitStateDetached::spendMana(const spells::PacketSender * server, const int spellCost) const +void CUnitStateDetached::spendMana(ServerCallback * server, const int spellCost) const { if(spellCost != 1) logGlobal->warn("Unexpected spell cost %d for creature", spellCost); diff --git a/lib/battle/CUnitState.h b/lib/battle/CUnitState.h index f50a3fa8f..46fee68b3 100644 --- a/lib/battle/CUnitState.h +++ b/lib/battle/CUnitState.h @@ -176,17 +176,17 @@ public: int32_t getCasterUnitId() const override; - ui8 getSpellSchoolLevel(const spells::Spell * spell, int * outSelectedSchool = nullptr) const override; - int getEffectLevel(const spells::Spell * spell) const override; + int32_t getSpellSchoolLevel(const spells::Spell * spell, int32_t * outSelectedSchool = nullptr) const override; + int32_t getEffectLevel(const spells::Spell * spell) const override; int64_t getSpellBonus(const spells::Spell * spell, int64_t base, const Unit * affectedStack) const override; int64_t getSpecificSpellBonus(const spells::Spell * spell, int64_t base) const override; - int getEffectPower(const spells::Spell * spell) const override; - int getEnchantPower(const spells::Spell * spell) const override; + int32_t getEffectPower(const spells::Spell * spell) const override; + int32_t getEnchantPower(const spells::Spell * spell) const override; int64_t getEffectValue(const spells::Spell * spell) const override; - const PlayerColor getOwner() const override; + PlayerColor getCasterOwner() const override; void getCasterName(MetaString & text) const override; void getCastDescription(const spells::Spell * spell, const std::vector & attacked, MetaString & text) const override; @@ -230,7 +230,7 @@ public: int getMaxDamage(bool ranged) const override; int getAttack(bool ranged) const override; - int getDefence(bool ranged) const override; + int getDefense(bool ranged) const override; void save(JsonNode & data) override; void load(const JsonNode & data) override; @@ -286,7 +286,7 @@ public: int32_t unitBaseAmount() const override; - void spendMana(const spells::PacketSender * server, const int spellCost) const override; + void spendMana(ServerCallback * server, const int spellCost) const override; private: const IUnitInfo * unit; diff --git a/lib/battle/IBattleInfoCallback.h b/lib/battle/IBattleInfoCallback.h new file mode 100644 index 000000000..92f50ebe1 --- /dev/null +++ b/lib/battle/IBattleInfoCallback.h @@ -0,0 +1,60 @@ +/* + * IBattleInfoCallback.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 "BattleHex.h" + +struct CObstacleInstance; +class BFieldType; +class ETerrainType; + +namespace battle +{ + class IUnitInfo; + class Unit; + using Units = std::vector; + using UnitFilter = std::function; +} + +namespace scripting +{ + class Pool; +} + +class DLL_LINKAGE IBattleInfoCallback +{ +public: + virtual scripting::Pool * getContextPool() const = 0; + + virtual ETerrainType battleTerrainType() const = 0; + virtual BFieldType battleGetBattlefieldType() const = 0; + + ///return none if battle is ongoing; otherwise the victorious side (0/1) or 2 if it is a draw + virtual boost::optional battleIsFinished() const = 0; + + virtual si8 battleTacticDist() const = 0; //returns tactic distance in current tactics phase; 0 if not in tactics phase + virtual si8 battleGetTacticsSide() const = 0; //returns which side is in tactics phase, undefined if none (?) + + virtual uint32_t battleNextUnitId() const = 0; + + virtual battle::Units battleGetUnitsIf(battle::UnitFilter predicate) const = 0; + + virtual const battle::Unit * battleGetUnitByID(uint32_t ID) const = 0; + virtual const battle::Unit * battleGetUnitByPos(BattleHex pos, bool onlyAlive = true) const = 0; + + virtual const battle::Unit * battleActiveUnit() const = 0; + + //blocking obstacles makes tile inaccessible, others cause special effects (like Land Mines, Moat, Quicksands) + virtual std::vector> battleGetAllObstaclesOnPos(BattleHex tile, bool onlyBlocking = true) const = 0; + virtual std::vector> getAllAffectedObstaclesByStack(const battle::Unit * unit) const = 0; +}; + + diff --git a/lib/battle/IBattleState.h b/lib/battle/IBattleState.h index 5f6f3df0e..8aa80e4d2 100644 --- a/lib/battle/IBattleState.h +++ b/lib/battle/IBattleState.h @@ -27,7 +27,6 @@ namespace battle class UnitInfo; } - class DLL_LINKAGE IBattleInfo { public: @@ -79,6 +78,7 @@ public: virtual void setUnitState(uint32_t id, const JsonNode & data, int64_t healthDelta) = 0; virtual void moveUnit(uint32_t id, BattleHex destination) = 0; virtual void removeUnit(uint32_t id) = 0; + virtual void updateUnit(uint32_t id, const JsonNode & data) = 0; virtual void addUnitBonus(uint32_t id, const std::vector & bonus) = 0; virtual void updateUnitBonus(uint32_t id, const std::vector & bonus) = 0; diff --git a/lib/battle/SideInBattle.h b/lib/battle/SideInBattle.h index a0c821a38..9f571e614 100644 --- a/lib/battle/SideInBattle.h +++ b/lib/battle/SideInBattle.h @@ -20,7 +20,7 @@ struct DLL_LINKAGE SideInBattle const CArmedInstance * armyObject; //adv. map object with army that participates in battle; may be same as hero uint32_t castSpellsCount; //how many spells each side has been cast this turn - std::vector usedSpellsHistory; //every time hero casts spell, it's inserted here -> eagle eye skill + std::vector usedSpellsHistory; //every time hero casts spell, it's inserted here -> eagle eye skill int32_t enchanterCounter; //tends to pass through 0, so sign is needed SideInBattle(); diff --git a/lib/battle/Unit.h b/lib/battle/Unit.h index 5793e9239..26d834d93 100644 --- a/lib/battle/Unit.h +++ b/lib/battle/Unit.h @@ -10,11 +10,12 @@ #pragma once +#include + #include "../HeroBonus.h" #include "IUnitInfo.h" #include "BattleHex.h" -#include "../spells/Magic.h" struct MetaString; class JsonNode; diff --git a/lib/events/ApplyDamage.cpp b/lib/events/ApplyDamage.cpp new file mode 100644 index 000000000..8ba3de747 --- /dev/null +++ b/lib/events/ApplyDamage.cpp @@ -0,0 +1,62 @@ +/* + * ApplyDamage.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 + +#include "ApplyDamage.h" + +#include "../../lib/NetPacks.h" + +namespace events +{ + +SubscriptionRegistry * ApplyDamage::getRegistry() +{ + static std::unique_ptr> Instance = make_unique>(); + return Instance.get(); +} + +CApplyDamage::CApplyDamage(const Environment * env_, BattleStackAttacked * pack_, std::shared_ptr target_) + : env(env_), + pack(pack_), + target(target_) + +{ + initalDamage = pack->damageAmount; +} + +bool CApplyDamage::isEnabled() const +{ + return true; +} + +int64_t CApplyDamage::getInitalDamage() const +{ + return initalDamage; +} + +int64_t CApplyDamage::getDamage() const +{ + return pack->damageAmount; +} + +void CApplyDamage::setDamage(int64_t value) +{ + pack->damageAmount = value; +} + +const battle::Unit * CApplyDamage::getTarget() const +{ + return target.get(); +} + + +}; diff --git a/lib/events/ApplyDamage.h b/lib/events/ApplyDamage.h new file mode 100644 index 000000000..c56d730fe --- /dev/null +++ b/lib/events/ApplyDamage.h @@ -0,0 +1,39 @@ +/* + * ApplyDamage.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 + +namespace events +{ + +class DLL_LINKAGE CApplyDamage : public ApplyDamage +{ +public: + CApplyDamage(const Environment * env_, BattleStackAttacked * pack_, std::shared_ptr target_); + + bool isEnabled() const override; + int64_t getInitalDamage() const override; + int64_t getDamage() const override; + void setDamage(int64_t value) override; + const battle::Unit * getTarget() const override; +private: + int64_t initalDamage; + + const Environment * env; + BattleStackAttacked * pack; + std::shared_ptr target; +}; + +} + + + diff --git a/lib/events/GameResumed.cpp b/lib/events/GameResumed.cpp new file mode 100644 index 000000000..b7d98bb77 --- /dev/null +++ b/lib/events/GameResumed.cpp @@ -0,0 +1,39 @@ +/* + * GameResumed.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 "GameResumed.h" + +#include + +namespace events +{ + +SubscriptionRegistry * GameResumed::getRegistry() +{ + static std::unique_ptr> Instance = make_unique>(); + return Instance.get(); +} + +void GameResumed::defaultExecute(const EventBus * bus) +{ + CGameResumed event; + bus->executeEvent(event); +} + +CGameResumed::CGameResumed() = default; + +bool CGameResumed::isEnabled() const +{ + return true; +} + + +} diff --git a/lib/events/GameResumed.h b/lib/events/GameResumed.h new file mode 100644 index 000000000..c0c36e3be --- /dev/null +++ b/lib/events/GameResumed.h @@ -0,0 +1,26 @@ +/* + * GameResumed.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 + +namespace events +{ + +class DLL_LINKAGE CGameResumed : public GameResumed +{ +public: + CGameResumed(); + + bool isEnabled() const override; +}; + +} diff --git a/lib/events/ObjectVisitEnded.cpp b/lib/events/ObjectVisitEnded.cpp new file mode 100644 index 000000000..6c8f2e2af --- /dev/null +++ b/lib/events/ObjectVisitEnded.cpp @@ -0,0 +1,55 @@ +/* + * ObjectVisitEnded.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 "ObjectVisitEnded.h" + +#include + + +namespace events +{ + +SubscriptionRegistry * ObjectVisitEnded::getRegistry() +{ + static std::unique_ptr Instance = make_unique(); + return Instance.get(); +} + +void ObjectVisitEnded::defaultExecute(const EventBus * bus, const ExecHandler & execHandler, + const PlayerColor & player, const ObjectInstanceID & heroId) +{ + CObjectVisitEnded event(player, heroId); + bus->executeEvent(event, execHandler); +} + +CObjectVisitEnded::CObjectVisitEnded(const PlayerColor & player_, const ObjectInstanceID & heroId_) + : player(player_), + heroId(heroId_) +{ + +} + +bool CObjectVisitEnded::isEnabled() const +{ + return true; +} + +PlayerColor CObjectVisitEnded::getPlayer() const +{ + return player; +} + +ObjectInstanceID CObjectVisitEnded::getHero() const +{ + return heroId; +} + +} diff --git a/lib/events/ObjectVisitEnded.h b/lib/events/ObjectVisitEnded.h new file mode 100644 index 000000000..123c0d03e --- /dev/null +++ b/lib/events/ObjectVisitEnded.h @@ -0,0 +1,34 @@ +/* + * ObjectVisitEnded.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 + +#include "../GameConstants.h" + +namespace events +{ + +class DLL_LINKAGE CObjectVisitEnded : public ObjectVisitEnded +{ +public: + CObjectVisitEnded(const PlayerColor & player_, const ObjectInstanceID & heroId_); + + PlayerColor getPlayer() const override; + ObjectInstanceID getHero() const override; + bool isEnabled() const override; +private: + PlayerColor player; + ObjectInstanceID heroId; + bool enabled; +}; + +} diff --git a/lib/events/ObjectVisitStarted.cpp b/lib/events/ObjectVisitStarted.cpp new file mode 100644 index 000000000..863da819d --- /dev/null +++ b/lib/events/ObjectVisitStarted.cpp @@ -0,0 +1,67 @@ +/* + * ObjectVisitStarted.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 "ObjectVisitStarted.h" + +#include + + +namespace events +{ + +SubscriptionRegistry * ObjectVisitStarted::getRegistry() +{ + static std::unique_ptr Instance = make_unique(); + return Instance.get(); +} + +void ObjectVisitStarted::defaultExecute(const EventBus * bus, const ExecHandler & execHandler, + const PlayerColor & player, const ObjectInstanceID & heroId, const ObjectInstanceID & objId) +{ + CObjectVisitStarted event(player, heroId, objId); + bus->executeEvent(event, execHandler); +} + +CObjectVisitStarted::CObjectVisitStarted(const PlayerColor & player_, const ObjectInstanceID & heroId_, const ObjectInstanceID & objId_) + : player(player_), + heroId(heroId_), + objId(objId_), + enabled(true) +{ +} + +PlayerColor CObjectVisitStarted::getPlayer() const +{ + return player; +} + +ObjectInstanceID CObjectVisitStarted::getHero() const +{ + return heroId; +} + +ObjectInstanceID CObjectVisitStarted::getObject() const +{ + return objId; +} + +bool CObjectVisitStarted::isEnabled() const +{ + return enabled; +} + +void CObjectVisitStarted::setEnabled(bool enable) +{ + enabled = enable; +} + + +} diff --git a/lib/events/ObjectVisitStarted.h b/lib/events/ObjectVisitStarted.h new file mode 100644 index 000000000..d3754bddc --- /dev/null +++ b/lib/events/ObjectVisitStarted.h @@ -0,0 +1,38 @@ +/* + * ObjectVisitStarted.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 + +#include "../GameConstants.h" + +namespace events +{ + +class DLL_LINKAGE CObjectVisitStarted : public ObjectVisitStarted +{ +public: + CObjectVisitStarted(const PlayerColor & player_, const ObjectInstanceID & heroId_, const ObjectInstanceID & objId_); + + PlayerColor getPlayer() const override; + ObjectInstanceID getHero() const override; + ObjectInstanceID getObject() const override; + + bool isEnabled() const override; + void setEnabled(bool enable) override; +private: + PlayerColor player; + ObjectInstanceID heroId; + ObjectInstanceID objId; + bool enabled; +}; + +} diff --git a/lib/events/PlayerGotTurn.cpp b/lib/events/PlayerGotTurn.cpp new file mode 100644 index 000000000..ae5453a7a --- /dev/null +++ b/lib/events/PlayerGotTurn.cpp @@ -0,0 +1,61 @@ +/* + * PlayerGotTurn.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 "PlayerGotTurn.h" + +#include + +namespace events +{ + +SubscriptionRegistry * PlayerGotTurn::getRegistry() +{ + static std::unique_ptr> Instance = make_unique>(); + return Instance.get(); +} + +void PlayerGotTurn::defaultExecute(const EventBus * bus, const ExecHandler & execHandler, PlayerColor & player) +{ + CPlayerGotTurn event; + event.setPlayer(player); + bus->executeEvent(event, execHandler); + player = event.getPlayer(); +} + +CPlayerGotTurn::CPlayerGotTurn() = default; + +bool CPlayerGotTurn::isEnabled() const +{ + return true; +} + +PlayerColor CPlayerGotTurn::getPlayer() const +{ + return player; +} + +void CPlayerGotTurn::setPlayer(const PlayerColor & value) +{ + player = value; +} + +int32_t CPlayerGotTurn::getPlayerIndex() const +{ + return player.getNum(); +} + +void CPlayerGotTurn::setPlayerIndex(int32_t value) +{ + player = PlayerColor(value); +} + + +} diff --git a/lib/events/PlayerGotTurn.h b/lib/events/PlayerGotTurn.h new file mode 100644 index 000000000..8258f25e6 --- /dev/null +++ b/lib/events/PlayerGotTurn.h @@ -0,0 +1,36 @@ +/* + * PlayerGotTurn.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 + +#include "../GameConstants.h" + +namespace events +{ + +class DLL_LINKAGE CPlayerGotTurn : public PlayerGotTurn +{ +public: + CPlayerGotTurn(); + + bool isEnabled() const override; + + PlayerColor getPlayer() const override; + void setPlayer(const PlayerColor & value) override; + + int32_t getPlayerIndex() const override; + void setPlayerIndex(int32_t value) override; +private: + PlayerColor player; +}; + +} diff --git a/lib/events/TurnStarted.cpp b/lib/events/TurnStarted.cpp new file mode 100644 index 000000000..015ab9ca0 --- /dev/null +++ b/lib/events/TurnStarted.cpp @@ -0,0 +1,39 @@ +/* + * TurnStarted.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 "TurnStarted.h" + +#include + +namespace events +{ + +SubscriptionRegistry * TurnStarted::getRegistry() +{ + static std::unique_ptr> Instance = make_unique>(); + return Instance.get(); +} + +void TurnStarted::defaultExecute(const EventBus * bus) +{ + CTurnStarted event; + bus->executeEvent(event); +} + +CTurnStarted::CTurnStarted() = default; + +bool CTurnStarted::isEnabled() const +{ + return true; +} + + +} diff --git a/lib/events/TurnStarted.h b/lib/events/TurnStarted.h new file mode 100644 index 000000000..a7bb3cb98 --- /dev/null +++ b/lib/events/TurnStarted.h @@ -0,0 +1,26 @@ +/* + * TurnStarted.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 + +namespace events +{ + +class DLL_LINKAGE CTurnStarted : public TurnStarted +{ +public: + CTurnStarted(); + bool isEnabled() const override; + static void defaultExecute(const EventBus * bus); +}; + +} diff --git a/lib/filesystem/Filesystem.cpp b/lib/filesystem/Filesystem.cpp index ac777b42a..2ad87018d 100644 --- a/lib/filesystem/Filesystem.cpp +++ b/lib/filesystem/Filesystem.cpp @@ -205,7 +205,19 @@ void CResourceHandler::load(const std::string &fsConfigURI) void CResourceHandler::addFilesystem(const std::string & parent, const std::string & identifier, ISimpleResourceLoader * loader) { - assert(knownLoaders.count(identifier) == 0); + if(knownLoaders.count(identifier) != 0) + { + logMod->error("[CRITICAL] Virtual filesystem %s already loaded!", identifier); + delete loader; + return; + } + + if(knownLoaders.count(parent) == 0) + { + logMod->error("[CRITICAL] Parent virtual filesystem %s for %s not found!", parent, identifier); + delete loader; + return; + } auto list = dynamic_cast(knownLoaders.at(parent)); assert(list); diff --git a/lib/filesystem/ResourceID.cpp b/lib/filesystem/ResourceID.cpp index f85340f99..7b70d960c 100644 --- a/lib/filesystem/ResourceID.cpp +++ b/lib/filesystem/ResourceID.cpp @@ -156,7 +156,9 @@ EResType::Type EResTypeHelper::getTypeFromExtension(std::string extension) {".ERM", EResType::ERM}, {".ERT", EResType::ERT}, {".ERS", EResType::ERS}, - {".VMAP", EResType::MAP} + {".VMAP", EResType::MAP}, + {".VERM", EResType::ERM}, + {".LUA", EResType::LUA} }; auto iter = stringToRes.find(extension); diff --git a/lib/filesystem/ResourceID.h b/lib/filesystem/ResourceID.h index 901a6b913..4310b5911 100644 --- a/lib/filesystem/ResourceID.h +++ b/lib/filesystem/ResourceID.h @@ -56,7 +56,8 @@ namespace EResType ERT, ERS, OTHER, - UNDEFINED + UNDEFINED, + LUA }; } diff --git a/lib/mapObjects/CArmedInstance.cpp b/lib/mapObjects/CArmedInstance.cpp index d130db889..e5185c226 100644 --- a/lib/mapObjects/CArmedInstance.cpp +++ b/lib/mapObjects/CArmedInstance.cpp @@ -26,7 +26,7 @@ void CArmedInstance::randomizeArmy(int type) { int level = randID / 2; bool upgrade = randID % 2; - elem.second->setType(VLC->townh->factions[type]->town->creatures[level][upgrade]); + elem.second->setType((*VLC->townh)[type]->town->creatures[level][upgrade]); randID = -1; } @@ -67,7 +67,7 @@ void CArmedInstance::updateMoraleBonusFromArmy() for(auto slot : Slots()) { const CStackInstance * inst = slot.second; - const CCreature * creature = VLC->creh->creatures[inst->getCreatureID()]; + const CCreature * creature = VLC->creh->objects[inst->getCreatureID()]; factions.insert(creature->faction); // Check for undead flag instead of faction (undead mummies are neutral) @@ -82,7 +82,7 @@ void CArmedInstance::updateMoraleBonusFromArmy() for(TFaction f : factions) { - if (VLC->townh->factions[f]->alignment != EAlignment::EVIL) + if ((*VLC->townh)[f]->alignment != EAlignment::EVIL) mixableFactions++; } if (mixableFactions > 0) @@ -129,7 +129,7 @@ void CArmedInstance::armyChanged() CBonusSystemNode * CArmedInstance::whereShouldBeAttached(CGameState *gs) { if(tempOwner < PlayerColor::PLAYER_LIMIT) - return gs->getPlayer(tempOwner); + return gs->getPlayerState(tempOwner); else return &gs->globalEffects; } diff --git a/lib/mapObjects/CBank.cpp b/lib/mapObjects/CBank.cpp index 7e1ed8bff..69e4d6459 100644 --- a/lib/mapObjects/CBank.cpp +++ b/lib/mapObjects/CBank.cpp @@ -11,11 +11,13 @@ #include "StdInc.h" #include "CBank.h" +#include +#include + #include "../NetPacks.h" #include "../CGeneralTextHandler.h" #include "../CSoundBase.h" #include "CommonConstructors.h" -#include "../spells/CSpellHandler.h" #include "../IGameCallback.h" #include "../CGameState.h" @@ -216,7 +218,7 @@ void CBank::doVisit(const CGHeroInstance * hero) const } cb->showInfoDialog(&iw); } - + //grant resources if (bc) @@ -238,7 +240,7 @@ void CBank::doVisit(const CGHeroInstance * hero) const iw.components.push_back(Component(Component::ARTIFACT, elem, 0, 0)); loot << "%s"; loot.addReplacement(MetaString::ART_NAMES, elem); - cb->giveHeroNewArtifact(hero, VLC->arth->artifacts[elem], ArtifactPosition::FIRST_AVAILABLE); + cb->giveHeroNewArtifact(hero, VLC->arth->objects[elem], ArtifactPosition::FIRST_AVAILABLE); } //display loot if (!iw.components.empty()) @@ -272,9 +274,9 @@ void CBank::doVisit(const CGHeroInstance * hero) const } for(const SpellID & spellId : bc->spells) { - const CSpell * spell = spellId.toSpell(); + auto spell = spellId.toSpell(VLC->spells()); iw.text.addTxt(MetaString::SPELL_NAME, spellId); - if(spell->level <= hero->maxSpellLevel()) + if(spell->getLevel() <= hero->maxSpellLevel()) { if(hero->canLearnSpell(spell)) { diff --git a/lib/mapObjects/CGHeroInstance.cpp b/lib/mapObjects/CGHeroInstance.cpp index 41294ce48..b6641b9eb 100644 --- a/lib/mapObjects/CGHeroInstance.cpp +++ b/lib/mapObjects/CGHeroInstance.cpp @@ -11,6 +11,9 @@ #include "StdInc.h" #include "CGHeroInstance.h" +#include +#include + #include "../NetPacks.h" #include "../CGeneralTextHandler.h" #include "../CHeroHandler.h" @@ -95,7 +98,7 @@ ui32 CGHeroInstance::getTileCost(const TerrainTile & dest, const TerrainTile & f ) { static const CSelector selectorPATHFINDING = Selector::typeSubtype(Bonus::SECONDARY_SKILL_PREMY, SecondarySkill::PATHFINDING); - static const std::string keyPATHFINDING = "type_" + std::to_string((si32)Bonus::SECONDARY_SKILL_PREMY) + "s_" + std::to_string((si32)SecondarySkill::PATHFINDING); + static const std::string keyPATHFINDING = "type_"+std::to_string((si32)Bonus::SECONDARY_SKILL_PREMY)+"s_"+std::to_string((si32)SecondarySkill::PATHFINDING); ret = VLC->heroh->terrCosts[from.terType]; ret -= valOfBonuses(selectorPATHFINDING, keyPATHFINDING); @@ -251,6 +254,11 @@ CGHeroInstance::CGHeroInstance() secSkills.push_back(std::make_pair(SecondarySkill::DEFAULT, -1)); } +PlayerColor CGHeroInstance::getOwner() const +{ + return tempOwner; +} + void CGHeroInstance::initHero(CRandomGenerator & rand, HeroTypeID SUBID) { subID = SUBID.getNum(); @@ -260,9 +268,9 @@ void CGHeroInstance::initHero(CRandomGenerator & rand, HeroTypeID SUBID) void CGHeroInstance::setType(si32 ID, si32 subID) { assert(ID == Obj::HERO); // just in case - type = VLC->heroh->heroes[subID]; + type = VLC->heroh->objects[subID]; portrait = type->imageIndex; - CGObjectInstance::setType(ID, type->heroClass->id); // to find object handler we must use heroClass->id + CGObjectInstance::setType(ID, type->heroClass->getIndex()); // to find object handler we must use heroClass->id this->subID = subID; // after setType subID used to store unique hero identify id. Check issue 2277 for details randomizeArmy(type->heroClass->faction); } @@ -271,10 +279,10 @@ void CGHeroInstance::initHero(CRandomGenerator & rand) { assert(validTypes(true)); if(!type) - type = VLC->heroh->heroes[subID]; + type = VLC->heroh->objects[subID]; if (ID == Obj::HERO) - appearance = VLC->objtypeh->getHandlerFor(Obj::HERO, type->heroClass->id)->getTemplates().front(); + appearance = VLC->objtypeh->getHandlerFor(Obj::HERO, type->heroClass->getIndex())->getTemplates().front(); if(!vstd::contains(spells, SpellID::PRESET)) //hero starts with a spell { @@ -512,7 +520,7 @@ void CGHeroInstance::initObj(CRandomGenerator & rand) if (ID != Obj::PRISON) { - auto customApp = VLC->objtypeh->getHandlerFor(ID, type->heroClass->id)->getOverride(cb->gameState()->getTile(visitablePos())->terType, this); + auto customApp = VLC->objtypeh->getHandlerFor(ID, type->heroClass->getIndex())->getOverride(cb->gameState()->getTile(visitablePos())->terType, this); if (customApp) appearance = customApp.get(); } @@ -577,11 +585,6 @@ void CGHeroInstance::setPropertyDer( ui8 what, ui32 val ) setStackCount(SlotID(0), val); } -TFaction CGHeroInstance::getFaction() const -{ - return type->heroClass->faction; -} - double CGHeroInstance::getFightingStrength() const { return sqrt((1.0 + 0.05*getPrimSkillLevel(PrimarySkill::ATTACK)) * (1.0 + 0.05*getPrimSkillLevel(PrimarySkill::DEFENSE))); @@ -613,13 +616,13 @@ int32_t CGHeroInstance::getCasterUnitId() const return -1; //TODO: special value for attacker/defender hero } -ui8 CGHeroInstance::getSpellSchoolLevel(const spells::Spell * spell, int * outSelectedSchool) const +int32_t CGHeroInstance::getSpellSchoolLevel(const spells::Spell * spell, int32_t * outSelectedSchool) const { - si16 skill = -1; //skill level + int32_t skill = -1; //skill level spell->forEachSchool([&, this](const spells::SchoolInfo & cnf, bool & stop) { - int thisSchool = std::max( + int32_t thisSchool = std::max( valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, cnf.skill), valOfBonuses(Bonus::MAGIC_SCHOOL_SKILL, 1 << ((ui8)cnf.id))); //FIXME: Bonus shouldn't be additive (Witchking Artifacts : Crown of Skies) if(thisSchool > skill) @@ -635,7 +638,7 @@ ui8 CGHeroInstance::getSpellSchoolLevel(const spells::Spell * spell, int * outSe vstd::amax(skill, 0); //in case we don't know any school vstd::amin(skill, 3); - return (ui8)skill; + return skill; } int64_t CGHeroInstance::getSpellBonus(const spells::Spell * spell, int64_t base, const battle::Unit * affectedStack) const @@ -666,7 +669,7 @@ int64_t CGHeroInstance::getSpecificSpellBonus(const spells::Spell * spell, int64 return base; } -int CGHeroInstance::getEffectLevel(const spells::Spell * spell) const +int32_t CGHeroInstance::getEffectLevel(const spells::Spell * spell) const { if(hasBonusOfType(Bonus::MAXED_SPELL, spell->getIndex())) return 3;//todo: recheck specialty from where this bonus is. possible bug @@ -674,12 +677,12 @@ int CGHeroInstance::getEffectLevel(const spells::Spell * spell) const return getSpellSchoolLevel(spell); } -int CGHeroInstance::getEffectPower(const spells::Spell * spell) const +int32_t CGHeroInstance::getEffectPower(const spells::Spell * spell) const { return getPrimSkillLevel(PrimarySkill::SPELL_POWER); } -int CGHeroInstance::getEnchantPower(const spells::Spell * spell) const +int32_t CGHeroInstance::getEnchantPower(const spells::Spell * spell) const { return getPrimSkillLevel(PrimarySkill::SPELL_POWER) + valOfBonuses(Bonus::SPELL_DURATION); } @@ -689,7 +692,7 @@ int64_t CGHeroInstance::getEffectValue(const spells::Spell * spell) const return 0; } -const PlayerColor CGHeroInstance::getOwner() const +PlayerColor CGHeroInstance::getCasterOwner() const { return tempOwner; } @@ -712,7 +715,7 @@ void CGHeroInstance::getCastDescription(const spells::Spell * spell, const std:: attacked.at(0)->addNameReplacement(text, true); } -void CGHeroInstance::spendMana(const spells::PacketSender * server, const int spellCost) const +void CGHeroInstance::spendMana(ServerCallback * server, const int spellCost) const { if(spellCost != 0) { @@ -721,16 +724,16 @@ void CGHeroInstance::spendMana(const spells::PacketSender * server, const int sp sm.hid = id; sm.val = -spellCost; - server->sendAndApply(&sm); + server->apply(&sm); } } -bool CGHeroInstance::canCastThisSpell(const CSpell * spell) const +bool CGHeroInstance::canCastThisSpell(const spells::Spell * spell) const { - const bool isAllowed = IObjectInterface::cb->isAllowed(0, spell->id); + const bool isAllowed = IObjectInterface::cb->isAllowed(0, spell->getIndex()); - const bool inSpellBook = vstd::contains(spells, spell->id) && hasSpellbook(); - const bool specificBonus = hasBonusOfType(Bonus::SPELL, spell->id); + const bool inSpellBook = vstd::contains(spells, spell->getId()) && hasSpellbook(); + const bool specificBonus = hasBonusOfType(Bonus::SPELL, spell->getIndex()); bool schoolBonus = false; @@ -742,13 +745,13 @@ bool CGHeroInstance::canCastThisSpell(const CSpell * spell) const } }); - const bool levelBonus = hasBonusOfType(Bonus::SPELLS_OF_LEVEL, spell->level); + const bool levelBonus = hasBonusOfType(Bonus::SPELLS_OF_LEVEL, spell->getLevel()); - if(spell->isSpecialSpell()) + if(spell->isSpecial()) { if(inSpellBook) {//hero has this spell in spellbook - logGlobal->error("Special spell %s in spellbook.", spell->name); + logGlobal->error("Special spell %s in spellbook.", spell->getName()); } return specificBonus; } @@ -758,7 +761,7 @@ bool CGHeroInstance::canCastThisSpell(const CSpell * spell) const { //hero has this spell in spellbook //it is normal if set in map editor, but trace it to possible debug of magic guild - logGlobal->trace("Banned spell %s in spellbook.", spell->name); + logGlobal->trace("Banned spell %s in spellbook.", spell->getName()); } return inSpellBook || specificBonus || schoolBonus || levelBonus; } @@ -768,32 +771,32 @@ bool CGHeroInstance::canCastThisSpell(const CSpell * spell) const } } -bool CGHeroInstance::canLearnSpell(const CSpell * spell) const +bool CGHeroInstance::canLearnSpell(const spells::Spell * spell) const { if(!hasSpellbook()) return false; - if(spell->level > maxSpellLevel()) //not enough wisdom + if(spell->getLevel() > maxSpellLevel()) //not enough wisdom return false; - if(vstd::contains(spells, spell->id))//already known + if(vstd::contains(spells, spell->getId()))//already known return false; - if(spell->isSpecialSpell()) + if(spell->isSpecial()) { - logGlobal->warn("Hero %s try to learn special spell %s", nodeName(), spell->name); + logGlobal->warn("Hero %s try to learn special spell %s", nodeName(), spell->getName()); return false;//special spells can not be learned } if(spell->isCreatureAbility()) { - logGlobal->warn("Hero %s try to learn creature spell %s", nodeName(), spell->name); + logGlobal->warn("Hero %s try to learn creature spell %s", nodeName(), spell->getName()); return false;//creature abilities can not be learned } - if(!IObjectInterface::cb->isAllowed(0, spell->id)) + if(!IObjectInterface::cb->isAllowed(0, spell->getIndex())) { - logGlobal->warn("Hero %s try to learn banned spell %s", nodeName(), spell->name); + logGlobal->warn("Hero %s try to learn banned spell %s", nodeName(), spell->getName()); return false;//banned spells should not be learned } @@ -829,7 +832,7 @@ CStackBasicDescriptor CGHeroInstance::calculateNecromancy (const BattleResult &b }; int maxCasualtyLevel = 1; for(auto & casualty : casualties) - vstd::amax(maxCasualtyLevel, VLC->creh->creatures[casualty.first]->level); + vstd::amax(maxCasualtyLevel, VLC->creh->objects[casualty.first]->level); // pick best bonus available std::shared_ptr topPick; for(std::shared_ptr newPick : *improvedNecromancy) @@ -845,7 +848,7 @@ CStackBasicDescriptor CGHeroInstance::calculateNecromancy (const BattleResult &b { auto quality = [getCreatureID](std::shared_ptr pick) -> std::vector { - const CCreature * c = VLC->creh->creatures[getCreatureID(pick)]; + const CCreature * c = VLC->creh->objects[getCreatureID(pick)]; std::vector v = {c->level, static_cast(c->cost.marketValue()), -pick->additionalInfo[1]}; return v; }; @@ -862,7 +865,7 @@ CStackBasicDescriptor CGHeroInstance::calculateNecromancy (const BattleResult &b // raise upgraded creature (at 2/3 rate) if no space available otherwise if(getSlotFor(creatureTypeRaised) == SlotID()) { - for(CreatureID upgraded : VLC->creh->creatures[creatureTypeRaised]->upgrades) + for(CreatureID upgraded : VLC->creh->objects[creatureTypeRaised]->upgrades) { if(getSlotFor(upgraded) != SlotID()) { @@ -873,11 +876,11 @@ CStackBasicDescriptor CGHeroInstance::calculateNecromancy (const BattleResult &b } } // calculate number of creatures raised - low level units contribute at 50% rate - const double raisedUnitHealth = VLC->creh->creatures[creatureTypeRaised]->MaxHealth(); + const double raisedUnitHealth = VLC->creh->objects[creatureTypeRaised]->MaxHealth(); double raisedUnits = 0; for(auto & casualty : casualties) { - const CCreature * c = VLC->creh->creatures[casualty.first]; + const CCreature * c = VLC->creh->objects[casualty.first]; double raisedFromCasualty = std::min(c->MaxHealth() / raisedUnitHealth, 1.0) * casualty.second * necromancySkill; if(c->level < requiredCasualtyLevel) raisedFromCasualty *= 0.5; @@ -953,7 +956,7 @@ si32 CGHeroInstance::getManaNewTurn() const // */ // void CGHeroInstance::giveArtifact (ui32 aid) //use only for fixed artifacts // { -// CArtifact * const artifact = VLC->arth->artifacts[aid]; //pointer to constant object +// CArtifact * const artifact = VLC->arth->objects[aid]; //pointer to constant object // CArtifactInstance *ai = CArtifactInstance::createNewArtifactInstance(artifact); // ai->putAt(this, ai->firstAvailableSlot(this)); // } @@ -983,7 +986,7 @@ void CGHeroInstance::getOutOffsets(std::vector &offsets) const }; } -int CGHeroInstance::getSpellCost(const CSpell * sp) const +int32_t CGHeroInstance::getSpellCost(const spells::Spell * sp) const { return sp->getCost(getSpellSchoolLevel(sp)); } @@ -1370,7 +1373,7 @@ std::string CGHeroInstance::getHeroTypeName() const } else { - return VLC->heroh->heroes[subID]->identifier; + return VLC->heroh->objects[subID]->identifier; } } return ""; @@ -1395,6 +1398,11 @@ void CGHeroInstance::setHeroTypeName(const std::string & identifier) } } +void CGHeroInstance::updateFrom(const JsonNode & data) +{ + CGObjectInstance::updateFrom(data); +} + void CGHeroInstance::serializeCommonOptions(JsonSerializeFormat & handler) { handler.serializeString("biography", biography); @@ -1411,7 +1419,7 @@ void CGHeroInstance::serializeCommonOptions(JsonSerializeFormat & handler) if(portrait >= 0) { if(portrait < legacyHeroes || portrait >= moddedStart) - handler.serializeId("portrait", portrait, -1, &VLC->heroh->decodeHero, &VLC->heroh->encodeHero); + handler.serializeId("portrait", portrait, -1); else handler.serializeInt("portrait", portrait, -1); } @@ -1421,7 +1429,7 @@ void CGHeroInstance::serializeCommonOptions(JsonSerializeFormat & handler) const JsonNode & portraitNode = handler.getCurrent()["portrait"]; if(portraitNode.getType() == JsonNode::JsonType::DATA_STRING) - handler.serializeId("portrait", portrait, -1, &VLC->heroh->decodeHero, &VLC->heroh->encodeHero); + handler.serializeId("portrait", portrait, -1); else handler.serializeInt("portrait", portrait, -1); } diff --git a/lib/mapObjects/CGHeroInstance.h b/lib/mapObjects/CGHeroInstance.h index 90f490af0..cdf6f04bf 100644 --- a/lib/mapObjects/CGHeroInstance.h +++ b/lib/mapObjects/CGHeroInstance.h @@ -9,9 +9,10 @@ */ #pragma once +#include + #include "CObjectHandler.h" #include "CArmedInstance.h" -#include "../spells/Magic.h" #include "../CArtHandler.h" // For CArtifactSet #include "../CRandomGenerator.h" @@ -160,7 +161,7 @@ public: EAlignment::EAlignment getAlignment() const; const std::string &getBiography() const; bool needsLastStack()const override; - TFaction getFaction() const; + ui32 getTileCost(const TerrainTile &dest, const TerrainTile &from, const TurnInfo * ti) const; //move cost - applying pathfinding skill, road and terrain modifiers. NOT includes diagonal move penalty, last move levelling ETerrainType::EETerrainType getNativeTerrain() const; ui32 getLowestCreatureSpeed() const; @@ -168,9 +169,11 @@ public: si32 manaRegain() const; //how many points of mana can hero regain "naturally" in one day si32 getManaNewTurn() const; //calculate how much mana this hero is going to have the next day int getCurrentLuck(int stack=-1, bool town=false) const; - int getSpellCost(const CSpell *sp) const; //do not use during battles -> bonuses from army would be ignored + int32_t getSpellCost(const spells::Spell * sp) const; //do not use during battles -> bonuses from army would be ignored + + bool canLearnSpell(const spells::Spell * spell) const; + bool canCastThisSpell(const spells::Spell * spell) const; //determines if this hero can cast given spell; takes into account existing spell in spellbook, existing spellbook and artifact bonuses - bool canLearnSpell(const CSpell * spell) const; // ----- primary and secondary skill, experience, level handling ----- @@ -208,7 +211,6 @@ public: ui64 getTotalStrength() const; // includes fighting strength and army strength TExpType calculateXp(TExpType exp) const; //apply learning skill - bool canCastThisSpell(const CSpell * spell) const; //determines if this hero can cast given spell; takes into account existing spell in spellbook, existing spellbook and artifact bonuses CStackBasicDescriptor calculateNecromancy (const BattleResult &battleResult) const; void showNecromancyDialog(const CStackBasicDescriptor &raisedStack, CRandomGenerator & rand) const; EDiggingStatus diggingStatus() const; @@ -238,6 +240,8 @@ public: CGHeroInstance(); virtual ~CGHeroInstance(); + PlayerColor getOwner() const override; + ///ArtBearer ArtBearer::ArtBearer bearerType() const override; @@ -247,23 +251,20 @@ public: ///spells::Caster int32_t getCasterUnitId() const override; - ui8 getSpellSchoolLevel(const spells::Spell * spell, int * outSelectedSchool = nullptr) const override; + int32_t getSpellSchoolLevel(const spells::Spell * spell, int32_t * outSelectedSchool = nullptr) const override; int64_t getSpellBonus(const spells::Spell * spell, int64_t base, const battle::Unit * affectedStack) const override; int64_t getSpecificSpellBonus(const spells::Spell * spell, int64_t base) const override; - int getEffectLevel(const spells::Spell * spell) const override; - - int getEffectPower(const spells::Spell * spell) const override; - - int getEnchantPower(const spells::Spell * spell) const override; - + int32_t getEffectLevel(const spells::Spell * spell) const override; + int32_t getEffectPower(const spells::Spell * spell) const override; + int32_t getEnchantPower(const spells::Spell * spell) const override; int64_t getEffectValue(const spells::Spell * spell) const override; - const PlayerColor getOwner() const override; + PlayerColor getCasterOwner() const override; void getCasterName(MetaString & text) const override; void getCastDescription(const spells::Spell * spell, const std::vector & attacked, MetaString & text) const override; - void spendMana(const spells::PacketSender * server, const int spellCost) const override; + void spendMana(ServerCallback * server, const int spellCost) const override; void deserializationFix(); @@ -272,6 +273,8 @@ public: std::string getObjectName() const override; void afterAddToMap(CMap * map) override; + + void updateFrom(const JsonNode & data) override; protected: void setPropertyDer(ui8 what, ui32 val) override;//synchr ///common part of hero instance and hero definition diff --git a/lib/mapObjects/CGMarket.cpp b/lib/mapObjects/CGMarket.cpp index e6b75dbdb..81f7150c9 100644 --- a/lib/mapObjects/CGMarket.cpp +++ b/lib/mapObjects/CGMarket.cpp @@ -58,7 +58,7 @@ bool IMarket::getOffer(int id1, int id2, int &val1, int &val2, EMarketMode::EMar const double effectivenessArray[] = {0.0, 0.3, 0.45, 0.50, 0.65, 0.7, 0.85, 0.9, 1.0}; double effectiveness = effectivenessArray[std::min(getMarketEfficiency(), 8)]; - double r = VLC->creh->creatures[id1]->cost[6], //value of given creature in gold + double r = VLC->creh->objects[id1]->cost[6], //value of given creature in gold g = VLC->objh->resVals[id2] / effectiveness; //value of wanted resource if(r>g) //if given resource is more expensive than wanted @@ -81,7 +81,7 @@ bool IMarket::getOffer(int id1, int id2, int &val1, int &val2, EMarketMode::EMar { double effectiveness = std::min((getMarketEfficiency() + 3.0) / 20.0, 0.6); double r = VLC->objh->resVals[id1], //value of offered resource - g = VLC->arth->artifacts[id2]->price / effectiveness; //value of bought artifact in gold + g = VLC->artifacts()->getByIndex(id2)->getPrice() / effectiveness; //value of bought artifact in gold if(id1 != 6) //non-gold prices are doubled r /= 2; @@ -93,7 +93,7 @@ bool IMarket::getOffer(int id1, int id2, int &val1, int &val2, EMarketMode::EMar case EMarketMode::ARTIFACT_RESOURCE: { double effectiveness = std::min((getMarketEfficiency() + 3.0) / 20.0, 0.6); - double r = VLC->arth->artifacts[id1]->price * effectiveness, + double r = VLC->artifacts()->getByIndex(id1)->getPrice() * effectiveness, g = VLC->objh->resVals[id2]; // if(id2 != 6) //non-gold prices are doubled @@ -106,14 +106,14 @@ bool IMarket::getOffer(int id1, int id2, int &val1, int &val2, EMarketMode::EMar case EMarketMode::CREATURE_EXP: { val1 = 1; - val2 = (VLC->creh->creatures[id1]->AIValue / 40) * 5; + val2 = (VLC->creh->objects[id1]->AIValue / 40) * 5; } break; case EMarketMode::ARTIFACT_EXP: { val1 = 1; - int givenClass = VLC->arth->artifacts[id1]->getArtClassSerial(); + int givenClass = VLC->arth->objects[id1]->getArtClassSerial(); if(givenClass < 0 || givenClass > 3) { val2 = 0; diff --git a/lib/mapObjects/CGPandoraBox.cpp b/lib/mapObjects/CGPandoraBox.cpp index 9f11932d2..c07de1358 100644 --- a/lib/mapObjects/CGPandoraBox.cpp +++ b/lib/mapObjects/CGPandoraBox.cpp @@ -11,10 +11,12 @@ #include "StdInc.h" #include "CGPandoraBox.h" +#include +#include + #include "../NetPacks.h" #include "../CSoundBase.h" -#include "../spells/CSpellHandler.h" #include "../CSkillHandler.h" #include "../StartInfo.h" #include "../IGameCallback.h" @@ -158,8 +160,8 @@ void CGPandoraBox::giveContentsAfterExp(const CGHeroInstance *h) const for (; i != spells.cend(); i++) { - const CSpell * sp = (*i).toSpell(); - if(h->canLearnSpell(sp)) + auto spell = (*i).toSpell(VLC->spells()); + if(h->canLearnSpell(spell)) { iw.components.push_back(Component(Component::SPELL, *i, 0, 0)); spellsToGive.insert(*i); @@ -265,7 +267,7 @@ void CGPandoraBox::giveContentsAfterExp(const CGHeroInstance *h) const cb->giveResources(h->getOwner(), resources); for(auto & elem : artifacts) - cb->giveHeroNewArtifact(h, VLC->arth->artifacts[elem],ArtifactPosition::FIRST_AVAILABLE); + cb->giveHeroNewArtifact(h, VLC->arth->objects[elem],ArtifactPosition::FIRST_AVAILABLE); iw.components.clear(); iw.text.clear(); diff --git a/lib/mapObjects/CGTownInstance.cpp b/lib/mapObjects/CGTownInstance.cpp index a1210c5ed..711b36a3b 100644 --- a/lib/mapObjects/CGTownInstance.cpp +++ b/lib/mapObjects/CGTownInstance.cpp @@ -51,9 +51,9 @@ void CCreGenAsCastleInfo::serializeJson(JsonSerializeFormat & handler) if(!asCastle) { std::vector standard; - standard.resize(VLC->townh->factions.size(), true); + standard.resize(VLC->townh->size(), true); - JsonSerializeFormat::LIC allowedLIC(standard, &CTownHandler::decodeFaction, &CTownHandler::encodeFaction); + JsonSerializeFormat::LIC allowedLIC(standard, &FactionID::decode, &FactionID::encode); allowedLIC.any = allowedFactions; handler.serializeLIC("allowedFactions", allowedLIC); @@ -260,7 +260,7 @@ void CGDwelling::newTurn(CRandomGenerator & rand) const { if(creatures[i].second.size()) { - CCreature *cre = VLC->creh->creatures[creatures[i].second[0]]; + CCreature *cre = VLC->creh->objects[creatures[i].second[0]]; TQuantity amount = cre->growth * (1 + cre->valOfBonuses(Bonus::CREATURE_GROWTH_PERCENT)/100) + cre->valOfBonuses(Bonus::CREATURE_GROWTH); if (VLC->modh->settings.DWELLINGS_ACCUMULATE_CREATURES && ID != Obj::REFUGEE_CAMP) //camp should not try to accumulate different kinds of creatures sac.creatures[i].first += amount; @@ -285,7 +285,7 @@ void CGDwelling::updateGuards() const //default condition - creatures are of level 5 or higher for (auto creatureEntry : creatures) { - if (VLC->creh->creatures[creatureEntry.second.at(0)]->level >= 5 && ID != Obj::REFUGEE_CAMP) + if (VLC->creh->objects[creatureEntry.second.at(0)]->level >= 5 && ID != Obj::REFUGEE_CAMP) { guarded = true; break; @@ -296,7 +296,7 @@ void CGDwelling::updateGuards() const { for (auto creatureEntry : creatures) { - const CCreature * crea = VLC->creh->creatures[creatureEntry.second.at(0)]; + const CCreature * crea = VLC->creh->objects[creatureEntry.second.at(0)]; SlotID slot = getSlotFor(crea->idNumber); if (hasStackAtSlot(slot)) //stack already exists, overwrite it @@ -324,7 +324,7 @@ void CGDwelling::updateGuards() const void CGDwelling::heroAcceptsCreatures( const CGHeroInstance *h) const { CreatureID crid = creatures[0].second[0]; - CCreature *crs = VLC->creh->creatures[crid]; + CCreature *crs = VLC->creh->objects[crid]; TQuantity count = creatures[0].first; if(crs->level == 1 && ID != Obj::REFUGEE_CAMP) //first level - creatures are for free @@ -447,8 +447,8 @@ int CGTownInstance::getSightRadius() const //returns sight distance auto height = town->buildings.at(bid)->height; if(ret < height) ret = height; - } } +} return ret; } @@ -532,7 +532,7 @@ GrowthInfo CGTownInstance::getGrowthInfo(int level) const if (creatures[level].second.empty()) return ret; //no dwelling - const CCreature *creature = VLC->creh->creatures[creatures[level].second.back()]; + const CCreature *creature = VLC->creh->objects[creatures[level].second.back()]; const int base = creature->growth; int castleBonus = 0; @@ -552,7 +552,7 @@ GrowthInfo CGTownInstance::getGrowthInfo(int level) const ret.entries.push_back(GrowthInfo::Entry(subID, BuildingID::HORDE_2, creature->hordeGrowth)); int dwellingBonus = 0; - if(const PlayerState *p = cb->getPlayer(tempOwner, false)) + if(const PlayerState *p = cb->getPlayerState(tempOwner, false)) { dwellingBonus = getDwellingBonus(creatures[level].second, p->dwellings); } @@ -852,16 +852,16 @@ void CGTownInstance::updateBonusingBuildings() //update to version 792 { switch (this->town->faction->index) { - case ETownType::CASTLE: + case ETownType::CASTLE: building->setBuildingSubtype(BuildingSubID::STABLES); - break; + break; - case ETownType::DUNGEON: + case ETownType::DUNGEON: if(building->getBuildingType() == BuildingID::SPECIAL_2) building->setBuildingSubtype(BuildingSubID::MANA_VORTEX); else if(building->getBuildingType() == BuildingID::SPECIAL_4) building->setBuildingSubtype(BuildingSubID::EXPERIENCE_VISITING_BONUS); - break; + break; case ETownType::TOWER: building->setBuildingSubtype(BuildingSubID::KNOWLEDGE_VISITING_BONUS); @@ -875,10 +875,10 @@ void CGTownInstance::updateBonusingBuildings() //update to version 792 building->setBuildingSubtype(BuildingSubID::SPELL_POWER_VISITING_BONUS); break; - case ETownType::FORTRESS: + case ETownType::FORTRESS: building->setBuildingSubtype(BuildingSubID::DEFENSE_VISITING_BONUS); - break; - } + break; + } } } //secondly, supplement bonusing buildings list and active bonuses; subtypes for these objects are already set in update792 @@ -985,7 +985,7 @@ void CGTownInstance::newTurn(CRandomGenerator & rand) const } else //upgrade { - cb->changeStackType(sl, VLC->creh->creatures[*c->upgrades.begin()]); + cb->changeStackType(sl, VLC->creh->objects[*c->upgrades.begin()]); } } if ((stacksCount() < GameConstants::ARMY_SIZE && rand.nextInt(99) < 25) || Slots().empty()) //add new stack @@ -998,7 +998,7 @@ void CGTownInstance::newTurn(CRandomGenerator & rand) const TQuantity count = creatureGrowth(i); if (!count) // no dwelling - count = VLC->creh->creatures[c]->growth; + count = VLC->creh->objects[c]->growth; {//no lower tiers or above current month @@ -1006,7 +1006,7 @@ void CGTownInstance::newTurn(CRandomGenerator & rand) const { StackLocation sl(this, n); if (slotEmpty(n)) - cb->insertNewStack(sl, VLC->creh->creatures[c], count); + cb->insertNewStack(sl, VLC->creh->objects[c], count); else //add to existing cb->changeStackCount(sl, count); } @@ -1095,7 +1095,7 @@ void CGTownInstance::removeCapitols (PlayerColor owner) const { if (hasCapitol()) // search if there's an older capitol { - PlayerState* state = cb->gameState()->getPlayer (owner); //get all towns owned by player + PlayerState* state = cb->gameState()->getPlayerState(owner); //get all towns owned by player for (auto i = state->towns.cbegin(); i < state->towns.cend(); ++i) { if (*i != this && (*i)->hasCapitol()) @@ -1136,7 +1136,7 @@ int CGTownInstance::getMarketEfficiency() const if(!hasBuiltSomeTradeBuilding()) return 0; - const PlayerState *p = cb->getPlayer(tempOwner); + const PlayerState *p = cb->getPlayerState(tempOwner); assert(p); int marketCount = 0; @@ -1197,7 +1197,7 @@ void CGTownInstance::setType(si32 ID, si32 subID) { assert(ID == Obj::TOWN); // just in case CGObjectInstance::setType(ID, subID); - town = VLC->townh->factions[subID]->town; + town = (*VLC->townh)[subID]->town; randomizeArmy(subID); updateAppearance(); } @@ -1264,14 +1264,14 @@ void CGTownInstance::recreateBuildingsBonuses() continue; for(auto bonus : building->buildingBonuses) - { +{ if(bonus->propagator != nullptr && bonus->propagator->getPropagatorType() == ALL_CREATURES) VLC->creh->addBonusForAllCreatures(bonus); else addNewBonus(bonus); - } } } +} void CGTownInstance::setVisitingHero(CGHeroInstance *h) { @@ -1285,7 +1285,7 @@ void CGTownInstance::setVisitingHero(CGHeroInstance *h) if(h) { - PlayerState *p = cb->gameState()->getPlayer(h->tempOwner); + PlayerState *p = cb->gameState()->getPlayerState(h->tempOwner); assert(p); h->detachFrom(p); h->attachTo(&townAndVis); @@ -1295,7 +1295,7 @@ void CGTownInstance::setVisitingHero(CGHeroInstance *h) } else { - PlayerState *p = cb->gameState()->getPlayer(visitingHero->tempOwner); + PlayerState *p = cb->gameState()->getPlayerState(visitingHero->tempOwner); visitingHero->visitedTown = nullptr; visitingHero->detachFrom(&townAndVis); visitingHero->attachTo(p); @@ -1308,7 +1308,7 @@ void CGTownInstance::setGarrisonedHero(CGHeroInstance *h) assert(!!garrisonHero == !h); if(h) { - PlayerState *p = cb->gameState()->getPlayer(h->tempOwner); + PlayerState *p = cb->gameState()->getPlayerState(h->tempOwner); assert(p); h->detachFrom(p); h->attachTo(this); @@ -1318,7 +1318,7 @@ void CGTownInstance::setGarrisonedHero(CGHeroInstance *h) } else { - PlayerState *p = cb->gameState()->getPlayer(garrisonHero->tempOwner); + PlayerState *p = cb->gameState()->getPlayerState(garrisonHero->tempOwner); garrisonHero->visitedTown = nullptr; garrisonHero->inTownGarrison = false; garrisonHero->detachFrom(this); @@ -1341,7 +1341,7 @@ const CTown * CGTownInstance::getTown() const { if(nullptr == town) { - return VLC->townh->factions[subID]->town; + return (*VLC->townh)[subID]->town; } else return town; @@ -1357,7 +1357,7 @@ int CGTownInstance::getTownLevel() const { if(town->buildings.at(bid)->upgrade == BuildingID::NONE) level++; - } +} return level; } @@ -1417,7 +1417,7 @@ bool CGTownInstance::hasBuilt(BuildingID buildingID, int townID) const } TResources CGTownInstance::getBuildingCost(BuildingID buildingID) const -{ +{ if (vstd::contains(town->buildings, buildingID)) return town->buildings.at(buildingID)->resources; else @@ -1636,6 +1636,31 @@ void CGTownInstance::serializeJsonOptions(JsonSerializeFormat & handler) } } +PlayerColor CGTownBuilding::getOwner() const +{ + return town->getOwner(); +} + +int32_t CGTownBuilding::getObjGroupIndex() const +{ + return -1; +} + +int32_t CGTownBuilding::getObjTypeIndex() const +{ + return 0; +} + +int3 CGTownBuilding::visitablePos() const +{ + return town->visitablePos(); +} + +int3 CGTownBuilding::getPosition() const +{ + return town->getPosition(); +} + COPWBonus::COPWBonus(BuildingID bid, BuildingSubID::EBuildingSubID subId, CGTownInstance * cgTown) { bID = bid; @@ -1657,7 +1682,7 @@ void COPWBonus::setProperty(ui8 what, ui32 val) } } -void COPWBonus::onHeroVisit(const CGHeroInstance * h) const +void COPWBonus::onHeroVisit (const CGHeroInstance * h) const { ObjectInstanceID heroID = h->id; if (town->hasBuilt(bID)) @@ -1690,7 +1715,7 @@ void COPWBonus::onHeroVisit(const CGHeroInstance * h) const if (visitors.empty()) { if (h->mana < h->manaLimit() * 2) - cb->setManaPoints(heroID, 2 * h->manaLimit()); + cb->setManaPoints (heroID, 2 * h->manaLimit()); //TODO: investigate line below //cb->setObjProperty (town->id, ObjProperty::VISITED, true); iw.text << getVisitingBonusGreeting(); @@ -1716,7 +1741,7 @@ void CTownBonus::setProperty (ui8 what, ui32 val) visitors.insert(ObjectInstanceID(val)); } -void CTownBonus::onHeroVisit(const CGHeroInstance * h) const +void CTownBonus::onHeroVisit (const CGHeroInstance * h) const { ObjectInstanceID heroID = h->id; if(town->hasBuilt(bID) && visitors.find(heroID) == visitors.end()) @@ -1728,34 +1753,34 @@ void CTownBonus::onHeroVisit(const CGHeroInstance * h) const switch (bType) { case BuildingSubID::KNOWLEDGE_VISITING_BONUS: //wall of knowledge - what = PrimarySkill::KNOWLEDGE; - val = 1; - iw.components.push_back(Component(Component::PRIM_SKILL, 3, 1, 0)); - break; + what = PrimarySkill::KNOWLEDGE; + val = 1; + iw.components.push_back (Component(Component::PRIM_SKILL, 3, 1, 0)); + break; case BuildingSubID::SPELL_POWER_VISITING_BONUS: //order of fire - what = PrimarySkill::SPELL_POWER; - val = 1; - iw.components.push_back(Component(Component::PRIM_SKILL, 2, 1, 0)); - break; + what = PrimarySkill::SPELL_POWER; + val = 1; + iw.components.push_back (Component(Component::PRIM_SKILL, 2, 1, 0)); + break; case BuildingSubID::ATTACK_VISITING_BONUS: //hall of Valhalla - what = PrimarySkill::ATTACK; - val = 1; - iw.components.push_back(Component(Component::PRIM_SKILL, 0, 1, 0)); - break; + what = PrimarySkill::ATTACK; + val = 1; + iw.components.push_back (Component(Component::PRIM_SKILL, 0, 1, 0)); + break; case BuildingSubID::EXPERIENCE_VISITING_BONUS: //academy of battle scholars - what = PrimarySkill::EXPERIENCE; + what = PrimarySkill::EXPERIENCE; val = static_cast(h->calculateXp(1000)); - iw.components.push_back(Component(Component::EXPERIENCE, 0, val, 0)); - break; + iw.components.push_back (Component(Component::EXPERIENCE, 0, val, 0)); + break; case BuildingSubID::DEFENSE_VISITING_BONUS: //cage of warlords - what = PrimarySkill::DEFENSE; - val = 1; - iw.components.push_back(Component(Component::PRIM_SKILL, 1, 1, 0)); - break; + what = PrimarySkill::DEFENSE; + val = 1; + iw.components.push_back (Component(Component::PRIM_SKILL, 1, 1, 0)); + break; case BuildingSubID::CUSTOM_VISITING_BONUS: const auto building = town->town->buildings.at(bID); @@ -1763,19 +1788,19 @@ void CTownBonus::onHeroVisit(const CGHeroInstance * h) const { const auto & bonuses = building->onVisitBonuses; applyBonuses(const_cast(h), bonuses); - } - break; + } + break; } if(what != PrimarySkill::NONE) { - iw.player = cb->getOwner(heroID); + iw.player = cb->getOwner(heroID); iw.text << getVisitingBonusGreeting(); - cb->showInfoDialog(&iw); - cb->changePrimSkill(cb->getHero(heroID), what, val); + cb->showInfoDialog(&iw); + cb->changePrimSkill (cb->getHero(heroID), what, val); town->addHeroToStructureVisitors(h, indexOnTV); - } } } +} void CTownBonus::applyBonuses(CGHeroInstance * h, const BonusList & bonuses) const { @@ -1810,7 +1835,7 @@ GrowthInfo::Entry::Entry(const std::string &format, int _count) GrowthInfo::Entry::Entry(int subID, BuildingID building, int _count) : count(_count) { - description = boost::str(boost::format("%s %+d") % VLC->townh->factions[subID]->town->buildings.at(building)->Name() % count); + description = boost::str(boost::format("%s %+d") % (*VLC->townh)[subID]->town->buildings.at(building)->Name() % count); } GrowthInfo::Entry::Entry(int _count, const std::string &fullDescription) diff --git a/lib/mapObjects/CGTownInstance.h b/lib/mapObjects/CGTownInstance.h index f41812cff..1cfbe30c8 100644 --- a/lib/mapObjects/CGTownInstance.h +++ b/lib/mapObjects/CGTownInstance.h @@ -119,6 +119,13 @@ public: bType = subId; } + PlayerColor getOwner() const override; + int32_t getObjGroupIndex() const override; + int32_t getObjTypeIndex() const override; + + int3 visitablePos() const override; + int3 getPosition() const override; + template void serialize(Handler &h, const int version) { h & bID; diff --git a/lib/mapObjects/CObjectClassesHandler.cpp b/lib/mapObjects/CObjectClassesHandler.cpp index 96dfd6081..2bb8f3336 100644 --- a/lib/mapObjects/CObjectClassesHandler.cpp +++ b/lib/mapObjects/CObjectClassesHandler.cpp @@ -209,9 +209,9 @@ void CObjectClassesHandler::loadObjectEntry(const std::string & identifier, cons if (overrideForce) // DO NOT override mod handlers by default { - obj->subObjects[id] = handler; - obj->subIds[convertedId] = id; - } + obj->subObjects[id] = handler; + obj->subIds[convertedId] = id; +} else { logGlobal->warn("Don't override handler %s in object %s(%d)::%s(%d) subTypeName : %s" @@ -219,7 +219,7 @@ void CObjectClassesHandler::loadObjectEntry(const std::string & identifier, cons } } -CObjectClassesHandler::ObjectContainter * CObjectClassesHandler::loadFromJson(const JsonNode & json, const std::string & name) +CObjectClassesHandler::ObjectContainter * CObjectClassesHandler::loadFromJson(const std::string & scope, const JsonNode & json, const std::string & name) { auto obj = new ObjectContainter(); obj->identifier = name; @@ -242,14 +242,14 @@ CObjectClassesHandler::ObjectContainter * CObjectClassesHandler::loadFromJson(co void CObjectClassesHandler::loadObject(std::string scope, std::string name, const JsonNode & data) { - auto object = loadFromJson(data, normalizeIdentifier(scope, "core", name)); + auto object = loadFromJson(scope, data, normalizeIdentifier(scope, "core", name)); objects[object->id] = object; VLC->modh->identifiers.registerObject(scope, "object", name, object->id); } void CObjectClassesHandler::loadObject(std::string scope, std::string name, const JsonNode & data, size_t index) { - auto object = loadFromJson(data, normalizeIdentifier(scope, "core", name)); + auto object = loadFromJson(scope, data, normalizeIdentifier(scope, "core", name)); assert(objects[(si32)index] == nullptr); // ensure that this id was not loaded before objects[(si32)index] = object; VLC->modh->identifiers.registerObject(scope, "object", name, object->id); diff --git a/lib/mapObjects/CObjectClassesHandler.h b/lib/mapObjects/CObjectClassesHandler.h index 9ea070f47..9d43ea28a 100644 --- a/lib/mapObjects/CObjectClassesHandler.h +++ b/lib/mapObjects/CObjectClassesHandler.h @@ -284,7 +284,7 @@ class DLL_LINKAGE CObjectClassesHandler : public IHandlerBase std::map> customNames; void loadObjectEntry(const std::string & identifier, const JsonNode & entry, ObjectContainter * obj); - ObjectContainter * loadFromJson(const JsonNode & json, const std::string & name); + ObjectContainter * loadFromJson(const std::string & scope, const JsonNode & json, const std::string & name); public: CObjectClassesHandler(); ~CObjectClassesHandler(); diff --git a/lib/mapObjects/CObjectHandler.cpp b/lib/mapObjects/CObjectHandler.cpp index 05060f890..8b3dee46a 100644 --- a/lib/mapObjects/CObjectHandler.cpp +++ b/lib/mapObjects/CObjectHandler.cpp @@ -139,6 +139,21 @@ CGObjectInstance::~CGObjectInstance() { } +int32_t CGObjectInstance::getObjGroupIndex() const +{ + return ID.num; +} + +int32_t CGObjectInstance::getObjTypeIndex() const +{ + return subID; +} + +int3 CGObjectInstance::getPosition() const +{ + return pos; +} + void CGObjectInstance::setOwner(PlayerColor ow) { tempOwner = ow; @@ -346,6 +361,11 @@ bool CGObjectInstance::passableFor(PlayerColor color) const return false; } +void CGObjectInstance::updateFrom(const JsonNode & data) +{ + +} + void CGObjectInstance::serializeJson(JsonSerializeFormat & handler) { //only save here, loading is handled by map loader diff --git a/lib/mapObjects/CObjectHandler.h b/lib/mapObjects/CObjectHandler.h index 9d5378968..3e3777d91 100644 --- a/lib/mapObjects/CObjectHandler.h +++ b/lib/mapObjects/CObjectHandler.h @@ -23,6 +23,7 @@ struct BattleResult; class JsonSerializeFormat; class CRandomGenerator; class CMap; +class JsonNode; // This one teleport-specific, but has to be available everywhere in callbacks and netpacks // For now it's will be there till teleports code refactored and moved into own file @@ -36,6 +37,13 @@ public: IObjectInterface(); virtual ~IObjectInterface(); + virtual int32_t getObjGroupIndex() const = 0; + virtual int32_t getObjTypeIndex() const = 0; + + virtual PlayerColor getOwner() const = 0; + virtual int3 visitablePos() const = 0; + virtual int3 getPosition() const = 0; + virtual void onHeroVisit(const CGHeroInstance * h) const; virtual void onHeroLeave(const CGHeroInstance * h) const; virtual void newTurn(CRandomGenerator & rand) const; @@ -126,10 +134,13 @@ public: CGObjectInstance(); ~CGObjectInstance(); + int32_t getObjGroupIndex() const override; + int32_t getObjTypeIndex() const override; + /// "center" tile from which the sight distance is calculated int3 getSightCenter() const; - PlayerColor getOwner() const; + PlayerColor getOwner() const override; void setOwner(PlayerColor ow); /** APPEARANCE ACCESSORS **/ @@ -137,7 +148,8 @@ public: int getWidth() const; //returns width of object graphic in tiles int getHeight() const; //returns height of object graphic in tiles bool visitableAt(int x, int y) const; //returns true if object is visitable at location (x, y) (h3m pos) - int3 visitablePos() const; + int3 visitablePos() const override; + int3 getPosition() const override; bool blockingAt(int x, int y) const; //returns true if object is blocking location (x, y) (h3m pos) bool coveringAt(int x, int y) const; //returns true if object covers with picture location (x, y) (h3m pos) std::set getBlockedPos() const; //returns set of positions blocked by this object @@ -202,6 +214,7 @@ public: ///Entry point of Json (de-)serialization void serializeJson(JsonSerializeFormat & handler); + virtual void updateFrom(const JsonNode & data); protected: /// virtual method that allows synchronously update object state on server and all clients virtual void setPropertyDer(ui8 what, ui32 val); diff --git a/lib/mapObjects/CQuest.cpp b/lib/mapObjects/CQuest.cpp index 765e06e50..5bd5deca5 100644 --- a/lib/mapObjects/CQuest.cpp +++ b/lib/mapObjects/CQuest.cpp @@ -179,9 +179,9 @@ void CQuest::getVisitText(MetaString &iwText, std::vector &components break; case MISSION_HERO: //FIXME: portrait may not match hero, if custom portrait was set in map editor - components.push_back(Component(Component::HERO_PORTRAIT, VLC->heroh->heroes[m13489val]->imageIndex, 0, 0)); + components.push_back(Component(Component::HERO_PORTRAIT, VLC->heroh->objects[m13489val]->imageIndex, 0, 0)); if(!isCustom) - iwText.addReplacement(VLC->heroh->heroes[m13489val]->name); + iwText.addReplacement(VLC->heroh->objects[m13489val]->name); break; case MISSION_KILL_CREATURE: { @@ -317,7 +317,7 @@ void CQuest::getRolloverText(MetaString &ms, bool onHover) const } break; case MISSION_HERO: - ms.addReplacement(VLC->heroh->heroes[m13489val]->name); + ms.addReplacement(VLC->heroh->objects[m13489val]->name); break; case MISSION_PLAYER: ms.addReplacement(VLC->generaltexth->colors[m13489val]); @@ -400,7 +400,7 @@ void CQuest::getCompletionText(MetaString &iwText, std::vector &compo break; case MISSION_HERO: if (!isCustomComplete) - iwText.addReplacement(VLC->heroh->heroes[m13489val]->name); + iwText.addReplacement(VLC->heroh->objects[m13489val]->name); break; case MISSION_PLAYER: if (!isCustomComplete) @@ -477,7 +477,7 @@ void CQuest::serializeJson(JsonSerializeFormat & handler, const std::string & fi } break; case MISSION_HERO: - handler.serializeId("hero", m13489val, 0, &CHeroHandler::decodeHero, &CHeroHandler::encodeHero); + handler.serializeId("hero", m13489val, 0); break; case MISSION_PLAYER: handler.serializeEnum("player", m13489val, PlayerColor::CANNOT_DETERMINE.getNum(), GameConstants::PLAYER_COLOR_NAMES); @@ -817,7 +817,7 @@ void CGSeerHut::completeQuest (const CGHeroInstance * h) const //reward cb->changeSecSkill(h, SecondarySkill(rID), rVal, false); break; case ARTIFACT: - cb->giveHeroNewArtifact(h, VLC->arth->artifacts[rID],ArtifactPosition::FIRST_AVAILABLE); + cb->giveHeroNewArtifact(h, VLC->arth->objects[rID],ArtifactPosition::FIRST_AVAILABLE); break; case SPELL: { @@ -931,15 +931,15 @@ void CGSeerHut::serializeJsonOptions(JsonSerializeFormat & handler) identifier = CSkillHandler::encodeSkill(rID); break; case ARTIFACT: - identifier = ArtifactID(rID).toArtifact()->identifier; + identifier = ArtifactID(rID).toArtifact(VLC->artifacts())->getJsonKey(); amount = 1; break; case SPELL: - identifier = SpellID(rID).toSpell()->identifier; + identifier = SpellID(rID).toSpell(VLC->spells())->getJsonKey(); amount = 1; break; case CREATURE: - identifier = CreatureID(rID).toCreature()->identifier; + identifier = CreatureID(rID).toCreature(VLC->creatures())->getJsonKey(); break; default: assert(false); diff --git a/lib/mapObjects/CRewardableObject.cpp b/lib/mapObjects/CRewardableObject.cpp index 4f4116494..638d61bc7 100644 --- a/lib/mapObjects/CRewardableObject.cpp +++ b/lib/mapObjects/CRewardableObject.cpp @@ -41,7 +41,7 @@ bool CRewardLimiter::heroAllowed(const CGHeroInstance * hero) const return false; } - if(!IObjectInterface::cb->getPlayer(hero->tempOwner)->resources.canAfford(resources)) + if(!IObjectInterface::cb->getPlayerState(hero->tempOwner)->resources.canAfford(resources)) return false; if(minLevel > (si32)hero->level) @@ -295,7 +295,7 @@ void CRewardableObject::grantRewardAfterLevelup(const CVisitInfo & info, const C } for(ArtifactID art : info.reward.artifacts) - cb->giveHeroNewArtifact(hero, VLC->arth->artifacts[art],ArtifactPosition::FIRST_AVAILABLE); + cb->giveHeroNewArtifact(hero, VLC->arth->objects[art],ArtifactPosition::FIRST_AVAILABLE); if(!info.reward.spells.empty()) { @@ -326,11 +326,11 @@ bool CRewardableObject::wasVisited(PlayerColor player) const case VISIT_BONUS: return false; case VISIT_ONCE: - return vstd::contains(cb->getPlayer(player)->visitedObjects, ObjectInstanceID(id)); + return vstd::contains(cb->getPlayerState(player)->visitedObjects, ObjectInstanceID(id)); case VISIT_HERO: return false; case VISIT_PLAYER: - return vstd::contains(cb->getPlayer(player)->visitedObjects, ObjectInstanceID(id)); + return vstd::contains(cb->getPlayerState(player)->visitedObjects, ObjectInstanceID(id)); default: return false; } @@ -774,7 +774,7 @@ void CGBonusingObject::onHeroVisit(const CGHeroInstance *h) const if(slot.second->type->idNumber == CreatureID::CAVALIER) { cb->changeStackType(StackLocation(h, slot.first), - VLC->creh->creatures[CreatureID::CHAMPION]); + VLC->creh->objects[CreatureID::CHAMPION]); } } } @@ -854,7 +854,7 @@ void CGOnceVisitable::initObj(CRandomGenerator & rand) info[1].reward.bonuses.push_back(bonus); info[0].limiter.numOfGrants = 1; info[0].message.addTxt(MetaString::ADVOB_TXT, 162); - info[0].message.addReplacement(VLC->arth->artifacts[info[0].reward.artifacts.back()]->Name()); + info[0].message.addReplacement(VLC->arth->objects[info[0].reward.artifacts.back()]->getName()); info[1].message.addTxt(MetaString::ADVOB_TXT, 163); } break; @@ -870,7 +870,7 @@ void CGOnceVisitable::initObj(CRandomGenerator & rand) loadRandomArtifact(rand, info[0], 10, 10, 0, 0); info[0].limiter.numOfGrants = 1; info[0].message.addTxt(MetaString::ADVOB_TXT, 155); - info[0].message.addReplacement(VLC->arth->artifacts[info[0].reward.artifacts.back()]->Name()); + info[0].message.addReplacement(VLC->arth->objects[info[0].reward.artifacts.back()]->getName()); } else if(hlp < 90) //2 - 5 of non-gold resource { diff --git a/lib/mapObjects/CommonConstructors.cpp b/lib/mapObjects/CommonConstructors.cpp index c4350247a..062f553e1 100644 --- a/lib/mapObjects/CommonConstructors.cpp +++ b/lib/mapObjects/CommonConstructors.cpp @@ -38,7 +38,7 @@ void CTownInstanceConstructor::initTypeData(const JsonNode & input) { VLC->modh->identifiers.requestIdentifier("faction", input["faction"], [&](si32 index) { - faction = VLC->townh->factions[index]; + faction = (*VLC->townh)[index]; }); filtersJson = input["filters"]; @@ -92,7 +92,7 @@ CHeroInstanceConstructor::CHeroInstanceConstructor() void CHeroInstanceConstructor::initTypeData(const JsonNode & input) { VLC->modh->identifiers.requestIdentifier("heroClass", input["heroClass"], - [&](si32 index) { heroClass = VLC->heroh->classes.heroClasses[index]; }); + [&](si32 index) { heroClass = VLC->heroh->classes[index]; }); filtersJson = input["filters"]; } @@ -153,7 +153,7 @@ void CDwellingInstanceConstructor::initTypeData(const JsonNode & input) { VLC->modh->identifiers.requestIdentifier("creature", creatures[j], [=] (si32 index) { - availableCreatures[i][j] = VLC->creh->creatures[index]; + availableCreatures[i][j] = VLC->creh->objects[index]; }); } assert(!availableCreatures[i].empty()); diff --git a/lib/mapObjects/JsonRandom.cpp b/lib/mapObjects/JsonRandom.cpp index 1d5c370ea..aecba04a9 100644 --- a/lib/mapObjects/JsonRandom.cpp +++ b/lib/mapObjects/JsonRandom.cpp @@ -94,7 +94,7 @@ namespace JsonRandom return VLC->arth->pickRandomArtifact(rng, [=](ArtifactID artID) -> bool { - CArtifact * art = VLC->arth->artifacts[artID]; + CArtifact * art = VLC->arth->objects[artID]; if (!vstd::iswithin(art->price, minValue, maxValue)) return false; @@ -158,13 +158,13 @@ namespace JsonRandom CStackBasicDescriptor loadCreature(const JsonNode & value, CRandomGenerator & rng) { CStackBasicDescriptor stack; - stack.type = VLC->creh->creatures[VLC->modh->identifiers.getIdentifier("creature", value["type"]).get()]; + stack.type = VLC->creh->objects[VLC->modh->identifiers.getIdentifier("creature", value["type"]).get()]; stack.count = loadValue(value, rng); if (!value["upgradeChance"].isNull() && !stack.type->upgrades.empty()) { if (int(value["upgradeChance"].Float()) > rng.nextInt(99)) // select random upgrade { - stack.type = VLC->creh->creatures[*RandomGeneratorUtil::nextItem(stack.type->upgrades, rng)]; + stack.type = VLC->creh->objects[*RandomGeneratorUtil::nextItem(stack.type->upgrades, rng)]; } } return stack; @@ -194,12 +194,12 @@ namespace JsonRandom info.minAmount = static_cast(node["min"].Float()); info.maxAmount = static_cast(node["max"].Float()); } - const CCreature * crea = VLC->creh->creatures[VLC->modh->identifiers.getIdentifier("creature", node["type"]).get()]; + const CCreature * crea = VLC->creh->objects[VLC->modh->identifiers.getIdentifier("creature", node["type"]).get()]; info.allowedCreatures.push_back(crea); if (node["upgradeChance"].Float() > 0) { for (auto creaID : crea->upgrades) - info.allowedCreatures.push_back(VLC->creh->creatures[creaID]); + info.allowedCreatures.push_back(VLC->creh->objects[creaID]); } ret.push_back(info); } diff --git a/lib/mapObjects/MiscObjects.cpp b/lib/mapObjects/MiscObjects.cpp index 70e316268..ab2b90ea0 100644 --- a/lib/mapObjects/MiscObjects.cpp +++ b/lib/mapObjects/MiscObjects.cpp @@ -70,14 +70,14 @@ void CTeamVisited::setPropertyDer(ui8 what, ui32 val) bool CTeamVisited::wasVisited(PlayerColor player) const { - return wasVisited(cb->getPlayer(player)->team); + return wasVisited(cb->getPlayerState(player)->team); } bool CTeamVisited::wasVisited(TeamID team) const { for(auto i : players) { - if(cb->getPlayer(i)->team == team) + if(cb->getPlayerState(i)->team == team) return true; } return false; @@ -193,7 +193,7 @@ void CGCreature::onHeroVisit( const CGHeroInstance * h ) const std::string tmp = VLC->generaltexth->advobtxt[90]; boost::algorithm::replace_first(tmp,"%d",boost::lexical_cast(getStackCount(SlotID(0)))); boost::algorithm::replace_first(tmp,"%d",boost::lexical_cast(action)); - boost::algorithm::replace_first(tmp,"%s",VLC->creh->creatures[subID]->namePl); + boost::algorithm::replace_first(tmp,"%s",VLC->creh->objects[subID]->namePl); ynd.text << tmp; cb->showBlockingDialog(&ynd); break; @@ -226,7 +226,7 @@ void CGCreature::initObj(CRandomGenerator & rand) stacks[SlotID(0)]->setType(CreatureID(subID)); TQuantity &amount = stacks[SlotID(0)]->count; - CCreature &c = *VLC->creh->creatures[subID]; + CCreature &c = *VLC->creh->objects[subID]; if(amount == 0) { amount = rand.nextInt(c.ammMin, c.ammMax); @@ -299,11 +299,11 @@ int CGCreature::takenAction(const CGHeroInstance *h, bool allowJoin) const powerFactor = -3; std::set myKindCres; //what creatures are the same kind as we - const CCreature * myCreature = VLC->creh->creatures[subID]; + const CCreature * myCreature = VLC->creh->objects[subID]; myKindCres.insert(myCreature->idNumber); //we myKindCres.insert(myCreature->upgrades.begin(), myCreature->upgrades.end()); //our upgrades - for(ConstTransitivePtr &crea : VLC->creh->creatures) + for(ConstTransitivePtr &crea : VLC->creh->objects) { if(vstd::contains(crea->upgrades, myCreature->idNumber)) //it's our base creatures myKindCres.insert(crea->idNumber); @@ -337,7 +337,7 @@ int CGCreature::takenAction(const CGHeroInstance *h, bool allowJoin) const return JOIN_FOR_FREE; else if(diplomacy * 2 + sympathy + 1 >= character) - return VLC->creh->creatures[subID]->cost[6] * getStackCount(SlotID(0)); //join for gold + return VLC->creh->objects[subID]->cost[6] * getStackCount(SlotID(0)); //join for gold } //we are still here - creatures have not joined hero, flee or fight @@ -437,7 +437,7 @@ void CGCreature::fight( const CGHeroInstance *h ) const if(!upgrades.empty()) { auto it = RandomGeneratorUtil::nextItem(upgrades, CRandomGenerator::getDefault()); - cb->changeStackType(StackLocation(this, slotID), VLC->creh->creatures[*it]); + cb->changeStackType(StackLocation(this, slotID), VLC->creh->objects[*it]); } } } @@ -471,7 +471,7 @@ void CGCreature::battleFinished(const CGHeroInstance *hero, const BattleResult & { //merge stacks into one TSlots::const_iterator i; - CCreature * cre = VLC->creh->creatures[formation.basicType]; + CCreature * cre = VLC->creh->objects[formation.basicType]; for(i = stacks.begin(); i != stacks.end(); i++) { if(cre->isMyUpgrade(i->second->type)) @@ -582,7 +582,7 @@ void CGCreature::giveReward(const CGHeroInstance * h) const if(gainedArtifact != ArtifactID::NONE) { - cb->giveHeroNewArtifact(h, VLC->arth->artifacts[gainedArtifact], ArtifactPosition::FIRST_AVAILABLE); + cb->giveHeroNewArtifact(h, VLC->arth->objects[gainedArtifact], ArtifactPosition::FIRST_AVAILABLE); iw.components.push_back(Component(Component::ARTIFACT, gainedArtifact, 0, 0)); } @@ -1295,7 +1295,7 @@ void CGArtifact::initObj(CRandomGenerator & rand) storedArtifact = a; } if(!storedArtifact->artType) - storedArtifact->setType(VLC->arth->artifacts[subID]); + storedArtifact->setType(VLC->arth->objects[subID]); } if(ID == Obj::SPELL_SCROLL) subID = 1; @@ -1308,7 +1308,7 @@ void CGArtifact::initObj(CRandomGenerator & rand) std::string CGArtifact::getObjectName() const { - return VLC->arth->artifacts[subID]->Name(); + return VLC->artifacts()->getByIndex(subID)->getName(); } void CGArtifact::onHeroVisit(const CGHeroInstance * h) const @@ -1326,8 +1326,12 @@ void CGArtifact::onHeroVisit(const CGHeroInstance * h) const iw.text << message; else { - if(VLC->arth->artifacts[subID]->EventText().size()) - iw.text << std::pair(MetaString::ART_EVNTS, subID); + auto artifact = ArtifactID(subID).toArtifact(VLC->artifacts()); + + if((artifact != nullptr) && (!artifact->getEventText().empty())) + { + iw.text.addTxt(MetaString::ART_EVNTS, subID); + } else //fix for mod artifacts with no event text { iw.text.addTxt(MetaString::ADVOB_TXT, 183); //% has found treasure diff --git a/lib/mapping/CMap.cpp b/lib/mapping/CMap.cpp index 5eff238e9..8ce654544 100644 --- a/lib/mapping/CMap.cpp +++ b/lib/mapping/CMap.cpp @@ -519,11 +519,11 @@ void CMap::checkForObjectives() switch (cond.condition) { case EventCondition::HAVE_ARTIFACT: - boost::algorithm::replace_first(event.onFulfill, "%s", VLC->arth->artifacts[cond.objectType]->Name()); + boost::algorithm::replace_first(event.onFulfill, "%s", VLC->arth->objects[cond.objectType]->getName()); break; case EventCondition::HAVE_CREATURES: - boost::algorithm::replace_first(event.onFulfill, "%s", VLC->creh->creatures[cond.objectType]->nameSing); + boost::algorithm::replace_first(event.onFulfill, "%s", VLC->creh->objects[cond.objectType]->nameSing); boost::algorithm::replace_first(event.onFulfill, "%d", boost::lexical_cast(cond.value)); break; diff --git a/lib/mapping/MapFormatH3M.cpp b/lib/mapping/MapFormatH3M.cpp index 3a692dbfe..6eba5033c 100644 --- a/lib/mapping/MapFormatH3M.cpp +++ b/lib/mapping/MapFormatH3M.cpp @@ -602,7 +602,7 @@ void CMapLoaderH3M::readTeamInfo() void CMapLoaderH3M::readAllowedHeroes() { - mapHeader->allowedHeroes.resize(VLC->heroh->heroes.size(), true); + mapHeader->allowedHeroes.resize(VLC->heroh->size(), true); const int bytes = mapHeader->version == EMapFormat::ROE ? 16 : 20; @@ -646,7 +646,7 @@ void CMapLoaderH3M::readDisposedHeroes() void CMapLoaderH3M::readAllowedArtifacts() { - map->allowedArtifact.resize (VLC->arth->artifacts.size(),true); //handle new artifacts, make them allowed by default + map->allowedArtifact.resize (VLC->arth->objects.size(),true); //handle new artifacts, make them allowed by default // Reading allowed artifacts: 17 or 18 bytes if(map->version != EMapFormat::ROE) @@ -660,7 +660,7 @@ void CMapLoaderH3M::readAllowedArtifacts() // ban combo artifacts if (map->version == EMapFormat::ROE || map->version == EMapFormat::AB) { - for(CArtifact * artifact : VLC->arth->artifacts) + for(CArtifact * artifact : VLC->arth->objects) { // combo if (artifact->constituents) @@ -712,7 +712,7 @@ void CMapLoaderH3M::readAllowedSpellsAbilities() //do not generate special abilities and spells for (auto spell : VLC->spellh->objects) - if (spell->isSpecialSpell() || spell->isCreatureAbility()) + if (spell->isSpecial() || spell->isCreatureAbility()) map->allowedSpell[spell->id] = false; } @@ -873,7 +873,7 @@ bool CMapLoaderH3M::loadArtifactToSlot(CGHeroInstance * hero, int slot) bool isArt = aid != artmask; if(isArt) { - const CArtifact * art = ArtifactID(aid).toArtifact(); + const Artifact * art = ArtifactID(aid).toArtifact(VLC->artifacts()); if(nullptr == art) { @@ -1370,7 +1370,7 @@ void CMapLoaderH3M::readObjects() mask[1] = reader.readUInt8(); castleSpec->allowedFactions.clear(); - castleSpec->allowedFactions.resize(VLC->townh->factions.size(), false); + castleSpec->allowedFactions.resize(VLC->townh->size(), false); for(int i = 0; i < MASK_SIZE; i++) castleSpec->allowedFactions[i] = ((mask[0] & (1 << i))>0); @@ -1423,7 +1423,7 @@ void CMapLoaderH3M::readObjects() } else { - logGlobal->info("Hero placeholder: %s at %s", VLC->heroh->heroes[htid]->name, objPos.toString()); + logGlobal->info("Hero placeholder: %s at %s", VLC->heroh->objects[htid]->name, objPos.toString()); hp->power = 0; } @@ -1887,7 +1887,7 @@ void CMapLoaderH3M::readQuest(IQuestObject * guard) guard->quest->m6creatures.resize(typeNumber); for(int hh = 0; hh < typeNumber; ++hh) { - guard->quest->m6creatures[hh].type = VLC->creh->creatures[reader.readUInt16()]; + guard->quest->m6creatures[hh].type = VLC->creh->objects[reader.readUInt16()]; guard->quest->m6creatures[hh].count = reader.readUInt16(); } break; diff --git a/lib/mapping/MapFormatJson.cpp b/lib/mapping/MapFormatJson.cpp index 97d69927f..6d956f42b 100644 --- a/lib/mapping/MapFormatJson.cpp +++ b/lib/mapping/MapFormatJson.cpp @@ -363,17 +363,17 @@ void CMapFormatJson::serializeAllowedFactions(JsonSerializeFormat & handler, std //TODO: unify allowed factions with others - make them std::vector std::vector temp; - temp.resize(VLC->townh->factions.size(), false); + temp.resize(VLC->townh->size(), false); auto standard = VLC->townh->getDefaultAllowed(); if(handler.saving) { - for(auto faction : VLC->townh->factions) + for(auto faction : VLC->townh->objects) if(faction->town && vstd::contains(value, faction->index)) temp[std::size_t(faction->index)] = true; } - handler.serializeLIC("allowedFactions", &CTownHandler::decodeFaction, &CTownHandler::encodeFaction, standard, temp); + handler.serializeLIC("allowedFactions", &FactionID::decode, &FactionID::encode, standard, temp); if(!handler.saving) { @@ -395,7 +395,7 @@ void CMapFormatJson::serializeHeader(JsonSerializeFormat & handler) serializePlayerInfo(handler); - handler.serializeLIC("allowedHeroes", &CHeroHandler::decodeHero, &CHeroHandler::encodeHero, VLC->heroh->getDefaultAllowed(), mapHeader->allowedHeroes); + handler.serializeLIC("allowedHeroes", &HeroTypeID::decode, &HeroTypeID::encode, VLC->heroh->getDefaultAllowed(), mapHeader->allowedHeroes); handler.serializeString("victoryString", mapHeader->victoryMessage); handler.serializeInt("victoryIconIndex", mapHeader->victoryIconIndex); @@ -513,7 +513,7 @@ void CMapFormatJson::serializePlayerInfo(JsonSerializeFormat & handler) } else { - temp = VLC->heroh->heroes[hero->subID]->identifier; + temp = VLC->heroh->objects[hero->subID]->identifier; } handler.serializeString("type", temp); } @@ -529,21 +529,19 @@ void CMapFormatJson::serializePlayerInfo(JsonSerializeFormat & handler) for(const auto & hero : handler.getCurrent().Struct()) { - const JsonNode & data = hero.second; - const std::string instanceName = hero.first; + const JsonNode & data = hero.second; + const std::string instanceName = hero.first; - SHeroName hname; + SHeroName hname; hname.heroId = -1; std::string rawId = data["type"].String(); if(rawId != "") - { - hname.heroId = VLC->heroh->decodeHero(rawId); - } + hname.heroId = HeroTypeID::decode(rawId); - hname.heroName = data["name"].String(); + hname.heroName = data["name"].String(); - if(instanceName == info.mainHeroInstance) + if(instanceName == info.mainHeroInstance) { //this is main hero info.mainCustomHeroName = hname.heroName; @@ -702,7 +700,7 @@ void CMapFormatJson::readDisposedHeroes(JsonSerializeFormat & handler) for(const auto & entry : data.Struct()) { - HeroTypeID type(VLC->heroh->decodeHero(entry.first)); + HeroTypeID type(HeroTypeID::decode(entry.first)); ui8 mask = 0; @@ -737,7 +735,7 @@ void CMapFormatJson::writeDisposedHeroes(JsonSerializeFormat & handler) for(const DisposedHero & hero : map->disposedHeroes) { - std::string type = VLC->heroh->encodeHero(hero.heroId); + std::string type = HeroTypeID::encode(hero.heroId); auto definition = definitions->enterStruct(type); diff --git a/lib/registerTypes/RegisterTypes.h b/lib/registerTypes/RegisterTypes.h index 527ca9e03..c5611f5e6 100644 --- a/lib/registerTypes/RegisterTypes.h +++ b/lib/registerTypes/RegisterTypes.h @@ -263,6 +263,7 @@ void registerTypesClientPacks1(Serializer &s) s.template registerType(); s.template registerType(); s.template registerType(); + s.template registerType(); } template @@ -272,6 +273,7 @@ void registerTypesClientPacks2(Serializer &s) s.template registerType(); s.template registerType(); s.template registerType(); + s.template registerType(); s.template registerType(); s.template registerType(); s.template registerType(); @@ -341,8 +343,6 @@ void registerTypesServerPacks(Serializer &s) s.template registerType(); s.template registerType(); s.template registerType(); - s.template registerType(); - s.template registerType(); s.template registerType(); } diff --git a/lib/rmg/CMapGenOptions.cpp b/lib/rmg/CMapGenOptions.cpp index 321bf1331..7d377da49 100644 --- a/lib/rmg/CMapGenOptions.cpp +++ b/lib/rmg/CMapGenOptions.cpp @@ -475,8 +475,8 @@ void CMapGenOptions::CPlayerSettings::setStartingTown(si32 value) assert(value >= -1); if(value >= 0) { - assert(value < static_cast(VLC->townh->factions.size())); - assert(VLC->townh->factions[value]->town != nullptr); + assert(value < static_cast(VLC->townh->size())); + assert((*VLC->townh)[value]->town != nullptr); } startingTown = value; } diff --git a/lib/rmg/CMapGenerator.cpp b/lib/rmg/CMapGenerator.cpp index 95f453430..f7e33fcdd 100644 --- a/lib/rmg/CMapGenerator.cpp +++ b/lib/rmg/CMapGenerator.cpp @@ -116,7 +116,7 @@ void CMapGenerator::initPrisonsRemaining() void CMapGenerator::initQuestArtsRemaining() { - for (auto art : VLC->arth->artifacts) + for (auto art : VLC->arth->objects) { if (art->aClass == CArtifact::ART_TREASURE && VLC->arth->legalArtifact(art->id) && art->constituentOf.empty()) //don't use parts of combined artifacts questArtifacts.push_back(art->id); @@ -189,7 +189,7 @@ std::string CMapGenerator::getMapDescription() const if(pSettings.getStartingTown() != CMapGenOptions::CPlayerSettings::RANDOM_TOWN) { ss << ", " << GameConstants::PLAYER_COLOR_NAMES[pSettings.getColor().getNum()] - << " town choice is " << VLC->townh->factions[pSettings.getStartingTown()]->name; + << " town choice is " << (*VLC->townh)[pSettings.getStartingTown()]->name; } } diff --git a/lib/rmg/CRmgTemplate.cpp b/lib/rmg/CRmgTemplate.cpp index de3140802..084e8716b 100644 --- a/lib/rmg/CRmgTemplate.cpp +++ b/lib/rmg/CRmgTemplate.cpp @@ -82,20 +82,6 @@ public: } }; -class FactionEncoder -{ -public: - static si32 decode(const std::string & json) - { - return VLC->townh->decodeFaction(json); - } - - static std::string encode(si32 id) - { - return VLC->townh->encodeFaction(id); - } -}; - const std::set ZoneOptions::DEFAULT_TERRAIN_TYPES = { ETerrainType::DIRT, @@ -338,8 +324,8 @@ void ZoneOptions::serializeJson(JsonSerializeFormat & handler) handler.serializeIdArray("terrainTypes", terrainTypes, DEFAULT_TERRAIN_TYPES); handler.serializeBool("townsAreSameType", townsAreSameType, false); - handler.serializeIdArray("allowedMonsters", monsterTypes, VLC->townh->getAllowedFactions(false)); - handler.serializeIdArray("allowedTowns", townTypes, VLC->townh->getAllowedFactions(true)); + handler.serializeIdArray("allowedMonsters", monsterTypes, VLC->townh->getAllowedFactions(false)); + handler.serializeIdArray("allowedTowns", townTypes, VLC->townh->getAllowedFactions(true)); { //TODO: add support for std::map to serializeEnum diff --git a/lib/rmg/CRmgTemplateZone.cpp b/lib/rmg/CRmgTemplateZone.cpp index 82544b23f..1c7252831 100644 --- a/lib/rmg/CRmgTemplateZone.cpp +++ b/lib/rmg/CRmgTemplateZone.cpp @@ -818,7 +818,7 @@ bool CRmgTemplateZone::addMonster(int3 &pos, si32 strength, bool clearSurroundin CreatureID creId = CreatureID::NONE; int amount = 0; std::vector possibleCreatures; - for (auto cre : VLC->creh->creatures) + for (auto cre : VLC->creh->objects) { if (cre->special) continue; @@ -834,14 +834,14 @@ bool CRmgTemplateZone::addMonster(int3 &pos, si32 strength, bool clearSurroundin if (possibleCreatures.size()) { creId = *RandomGeneratorUtil::nextItem(possibleCreatures, gen->rand); - amount = strength / VLC->creh->creatures[creId]->AIValue; + amount = strength / VLC->creh->objects[creId]->AIValue; if (amount >= 4) amount = static_cast(amount * gen->rand.nextDouble(0.75, 1.25)); } else //just pick any available creature { creId = CreatureID(132); //Azure Dragon - amount = strength / VLC->creh->creatures[creId]->AIValue; + amount = strength / VLC->creh->objects[creId]->AIValue; } auto guardFactory = VLC->objtypeh->getHandlerFor(Obj::MONSTER, creId); @@ -1142,9 +1142,9 @@ void CRmgTemplateZone::initTownType () town->builtBuildings.insert(BuildingID::FORT); town->builtBuildings.insert(BuildingID::DEFAULT); - for (auto spell : VLC->spellh->objects) //add all regular spells to town + for(auto spell : VLC->spellh->objects) //add all regular spells to town { - if (!spell->isSpecialSpell() && !spell->isCreatureAbility()) + if(!spell->isSpecial() && !spell->isCreatureAbility()) town->possibleSpells.push_back(spell->id); } @@ -1192,9 +1192,9 @@ void CRmgTemplateZone::initTownType () town->builtBuildings.insert(BuildingID::FORT); town->builtBuildings.insert(BuildingID::DEFAULT); - for (auto spell : VLC->spellh->objects) //add all regular spells to town + for(auto spell : VLC->spellh->objects) //add all regular spells to town { - if (!spell->isSpecialSpell() && !spell->isCreatureAbility()) + if(!spell->isSpecial() && !spell->isCreatureAbility()) town->possibleSpells.push_back(spell->id); } //towns are big objects and should be centered around visitable position @@ -1266,7 +1266,7 @@ void CRmgTemplateZone::initTerrainType () { if (matchTerrainToTown && townType != ETownType::NEUTRAL) - terrainType = VLC->townh->factions[townType]->nativeTerrain; + terrainType = (*VLC->townh)[townType]->nativeTerrain; else terrainType = *RandomGeneratorUtil::nextItem(terrainTypes, gen->rand); @@ -2166,7 +2166,7 @@ void CRmgTemplateZone::addAllPossibleObjects() int numZones = static_cast(gen->getZones().size()); std::vector creatures; //native creatures for this zone - for (auto cre : VLC->creh->creatures) + for (auto cre : VLC->creh->objects) { if (!cre->special && cre->faction == townType) { @@ -2300,7 +2300,7 @@ void CRmgTemplateZone::addAllPossibleObjects() out.push_back(spell->id); } } - auto a = CArtifactInstance::createScroll(RandomGeneratorUtil::nextItem(out, gen->rand)->toSpell()); + auto a = CArtifactInstance::createScroll(*RandomGeneratorUtil::nextItem(out, gen->rand)); obj->storedArtifact = a; return obj; }; @@ -2433,7 +2433,6 @@ void CRmgTemplateZone::addAllPossibleObjects() std::vector spells; for (auto spell : VLC->spellh->objects) { - if (gen->isAllowedSpell(spell->id) && spell->school[(ESpellSchool)i]) spells.push_back(spell); } diff --git a/lib/rmg/CZonePlacer.cpp b/lib/rmg/CZonePlacer.cpp index 9cb59eb4b..e7d70b7b6 100644 --- a/lib/rmg/CZonePlacer.cpp +++ b/lib/rmg/CZonePlacer.cpp @@ -185,7 +185,7 @@ void CZonePlacer::prepareZones(TZoneMap &zones, TZoneVector &zonesVector, const zonesToPlace.push_back(zone); else { - switch (VLC->townh->factions[faction]->nativeTerrain) + switch ((*VLC->townh)[faction]->nativeTerrain) { case ETerrainType::GRASS: case ETerrainType::SWAMP: diff --git a/lib/serializer/CSerializer.cpp b/lib/serializer/CSerializer.cpp index 00aa956c6..ee9977f75 100644 --- a/lib/serializer/CSerializer.cpp +++ b/lib/serializer/CSerializer.cpp @@ -31,13 +31,13 @@ void CSerializer::addStdVecItems(CGameState *gs, LibClasses *lib) { registerVectoredType(&gs->map->objects, [](const CGObjectInstance &obj){ return obj.id; }); - registerVectoredType(&lib->heroh->heroes, + registerVectoredType(&lib->heroh->objects, [](const CHero &h){ return h.ID; }); registerVectoredType(&gs->map->allHeroes, [](const CGHeroInstance &h){ return h.type->ID; }); - registerVectoredType(&lib->creh->creatures, + registerVectoredType(&lib->creh->objects, [](const CCreature &cre){ return cre.idNumber; }); - registerVectoredType(&lib->arth->artifacts, + registerVectoredType(&lib->arth->objects, [](const CArtifact &art){ return art.id; }); registerVectoredType(&gs->map->artInstances, [](const CArtifactInstance &artInst){ return artInst.id; }); diff --git a/lib/serializer/CSerializer.h b/lib/serializer/CSerializer.h index 6622fe7d9..57fad1833 100644 --- a/lib/serializer/CSerializer.h +++ b/lib/serializer/CSerializer.h @@ -12,7 +12,7 @@ #include "../ConstTransitivePtr.h" #include "../GameConstants.h" -const ui32 SERIALIZATION_VERSION = 794; +const ui32 SERIALIZATION_VERSION = 800; const ui32 MINIMAL_SERIALIZATION_VERSION = 753; const std::string SAVEGAME_MAGIC = "VCMISVG"; diff --git a/lib/serializer/JsonDeserializer.cpp b/lib/serializer/JsonDeserializer.cpp index c6f284ef4..4fc2c9bbd 100644 --- a/lib/serializer/JsonDeserializer.cpp +++ b/lib/serializer/JsonDeserializer.cpp @@ -12,8 +12,10 @@ #include "../JsonNode.h" +#include + JsonDeserializer::JsonDeserializer(const IInstanceResolver * instanceResolver_, const JsonNode & root_): - JsonTreeSerializer(instanceResolver_, &root_, false) + JsonTreeSerializer(instanceResolver_, &root_, false, false) { } diff --git a/lib/serializer/JsonSerializeFormat.cpp b/lib/serializer/JsonSerializeFormat.cpp index 587836b30..8e7143629 100644 --- a/lib/serializer/JsonSerializeFormat.cpp +++ b/lib/serializer/JsonSerializeFormat.cpp @@ -123,8 +123,9 @@ JsonSerializeFormat::LICSet::LICSet(const std::set& Standard, const TDecod } //JsonSerializeFormat -JsonSerializeFormat::JsonSerializeFormat(const IInstanceResolver * instanceResolver_, const bool saving_): - saving(saving_), +JsonSerializeFormat::JsonSerializeFormat(const IInstanceResolver * instanceResolver_, const bool saving_, const bool updating_) + : saving(saving_), + updating(updating_), instanceResolver(instanceResolver_) { } diff --git a/lib/serializer/JsonSerializeFormat.h b/lib/serializer/JsonSerializeFormat.h index 61edca347..27070923a 100644 --- a/lib/serializer/JsonSerializeFormat.h +++ b/lib/serializer/JsonSerializeFormat.h @@ -129,6 +129,7 @@ public: }; const bool saving; + const bool updating; JsonSerializeFormat() = delete; virtual ~JsonSerializeFormat() = default; @@ -420,7 +421,7 @@ public: virtual void serializeRaw(const std::string & fieldName, JsonNode & value, const boost::optional defaultValue) = 0; protected: - JsonSerializeFormat(const IInstanceResolver * instanceResolver_, const bool saving_); + JsonSerializeFormat(const IInstanceResolver * instanceResolver_, const bool saving_, const bool updating_); ///bool <-> Json bool, indeterminate is default virtual void serializeInternal(const std::string & fieldName, boost::logic::tribool & value) = 0; diff --git a/lib/serializer/JsonSerializer.cpp b/lib/serializer/JsonSerializer.cpp index 057f9d0c9..7285d7e9e 100644 --- a/lib/serializer/JsonSerializer.cpp +++ b/lib/serializer/JsonSerializer.cpp @@ -13,7 +13,7 @@ #include "../JsonNode.h" JsonSerializer::JsonSerializer(const IInstanceResolver * instanceResolver_, JsonNode & root_): - JsonTreeSerializer(instanceResolver_, &root_, true) + JsonTreeSerializer(instanceResolver_, &root_, true, false) { } diff --git a/lib/serializer/JsonTreeSerializer.h b/lib/serializer/JsonTreeSerializer.h index b4f4b1c2d..c5d2becac 100644 --- a/lib/serializer/JsonTreeSerializer.h +++ b/lib/serializer/JsonTreeSerializer.h @@ -25,8 +25,8 @@ protected: T currentObject; std::vector treeRoute; - JsonTreeSerializer(const IInstanceResolver * instanceResolver_, T root, const bool saving_) - : JsonSerializeFormat(instanceResolver_, saving_), + JsonTreeSerializer(const IInstanceResolver * instanceResolver_, T root, const bool saving_, const bool updating_) + : JsonSerializeFormat(instanceResolver_, saving_, updating_), currentObject(root), treeRoute() { diff --git a/lib/serializer/JsonUpdater.cpp b/lib/serializer/JsonUpdater.cpp new file mode 100644 index 000000000..dc69e991b --- /dev/null +++ b/lib/serializer/JsonUpdater.cpp @@ -0,0 +1,309 @@ +/* + * JsonUpdater.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 "JsonUpdater.h" + +#include "../JsonNode.h" +#include "../HeroBonus.h" + +JsonUpdater::JsonUpdater(const IInstanceResolver * instanceResolver_, const JsonNode & root_) + : JsonTreeSerializer(instanceResolver_, &root_, false, true) +{ + +} + +void JsonUpdater::serializeInternal(const std::string & fieldName, boost::logic::tribool & value) +{ + const JsonNode & data = currentObject->operator[](fieldName); + if(data.getType() == JsonNode::JsonType::DATA_BOOL) + value = data.Bool(); +} + +void JsonUpdater::serializeInternal(const std::string & fieldName, si32 & value, const boost::optional & defaultValue, const TDecoder & decoder, const TEncoder & encoder) +{ +// std::string identifier; +// serializeString(fieldName, identifier); +// +// value = defaultValue ? defaultValue.get() : 0; +// +// if(identifier != "") +// { +// si32 rawId = decoder(identifier); +// if(rawId >= 0) +// value = rawId; +// } +} + +void JsonUpdater::serializeInternal(const std::string & fieldName, std::vector & value, const TDecoder & decoder, const TEncoder & encoder) +{ +// const JsonVector & data = currentObject->operator[](fieldName).Vector(); +// +// value.clear(); +// value.reserve(data.size()); +// +// for(const JsonNode elem : data) +// { +// si32 rawId = decoder(elem.String()); +// +// if(rawId >= 0) +// value.push_back(rawId); +// } +} + +void JsonUpdater::serializeInternal(const std::string & fieldName, double & value, const boost::optional & defaultValue) +{ + const JsonNode & data = currentObject->operator[](fieldName); + + if(data.isNumber()) + value = data.Float(); +} + +void JsonUpdater::serializeInternal(const std::string & fieldName, si64 & value, const boost::optional &) +{ + const JsonNode & data = currentObject->operator[](fieldName); + + if(data.isNumber()) + value = data.Integer(); +} + +void JsonUpdater::serializeInternal(const std::string & fieldName, si32 & value, const boost::optional & defaultValue, const std::vector & enumMap) +{ +// const std::string & valueName = currentObject->operator[](fieldName).String(); +// +// const si32 actualOptional = defaultValue ? defaultValue.get() : 0; +// +// si32 rawValue = vstd::find_pos(enumMap, valueName); +// if(rawValue < 0) +// value = actualOptional; +// else +// value = rawValue; +} + +void JsonUpdater::serializeInternal(std::string & value) +{ + value = currentObject->String(); +} + +void JsonUpdater::serializeInternal(int64_t & value) +{ + value = currentObject->Integer(); +} + +void JsonUpdater::serializeLIC(const std::string & fieldName, const TDecoder & decoder, const TEncoder & encoder, const std::vector & standard, std::vector & value) +{ + const JsonNode & field = currentObject->operator[](fieldName); + + if(field.isNull()) + return; + + const JsonNode & anyOf = field["anyOf"]; + const JsonNode & allOf = field["allOf"]; + const JsonNode & noneOf = field["noneOf"]; + + if(anyOf.Vector().empty() && allOf.Vector().empty()) + { + //permissive mode + value = standard; + } + else + { + //restrictive mode + value.clear(); + value.resize(standard.size(), false); + + readLICPart(anyOf, decoder, true, value); + readLICPart(allOf, decoder, true, value); + } + + readLICPart(noneOf, decoder, false, value); +} + +void JsonUpdater::serializeLIC(const std::string & fieldName, LIC & value) +{ + const JsonNode & field = currentObject->operator[](fieldName); + + if(field.isNull()) + return; + + const JsonNode & anyOf = field["anyOf"]; + const JsonNode & allOf = field["allOf"]; + const JsonNode & noneOf = field["noneOf"]; + + if(anyOf.Vector().empty()) + { + //permissive mode + value.any = value.standard; + } + else + { + //restrictive mode + value.any.clear(); + value.any.resize(value.standard.size(), false); + + readLICPart(anyOf, value.decoder, true, value.any); + } + + readLICPart(allOf, value.decoder, true, value.all); + readLICPart(noneOf, value.decoder, true, value.none); + + //remove any banned from allowed and required + for(si32 idx = 0; idx < value.none.size(); idx++) + { + if(value.none[idx]) + { + value.all[idx] = false; + value.any[idx] = false; + } + } + + //add all required to allowed + for(si32 idx = 0; idx < value.all.size(); idx++) + { + if(value.all[idx]) + { + value.any[idx] = true; + } + } +} + +void JsonUpdater::serializeLIC(const std::string & fieldName, LICSet & value) +{ + const JsonNode & field = currentObject->operator[](fieldName); + + if(field.isNull()) + return; + + const JsonNode & anyOf = field["anyOf"]; + const JsonNode & allOf = field["allOf"]; + const JsonNode & noneOf = field["noneOf"]; + + value.all.clear(); + value.none.clear(); + + if(anyOf.Vector().empty()) + { + //permissive mode + value.any = value.standard; + } + else + { + //restrictive mode + value.any.clear(); + readLICPart(anyOf, value.decoder, value.any); + + for(si32 item : value.standard) + if(!vstd::contains(value.any, item)) + value.none.insert(item); + } + + readLICPart(allOf, value.decoder, value.all); + readLICPart(noneOf, value.decoder, value.none); + + //remove any banned from allowed and required + auto isBanned = [&value](const si32 item)->bool + { + return vstd::contains(value.none, item); + }; + vstd::erase_if(value.all, isBanned); + vstd::erase_if(value.any, isBanned); + + //add all required to allowed + for(si32 item : value.all) + { + value.any.insert(item); + } +} + +void JsonUpdater::serializeString(const std::string & fieldName, std::string & value) +{ + const JsonNode & data = currentObject->operator[](fieldName); + if(data.getType() == JsonNode::JsonType::DATA_STRING) + value = data.String(); +} + +void JsonUpdater::serializeRaw(const std::string & fieldName, JsonNode & value, const boost::optional defaultValue) +{ + const JsonNode & data = currentObject->operator[](fieldName); + if(data.getType() != JsonNode::JsonType::DATA_NULL) + value = data; +} + +void JsonUpdater::serializeBonuses(const std::string & fieldName, CBonusSystemNode * value) +{ + const JsonNode & data = currentObject->operator[](fieldName); + + const JsonNode & toAdd = data["toAdd"]; + + if(toAdd.getType() == JsonNode::JsonType::DATA_VECTOR) + { + for(auto & item : toAdd.Vector()) + { + auto b = JsonUtils::parseBonus(item); + value->addNewBonus(b); + } + } + + const JsonNode & toRemove = data["toRemove"]; + + if(toRemove.getType() == JsonNode::JsonType::DATA_VECTOR) + { + for(auto & item : toRemove.Vector()) + { + auto mask = JsonUtils::parseBonus(item); + + auto selector = [mask](const Bonus * b) + { + //compare everything but turnsRemain, limiter and propagator + return mask->duration == b->duration + && mask->type == b->type + && mask->subtype == b->subtype + && mask->source == b->source + && mask->val == b->val + && mask->sid == b->sid + && mask->valType == b->valType + && mask->additionalInfo == b->additionalInfo + && mask->effectRange == b->effectRange + && mask->description == b->description; + }; + + value->removeBonuses(selector); + } + } +} + +void JsonUpdater::readLICPart(const JsonNode & part, const TDecoder & decoder, const bool val, std::vector & value) +{ + for(size_t index = 0; index < part.Vector().size(); index++) + { + const std::string & identifier = part.Vector()[index].String(); + + const si32 rawId = decoder(identifier); + if(rawId >= 0) + { + if(rawId < value.size()) + value[rawId] = val; + else + logGlobal->error("JsonUpdater::serializeLIC: id out of bounds %d", rawId); + } + } +} + +void JsonUpdater::readLICPart(const JsonNode & part, const TDecoder & decoder, std::set & value) +{ + for(size_t index = 0; index < part.Vector().size(); index++) + { + const std::string & identifier = part.Vector()[index].String(); + + const si32 rawId = decoder(identifier); + if(rawId != -1) + value.insert(rawId); + } +} + diff --git a/lib/serializer/JsonUpdater.h b/lib/serializer/JsonUpdater.h new file mode 100644 index 000000000..d1caa7ec7 --- /dev/null +++ b/lib/serializer/JsonUpdater.h @@ -0,0 +1,44 @@ +/* + * JsonUpdater.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 "JsonTreeSerializer.h" + +class CBonusSystemNode; + +class DLL_LINKAGE JsonUpdater: public JsonTreeSerializer +{ +public: + JsonUpdater(const IInstanceResolver * instanceResolver_, const JsonNode & root_); + + void serializeLIC(const std::string & fieldName, const TDecoder & decoder, const TEncoder & encoder, const std::vector & standard, std::vector & value) override; + void serializeLIC(const std::string & fieldName, LIC & value) override; + void serializeLIC(const std::string & fieldName, LICSet & value) override; + void serializeString(const std::string & fieldName, std::string & value) override; + + void serializeRaw(const std::string & fieldName, JsonNode & value, const boost::optional defaultValue) override; + + void serializeBonuses(const std::string & fieldName, CBonusSystemNode * value); + +protected: + void serializeInternal(const std::string & fieldName, boost::logic::tribool & value) override; + void serializeInternal(const std::string & fieldName, si32 & value, const boost::optional & defaultValue, const TDecoder & decoder, const TEncoder & encoder) override; + void serializeInternal(const std::string & fieldName, std::vector & value, const TDecoder & decoder, const TEncoder & encoder) override; + void serializeInternal(const std::string & fieldName, double & value, const boost::optional & defaultValue) override; + void serializeInternal(const std::string & fieldName, si64 & value, const boost::optional & defaultValue) override; + void serializeInternal(const std::string & fieldName, si32 & value, const boost::optional & defaultValue, const std::vector & enumMap) override; + + void serializeInternal(std::string & value) override; + void serializeInternal(int64_t & value) override; + +private: + void readLICPart(const JsonNode & part, const TDecoder & decoder, const bool val, std::vector & value); + void readLICPart(const JsonNode & part, const TDecoder & decoder, std::set & value); +}; diff --git a/lib/spells/AbilityCaster.cpp b/lib/spells/AbilityCaster.cpp index 3127cf3c5..0a9192d89 100644 --- a/lib/spells/AbilityCaster.cpp +++ b/lib/spells/AbilityCaster.cpp @@ -11,12 +11,14 @@ #include "AbilityCaster.h" +#include + #include "../battle/Unit.h" namespace spells { -AbilityCaster::AbilityCaster(const battle::Unit * actualCaster_, int baseSpellLevel_) +AbilityCaster::AbilityCaster(const battle::Unit * actualCaster_, int32_t baseSpellLevel_) : ProxyCaster(actualCaster_), actualCaster(actualCaster_), baseSpellLevel(baseSpellLevel_) @@ -25,9 +27,9 @@ AbilityCaster::AbilityCaster(const battle::Unit * actualCaster_, int baseSpellLe AbilityCaster::~AbilityCaster() = default; -ui8 AbilityCaster::getSpellSchoolLevel(const Spell * spell, int * outSelectedSchool) const +int32_t AbilityCaster::getSpellSchoolLevel(const Spell * spell, int32_t * outSelectedSchool) const { - int skill = baseSpellLevel; + auto skill = baseSpellLevel; if(spell->getLevel() > 0) { @@ -40,7 +42,7 @@ ui8 AbilityCaster::getSpellSchoolLevel(const Spell * spell, int * outSelectedSch return static_cast(skill); //todo: unify spell school level type } -int AbilityCaster::getEffectLevel(const Spell * spell) const +int32_t AbilityCaster::getEffectLevel(const Spell * spell) const { return getSpellSchoolLevel(spell); } @@ -50,7 +52,7 @@ void AbilityCaster::getCastDescription(const Spell * spell, const std::vector & attacked, MetaString & text) const override; - void spendMana(const PacketSender * server, const int spellCost) const override; + void spendMana(ServerCallback * server, const int32_t spellCost) const override; private: const battle::Unit * actualCaster; - int baseSpellLevel; + int32_t baseSpellLevel; }; } // namespace spells diff --git a/lib/spells/AdventureSpellMechanics.cpp b/lib/spells/AdventureSpellMechanics.cpp index 1ea510c79..3093f300d 100644 --- a/lib/spells/AdventureSpellMechanics.cpp +++ b/lib/spells/AdventureSpellMechanics.cpp @@ -27,9 +27,9 @@ AdventureSpellMechanics::AdventureSpellMechanics(const CSpell * s): { } -bool AdventureSpellMechanics::adventureCast(const SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const +bool AdventureSpellMechanics::adventureCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const { - if(!owner->isAdventureSpell()) + if(!owner->isAdventure()) { env->complain("Attempt to cast non adventure spell in adventure mode"); return false; @@ -43,7 +43,8 @@ bool AdventureSpellMechanics::adventureCast(const SpellCastEnvironment * env, co return false; } - const int cost = caster->getSpellCost(owner); + const auto level = caster->getSpellSchoolLevel(owner); + const auto cost = owner->getCost(level); if(!caster->canCastThisSpell(owner)) { @@ -65,12 +66,12 @@ bool AdventureSpellMechanics::adventureCast(const SpellCastEnvironment * env, co return result != ESpellCastResult::ERROR; } -ESpellCastResult AdventureSpellMechanics::applyAdventureEffects(const SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const +ESpellCastResult AdventureSpellMechanics::applyAdventureEffects(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const { if(owner->hasEffects()) { //todo: cumulative effects support - const int schoolLevel = parameters.caster->getSpellSchoolLevel(owner); + const auto schoolLevel = parameters.caster->getSpellSchoolLevel(owner); std::vector bonuses; @@ -81,7 +82,7 @@ ESpellCastResult AdventureSpellMechanics::applyAdventureEffects(const SpellCastE GiveBonus gb; gb.id = parameters.caster->id.getNum(); gb.bonus = b; - env->sendAndApply(&gb); + env->apply(&gb); } return ESpellCastResult::OK; @@ -94,25 +95,26 @@ ESpellCastResult AdventureSpellMechanics::applyAdventureEffects(const SpellCastE } } -ESpellCastResult AdventureSpellMechanics::beginCast(const SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const +ESpellCastResult AdventureSpellMechanics::beginCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const { return ESpellCastResult::OK; } -void AdventureSpellMechanics::performCast(const SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const +void AdventureSpellMechanics::performCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const { AdvmapSpellCast asc; asc.casterID = parameters.caster->id; asc.spellID = owner->id; - env->sendAndApply(&asc); + env->apply(&asc); ESpellCastResult result = applyAdventureEffects(env, parameters); endCast(env, parameters, result); } -void AdventureSpellMechanics::endCast(const SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters, const ESpellCastResult result) const +void AdventureSpellMechanics::endCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters, const ESpellCastResult result) const { - const int cost = parameters.caster->getSpellCost(owner); + const auto level = parameters.caster->getSpellSchoolLevel(owner); + const auto cost = owner->getCost(level); switch(result) { @@ -122,7 +124,7 @@ void AdventureSpellMechanics::endCast(const SpellCastEnvironment * env, const Ad sm.hid = parameters.caster->id; sm.absolute = false; sm.val = -cost; - env->sendAndApply(&sm); + env->apply(&sm); } break; default: @@ -136,7 +138,7 @@ SummonBoatMechanics::SummonBoatMechanics(const CSpell * s): { } -ESpellCastResult SummonBoatMechanics::applyAdventureEffects(const SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const +ESpellCastResult SummonBoatMechanics::applyAdventureEffects(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const { if(parameters.caster->boat) { @@ -144,7 +146,7 @@ ESpellCastResult SummonBoatMechanics::applyAdventureEffects(const SpellCastEnvir iw.player = parameters.caster->tempOwner; iw.text.addTxt(MetaString::GENERAL_TXT, 333);//%s is already in boat iw.text.addReplacement(parameters.caster->name); - env->sendAndApply(&iw); + env->apply(&iw); return ESpellCastResult::CANCEL; } @@ -154,20 +156,20 @@ ESpellCastResult SummonBoatMechanics::applyAdventureEffects(const SpellCastEnvir InfoWindow iw; iw.player = parameters.caster->tempOwner; iw.text.addTxt(MetaString::GENERAL_TXT, 334);//There is no place to put the boat. - env->sendAndApply(&iw); + env->apply(&iw); return ESpellCastResult::CANCEL; } - const int schoolLevel = parameters.caster->getSpellSchoolLevel(owner); + const auto schoolLevel = parameters.caster->getSpellSchoolLevel(owner); //check if spell works at all - if(env->getRandomGenerator().nextInt(99) >= owner->getPower(schoolLevel)) //power is % chance of success + if(env->getRNG()->getInt64Range(0, 99)() >= owner->getLevelPower(schoolLevel)) //power is % chance of success { InfoWindow iw; iw.player = parameters.caster->tempOwner; iw.text.addTxt(MetaString::GENERAL_TXT, 336); //%s tried to summon a boat, but failed. iw.text.addReplacement(parameters.caster->name); - env->sendAndApply(&iw); + env->apply(&iw); return ESpellCastResult::OK; } @@ -197,14 +199,14 @@ ESpellCastResult SummonBoatMechanics::applyAdventureEffects(const SpellCastEnvir cop.objid = nearest->id; cop.nPos = summonPos + int3(1,0,0); cop.flags = 1; - env->sendAndApply(&cop); + env->apply(&cop); } else if(schoolLevel < 2) //none or basic level -> cannot create boat :( { InfoWindow iw; iw.player = parameters.caster->tempOwner; iw.text.addTxt(MetaString::GENERAL_TXT, 335); //There are no boats to summon. - env->sendAndApply(&iw); + env->apply(&iw); } else //create boat { @@ -212,7 +214,7 @@ ESpellCastResult SummonBoatMechanics::applyAdventureEffects(const SpellCastEnvir no.ID = Obj::BOAT; no.subID = parameters.caster->getBoatType(); no.pos = summonPos + int3(1,0,0); - env->sendAndApply(&no); + env->apply(&no); } return ESpellCastResult::OK; } @@ -223,17 +225,17 @@ ScuttleBoatMechanics::ScuttleBoatMechanics(const CSpell * s): { } -ESpellCastResult ScuttleBoatMechanics::applyAdventureEffects(const SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const +ESpellCastResult ScuttleBoatMechanics::applyAdventureEffects(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const { - const int schoolLevel = parameters.caster->getSpellSchoolLevel(owner); + const auto schoolLevel = parameters.caster->getSpellSchoolLevel(owner); //check if spell works at all - if(env->getRandomGenerator().nextInt(99) >= owner->getPower(schoolLevel)) //power is % chance of success + if(env->getRNG()->getInt64Range(0, 99)() >= owner->getLevelPower(schoolLevel)) //power is % chance of success { InfoWindow iw; iw.player = parameters.caster->tempOwner; iw.text.addTxt(MetaString::GENERAL_TXT, 337); //%s tried to scuttle the boat, but failed iw.text.addReplacement(parameters.caster->name); - env->sendAndApply(&iw); + env->apply(&iw); return ESpellCastResult::OK; } @@ -253,7 +255,7 @@ ESpellCastResult ScuttleBoatMechanics::applyAdventureEffects(const SpellCastEnvi RemoveObject ro; ro.id = t->visitableObjects.back()->id; - env->sendAndApply(&ro); + env->apply(&ro); return ESpellCastResult::OK; } @@ -263,7 +265,7 @@ DimensionDoorMechanics::DimensionDoorMechanics(const CSpell * s): { } -ESpellCastResult DimensionDoorMechanics::applyAdventureEffects(const SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const +ESpellCastResult DimensionDoorMechanics::applyAdventureEffects(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const { if(!env->getMap()->isInTheMap(parameters.pos)) { @@ -292,33 +294,33 @@ ESpellCastResult DimensionDoorMechanics::applyAdventureEffects(const SpellCastEn return ESpellCastResult::ERROR; } - const int schoolLevel = parameters.caster->getSpellSchoolLevel(owner); + const auto schoolLevel = parameters.caster->getSpellSchoolLevel(owner); const int movementCost = GameConstants::BASE_MOVEMENT_COST * ((schoolLevel >= 3) ? 2 : 3); std::stringstream cachingStr; cachingStr << "source_" << Bonus::SPELL_EFFECT << "id_" << owner->id.num; - if(parameters.caster->getBonuses(Selector::source(Bonus::SPELL_EFFECT, owner->id), Selector::all, cachingStr.str())->size() >= owner->getPower(schoolLevel)) //limit casts per turn + if(parameters.caster->getBonuses(Selector::source(Bonus::SPELL_EFFECT, owner->id), Selector::all, cachingStr.str())->size() >= owner->getLevelPower(schoolLevel)) //limit casts per turn { InfoWindow iw; iw.player = parameters.caster->tempOwner; iw.text.addTxt(MetaString::GENERAL_TXT, 338); //%s is not skilled enough to cast this spell again today. iw.text.addReplacement(parameters.caster->name); - env->sendAndApply(&iw); + env->apply(&iw); return ESpellCastResult::CANCEL; } GiveBonus gb; gb.id = parameters.caster->id.getNum(); gb.bonus = Bonus(Bonus::ONE_DAY, Bonus::NONE, Bonus::SPELL_EFFECT, 0, owner->id); - env->sendAndApply(&gb); + env->apply(&gb); if(!dest->isClear(curr)) //wrong dest tile { InfoWindow iw; iw.player = parameters.caster->tempOwner; iw.text.addTxt(MetaString::GENERAL_TXT, 70); //Dimension Door failed! - env->sendAndApply(&iw); + env->apply(&iw); } else if(env->moveHero(parameters.caster->id, parameters.pos + parameters.caster->getVisitableOffset(), true)) { @@ -328,7 +330,7 @@ ESpellCastResult DimensionDoorMechanics::applyAdventureEffects(const SpellCastEn smp.val = parameters.caster->movement - movementCost; else smp.val = 0; - env->sendAndApply(&smp); + env->apply(&smp); } return ESpellCastResult::OK; } @@ -339,13 +341,13 @@ TownPortalMechanics::TownPortalMechanics(const CSpell * s): { } -ESpellCastResult TownPortalMechanics::applyAdventureEffects(const SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const +ESpellCastResult TownPortalMechanics::applyAdventureEffects(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const { const CGTownInstance * destination = nullptr; const int moveCost = movementCost(parameters); - if(parameters.caster->getSpellSchoolLevel(owner) < 2) - { + if(parameters.caster->getSpellSchoolLevel(owner) < 2) + { std::vector pool = getPossibleTowns(env, parameters); destination = findNearestTown(env, parameters, pool); @@ -360,7 +362,7 @@ ESpellCastResult TownPortalMechanics::applyAdventureEffects(const SpellCastEnvir InfoWindow iw; iw.player = parameters.caster->tempOwner; iw.text.addTxt(MetaString::GENERAL_TXT, 123); - env->sendAndApply(&iw); + env->apply(&iw); return ESpellCastResult::CANCEL; } } @@ -425,12 +427,12 @@ ESpellCastResult TownPortalMechanics::applyAdventureEffects(const SpellCastEnvir SetMovePoints smp; smp.hid = parameters.caster->id; smp.val = std::max(0, parameters.caster->movement - moveCost); - env->sendAndApply(&smp); + env->apply(&smp); } return ESpellCastResult::OK; } -ESpellCastResult TownPortalMechanics::beginCast(const SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const +ESpellCastResult TownPortalMechanics::beginCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const { std::vector towns = getPossibleTowns(env, parameters); @@ -439,7 +441,7 @@ ESpellCastResult TownPortalMechanics::beginCast(const SpellCastEnvironment * env InfoWindow iw; iw.player = parameters.caster->tempOwner; iw.text.addTxt(MetaString::GENERAL_TXT, 124); - env->sendAndApply(&iw); + env->apply(&iw); return ESpellCastResult::CANCEL; } @@ -450,7 +452,7 @@ ESpellCastResult TownPortalMechanics::beginCast(const SpellCastEnvironment * env InfoWindow iw; iw.player = parameters.caster->tempOwner; iw.text.addTxt(MetaString::GENERAL_TXT, 125); - env->sendAndApply(&iw); + env->apply(&iw); return ESpellCastResult::CANCEL; } @@ -495,7 +497,7 @@ ESpellCastResult TownPortalMechanics::beginCast(const SpellCastEnvironment * env InfoWindow iw; iw.player = parameters.caster->tempOwner; iw.text.addTxt(MetaString::GENERAL_TXT, 124); - env->sendAndApply(&iw); + env->apply(&iw); return ESpellCastResult::CANCEL; } @@ -513,7 +515,7 @@ ESpellCastResult TownPortalMechanics::beginCast(const SpellCastEnvironment * env return ESpellCastResult::OK; } -const CGTownInstance * TownPortalMechanics::findNearestTown(const SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters, const std::vector & pool) const +const CGTownInstance * TownPortalMechanics::findNearestTown(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters, const std::vector & pool) const { if(pool.empty()) return nullptr; @@ -534,7 +536,7 @@ const CGTownInstance * TownPortalMechanics::findNearestTown(const SpellCastEnvir return *nearest; } -std::vector TownPortalMechanics::getPossibleTowns(const SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const +std::vector TownPortalMechanics::getPossibleTowns(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const { std::vector ret; @@ -542,7 +544,7 @@ std::vector TownPortalMechanics::getPossibleTowns(const for(const auto & color : team->players) { - for(auto currTown : env->getCb()->getPlayer(color)->towns) + for(auto currTown : env->getCb()->getPlayerState(color)->towns) { ret.push_back(currTown.get()); } @@ -550,7 +552,7 @@ std::vector TownPortalMechanics::getPossibleTowns(const return ret; } -int TownPortalMechanics::movementCost(const AdventureSpellCastParameters & parameters) const +int32_t TownPortalMechanics::movementCost(const AdventureSpellCastParameters & parameters) const { return GameConstants::BASE_MOVEMENT_COST * ((parameters.caster->getSpellSchoolLevel(owner) >= 3) ? 2 : 3); } @@ -561,13 +563,13 @@ ViewMechanics::ViewMechanics(const CSpell * s): { } -ESpellCastResult ViewMechanics::applyAdventureEffects(const SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const +ESpellCastResult ViewMechanics::applyAdventureEffects(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const { ShowWorldViewEx pack; pack.player = parameters.caster->getOwner(); - const int spellLevel = parameters.caster->getSpellSchoolLevel(owner); + const auto spellLevel = parameters.caster->getSpellSchoolLevel(owner); const auto & fowMap = env->getCb()->getPlayerTeam(parameters.caster->getOwner())->fogOfWarMap; @@ -583,7 +585,7 @@ ESpellCastResult ViewMechanics::applyAdventureEffects(const SpellCastEnvironment } } - env->sendAndApply(&pack); + env->apply(&pack); return ESpellCastResult::OK; } @@ -594,7 +596,7 @@ ViewAirMechanics::ViewAirMechanics(const CSpell * s): { } -bool ViewAirMechanics::filterObject(const CGObjectInstance * obj, const int spellLevel) const +bool ViewAirMechanics::filterObject(const CGObjectInstance * obj, const int32_t spellLevel) const { return (obj->ID == Obj::ARTIFACT) || (spellLevel > 1 && obj->ID == Obj::HERO) || (spellLevel > 2 && obj->ID == Obj::TOWN); } @@ -605,7 +607,7 @@ ViewEarthMechanics::ViewEarthMechanics(const CSpell * s): { } -bool ViewEarthMechanics::filterObject(const CGObjectInstance * obj, const int spellLevel) const +bool ViewEarthMechanics::filterObject(const CGObjectInstance * obj, const int32_t spellLevel) const { return (obj->ID == Obj::RESOURCE) || (spellLevel > 1 && obj->ID == Obj::MINE); } diff --git a/lib/spells/AdventureSpellMechanics.h b/lib/spells/AdventureSpellMechanics.h index 186d3b120..f8860f8b6 100644 --- a/lib/spells/AdventureSpellMechanics.h +++ b/lib/spells/AdventureSpellMechanics.h @@ -27,13 +27,13 @@ class DLL_LINKAGE AdventureSpellMechanics : public IAdventureSpellMechanics public: AdventureSpellMechanics(const CSpell * s); - bool adventureCast(const SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const override final; + bool adventureCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const override final; protected: ///actual adventure cast implementation - virtual ESpellCastResult applyAdventureEffects(const SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const; - virtual ESpellCastResult beginCast(const SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const; - void performCast(const SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const; - void endCast(const SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters, const ESpellCastResult result) const; + virtual ESpellCastResult applyAdventureEffects(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const; + virtual ESpellCastResult beginCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const; + void performCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const; + void endCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters, const ESpellCastResult result) const; }; class DLL_LINKAGE SummonBoatMechanics : public AdventureSpellMechanics @@ -41,7 +41,7 @@ class DLL_LINKAGE SummonBoatMechanics : public AdventureSpellMechanics public: SummonBoatMechanics(const CSpell * s); protected: - ESpellCastResult applyAdventureEffects(const SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const override; + ESpellCastResult applyAdventureEffects(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const override; }; class DLL_LINKAGE ScuttleBoatMechanics : public AdventureSpellMechanics @@ -49,7 +49,7 @@ class DLL_LINKAGE ScuttleBoatMechanics : public AdventureSpellMechanics public: ScuttleBoatMechanics(const CSpell * s); protected: - ESpellCastResult applyAdventureEffects(const SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const override; + ESpellCastResult applyAdventureEffects(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const override; }; class DLL_LINKAGE DimensionDoorMechanics : public AdventureSpellMechanics @@ -57,7 +57,7 @@ class DLL_LINKAGE DimensionDoorMechanics : public AdventureSpellMechanics public: DimensionDoorMechanics(const CSpell * s); protected: - ESpellCastResult applyAdventureEffects(const SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const override; + ESpellCastResult applyAdventureEffects(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const override; }; class DLL_LINKAGE TownPortalMechanics : public AdventureSpellMechanics @@ -65,12 +65,12 @@ class DLL_LINKAGE TownPortalMechanics : public AdventureSpellMechanics public: TownPortalMechanics(const CSpell * s); protected: - ESpellCastResult applyAdventureEffects(const SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const override; - ESpellCastResult beginCast(const SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const override; + ESpellCastResult applyAdventureEffects(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const override; + ESpellCastResult beginCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const override; private: - const CGTownInstance * findNearestTown(const SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters, const std::vector & pool) const; - int movementCost(const AdventureSpellCastParameters & parameters) const; - std::vector getPossibleTowns(const SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const; + const CGTownInstance * findNearestTown(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters, const std::vector & pool) const; + int32_t movementCost(const AdventureSpellCastParameters & parameters) const; + std::vector getPossibleTowns(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const; }; class DLL_LINKAGE ViewMechanics : public AdventureSpellMechanics @@ -78,8 +78,8 @@ class DLL_LINKAGE ViewMechanics : public AdventureSpellMechanics public: ViewMechanics(const CSpell * s); protected: - ESpellCastResult applyAdventureEffects(const SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const override; - virtual bool filterObject(const CGObjectInstance * obj, const int spellLevel) const = 0; + ESpellCastResult applyAdventureEffects(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const override; + virtual bool filterObject(const CGObjectInstance * obj, const int32_t spellLevel) const = 0; }; class DLL_LINKAGE ViewAirMechanics : public ViewMechanics @@ -87,7 +87,7 @@ class DLL_LINKAGE ViewAirMechanics : public ViewMechanics public: ViewAirMechanics(const CSpell * s); protected: - bool filterObject(const CGObjectInstance * obj, const int spellLevel) const override; + bool filterObject(const CGObjectInstance * obj, const int32_t spellLevel) const override; }; class DLL_LINKAGE ViewEarthMechanics : public ViewMechanics @@ -95,7 +95,7 @@ class DLL_LINKAGE ViewEarthMechanics : public ViewMechanics public: ViewEarthMechanics(const CSpell * s); protected: - bool filterObject(const CGObjectInstance * obj, const int spellLevel) const override; + bool filterObject(const CGObjectInstance * obj, const int32_t spellLevel) const override; }; diff --git a/lib/spells/BattleSpellMechanics.cpp b/lib/spells/BattleSpellMechanics.cpp index fbc8caccf..ce0e79e56 100644 --- a/lib/spells/BattleSpellMechanics.cpp +++ b/lib/spells/BattleSpellMechanics.cpp @@ -134,7 +134,7 @@ BattleSpellMechanics::BattleSpellMechanics(const IBattleCast * event, std::share BattleSpellMechanics::~BattleSpellMechanics() = default; -void BattleSpellMechanics::applyEffects(BattleStateProxy * battleState, vstd::RNG & rng, const Target & targets, bool indirect, bool ignoreImmunity) const +void BattleSpellMechanics::applyEffects(ServerCallback * server, const Target & targets, bool indirect, bool ignoreImmunity) const { auto callback = [&](const effects::Effect * effect, bool & stop) { @@ -142,12 +142,12 @@ void BattleSpellMechanics::applyEffects(BattleStateProxy * battleState, vstd::RN { if(ignoreImmunity) { - effect->apply(battleState, rng, this, targets); + effect->apply(server, this, targets); } else { EffectTarget filtered = effect->filterTarget(this, targets); - effect->apply(battleState, rng, this, filtered); + effect->apply(server, this, filtered); } } }; @@ -157,14 +157,56 @@ void BattleSpellMechanics::applyEffects(BattleStateProxy * battleState, vstd::RN bool BattleSpellMechanics::canBeCast(Problem & problem) const { + auto genProblem = battle()->battleCanCastSpell(caster, mode); + if(genProblem != ESpellCastProblem::OK) + return adaptProblem(genProblem, problem); + + switch(mode) + { + case Mode::HERO: + { + const CGHeroInstance * castingHero = dynamic_cast(caster);//todo: unify hero|creature spell cost + if(!castingHero) + { + logGlobal->debug("CSpell::canBeCast: invalid caster"); + genProblem = ESpellCastProblem::NO_HERO_TO_CAST_SPELL; + } + else if(!castingHero->getArt(ArtifactPosition::SPELLBOOK)) + genProblem = ESpellCastProblem::NO_SPELLBOOK; + else if(!castingHero->canCastThisSpell(owner)) + genProblem = ESpellCastProblem::HERO_DOESNT_KNOW_SPELL; + else if(castingHero->mana < battle()->battleGetSpellCost(owner, castingHero)) //not enough mana + genProblem = ESpellCastProblem::NOT_ENOUGH_MANA; + } + break; + } + + if(genProblem != ESpellCastProblem::OK) + return adaptProblem(genProblem, problem); + + if(!owner->isCombat()) + return adaptProblem(ESpellCastProblem::ADVMAP_SPELL_INSTEAD_OF_BATTLE_SPELL, problem); + + const PlayerColor player = caster->getCasterOwner(); + const auto side = battle()->playerToSide(player); + + if(!side) + return adaptProblem(ESpellCastProblem::INVALID, problem); + + //effect like Recanter's Cloak. Blocks also passive casting. + //TODO: check creature abilities to block + //TODO: check any possible caster + + if(battle()->battleMaxSpellLevel(side.get()) < getSpellLevel() || battle()->battleMinSpellLevel(side.get()) > getSpellLevel()) + return adaptProblem(ESpellCastProblem::SPELL_LEVEL_LIMIT_EXCEEDED, problem); + return effects->applicable(problem, this); } -bool BattleSpellMechanics::canBeCastAt(const Target & target) const +bool BattleSpellMechanics::canBeCastAt(Problem & problem, const Target & target) const { - detail::ProblemImpl problem; - - //TODO: send problem to caller (for battle log message in BattleInterface) + if(!canBeCast(problem)) + return false; Target spellTarget = transformSpellTarget(target); @@ -190,7 +232,7 @@ std::vector BattleSpellMechanics::getAffectedStacks(const Target if(dest.unitValue) { //FIXME: remove and return battle::Unit - stacks.insert(cb->battleGetStackByID(dest.unitValue->unitId(), false)); + stacks.insert(battle()->battleGetStackByID(dest.unitValue->unitId(), false)); } } @@ -199,7 +241,7 @@ std::vector BattleSpellMechanics::getAffectedStacks(const Target return res; } -void BattleSpellMechanics::cast(const PacketSender * server, vstd::RNG & rng, const Target & target) +void BattleSpellMechanics::cast(ServerCallback * server, const Target & target) { BattleSpellCast sc; @@ -219,22 +261,22 @@ void BattleSpellMechanics::cast(const PacketSender * server, vstd::RNG & rng, co const CGHeroInstance * otherHero = nullptr; { //check it there is opponent hero - const ui8 otherSide = cb->otherSide(casterSide); + const ui8 otherSide = battle()->otherSide(casterSide); - if(cb->battleHasHero(otherSide)) - otherHero = cb->battleGetFightingHero(otherSide); + if(battle()->battleHasHero(otherSide)) + otherHero = battle()->battleGetFightingHero(otherSide); } //calculate spell cost if(mode == Mode::HERO) { auto casterHero = dynamic_cast(caster); - spellCost = cb->battleGetSpellCost(owner, casterHero); + spellCost = battle()->battleGetSpellCost(owner, casterHero); if(nullptr != otherHero) //handle mana channel { int manaChannel = 0; - for(const CStack * stack : cb->battleGetAllStacks(true)) //TODO: shouldn't bonus system handle it somehow? + for(const CStack * stack : battle()->battleGetAllStacks(true)) //TODO: shouldn't bonus system handle it somehow? { if(stack->owner == otherHero->tempOwner) { @@ -251,7 +293,9 @@ void BattleSpellMechanics::cast(const PacketSender * server, vstd::RNG & rng, co sc.activeCast = true; } - beforeCast(sc, rng, target); + beforeCast(sc, *server->getRNG(), target); + + BattleLogMessage castDescription; switch (mode) { @@ -263,7 +307,7 @@ void BattleSpellMechanics::cast(const PacketSender * server, vstd::RNG & rng, co MetaString line; caster->getCastDescription(owner, affectedUnits, line); if(!line.message.empty()) - sc.battleLog.push_back(line); + castDescription.lines.push_back(line); } break; @@ -276,13 +320,13 @@ void BattleSpellMechanics::cast(const PacketSender * server, vstd::RNG & rng, co for(auto & unit : affectedUnits) sc.affectedCres.insert(unit->unitId()); - server->sendAndApply(&sc); + if(!castDescription.lines.empty()) + server->apply(&castDescription); - { - BattleStateProxy proxy(server); - for(auto & p : effectsToApply) - p.first->apply(&proxy, rng, this, p.second); - } + server->apply(&sc); + + for(auto & p : effectsToApply) + p.first->apply(server, this, p.second); // afterCast(); @@ -360,8 +404,9 @@ void BattleSpellMechanics::beforeCast(BattleSpellCast & sc, vstd::RNG & rng, con addCustomEffect(sc, unit, 78); } -void BattleSpellMechanics::cast(IBattleState * battleState, vstd::RNG & rng, const Target & target) +void BattleSpellMechanics::castEval(ServerCallback * server, const Target & target) { + affectedUnits.clear(); //TODO: evaluate caster updates (mana usage etc.) //TODO: evaluate random values @@ -369,27 +414,15 @@ void BattleSpellMechanics::cast(IBattleState * battleState, vstd::RNG & rng, con effectsToApply = effects->prepare(this, target, spellTarget); - std::set stacks = collectTargets(); + std::set unitTargets = collectTargets(); - for(const battle::Unit * one : stacks) - { - auto selector = std::bind(&BattleSpellMechanics::counteringSelector, this, _1); + auto selector = std::bind(&BattleSpellMechanics::counteringSelector, this, _1); - std::vector buffer; - auto bl = one->getBonuses(selector); + std::copy(std::begin(unitTargets), std::end(unitTargets), std::back_inserter(affectedUnits)); + doRemoveEffects(server, affectedUnits, selector); - for(auto item : *bl) - buffer.emplace_back(*item); - - if(!buffer.empty()) - battleState->removeUnitBonus(one->unitId(), buffer); - } - - { - BattleStateProxy proxy(battleState); - for(auto & p : effectsToApply) - p.first->apply(&proxy, rng, this, p.second); - } + for(auto & p : effectsToApply) + p.first->apply(server, this, p.second); } void BattleSpellMechanics::addCustomEffect(BattleSpellCast & sc, const battle::Unit * target, ui32 effect) @@ -419,7 +452,7 @@ std::set BattleSpellMechanics::collectTargets() const return result; } -void BattleSpellMechanics::doRemoveEffects(const PacketSender * server, const std::vector & targets, const CSelector & selector) +void BattleSpellMechanics::doRemoveEffects(ServerCallback * server, const std::vector & targets, const CSelector & selector) { SetStackEffect sse; @@ -436,7 +469,7 @@ void BattleSpellMechanics::doRemoveEffects(const PacketSender * server, const st } if(!sse.toRemove.empty()) - server->sendAndApply(&sse); + server->apply(&sse); } bool BattleSpellMechanics::counteringSelector(const Bonus * bonus) const @@ -583,7 +616,9 @@ std::vector BattleSpellMechanics::getPossibleDestinations(size_t in Target tmp = current; tmp.emplace_back(dest); - if(canBeCastAt(tmp)) + detail::ProblemImpl ingored; + + if(canBeCastAt(ingored, tmp)) ret.emplace_back(dest); } } @@ -631,5 +666,11 @@ std::vector BattleSpellMechanics::rangeInHexes(BattleHex centralHex, return ret; } +const Spell * BattleSpellMechanics::getSpell() const +{ + return owner; +} + + } diff --git a/lib/spells/BattleSpellMechanics.h b/lib/spells/BattleSpellMechanics.h index 7cf737797..8bd7544fb 100644 --- a/lib/spells/BattleSpellMechanics.h +++ b/lib/spells/BattleSpellMechanics.h @@ -25,13 +25,13 @@ public: BattleSpellMechanics(const IBattleCast * event, std::shared_ptr effects_, std::shared_ptr targetCondition_); virtual ~BattleSpellMechanics(); - void applyEffects(BattleStateProxy * battleState, vstd::RNG & rng, const Target & targets, bool indirect, bool ignoreImmunity) const override; + void applyEffects(ServerCallback * server, const Target & targets, bool indirect, bool ignoreImmunity) const override; bool canBeCast(Problem & problem) const override; - bool canBeCastAt(const Target & target) const override; + bool canBeCastAt(Problem & problem, const Target & target) const override; - void cast(const PacketSender * server, vstd::RNG & rng, const Target & target) override final; - void cast(IBattleState * battleState, vstd::RNG & rng, const Target & target) override final; + void cast(ServerCallback * server, const Target & target) override final; + void castEval(ServerCallback * server, const Target & target) override final; std::vector getAffectedStacks(const Target & target) const override final; @@ -42,6 +42,8 @@ public: std::vector rangeInHexes(BattleHex centralHex, bool * outDroppedHexes = nullptr) const override; + const Spell * getSpell() const override; + bool counteringSelector(const Bonus * bonus) const; private: @@ -58,7 +60,7 @@ private: std::set collectTargets() const; - static void doRemoveEffects(const PacketSender * server, const std::vector & targets, const CSelector & selector); + static void doRemoveEffects(ServerCallback * server, const std::vector & targets, const CSelector & selector); std::set spellRangeInHexes(BattleHex centralHex) const; diff --git a/lib/spells/BonusCaster.cpp b/lib/spells/BonusCaster.cpp index 05ca2db2a..e6f2a8959 100644 --- a/lib/spells/BonusCaster.cpp +++ b/lib/spells/BonusCaster.cpp @@ -11,6 +11,8 @@ #include "BonusCaster.h" +#include + #include "../NetPacksBase.h" #include "../HeroBonus.h" #include "../battle/Unit.h" @@ -48,7 +50,7 @@ void BonusCaster::getCastDescription(const Spell * spell, const std::vectoraddNameReplacement(text, true); } -void BonusCaster::spendMana(const PacketSender * server, const int spellCost) const +void BonusCaster::spendMana(ServerCallback * server, const int spellCost) const { logGlobal->error("Unexpected call to BonusCaster::spendMana"); } diff --git a/lib/spells/BonusCaster.h b/lib/spells/BonusCaster.h index 79997bcc3..c102c662b 100644 --- a/lib/spells/BonusCaster.h +++ b/lib/spells/BonusCaster.h @@ -25,7 +25,7 @@ public: void getCasterName(MetaString & text) const override; void getCastDescription(const Spell * spell, const std::vector & attacked, MetaString & text) const override; - void spendMana(const PacketSender * server, const int spellCost) const override; + void spendMana(ServerCallback * server, const int spellCost) const override; private: const Caster * actualCaster; diff --git a/lib/spells/CSpellHandler.cpp b/lib/spells/CSpellHandler.cpp index b68750d1c..c686b7d7a 100644 --- a/lib/spells/CSpellHandler.cpp +++ b/lib/spells/CSpellHandler.cpp @@ -15,6 +15,8 @@ #include "CSpellHandler.h" #include "Problem.h" +#include + #include "../CGeneralTextHandler.h" #include "../filesystem/Filesystem.h" @@ -101,12 +103,17 @@ CSpell::LevelInfo::~LevelInfo() ///CSpell CSpell::CSpell(): - id(SpellID::NONE), level(0), + id(SpellID::NONE), + level(0), power(0), - combatSpell(false), creatureAbility(false), + combat(false), + creatureAbility(false), positiveness(ESpellPositiveness::NEUTRAL), defaultProbability(0), - isRising(false), isDamage(false), isOffensive(false), isSpecial(true), + rising(false), + damage(false), + offensive(false), + special(true), targetType(spells::AimType::NO_TARGET), mechanics(), adventureMechanics() @@ -119,7 +126,7 @@ CSpell::~CSpell() } -bool CSpell::adventureCast(const SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const +bool CSpell::adventureCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const { assert(env); @@ -131,13 +138,12 @@ bool CSpell::adventureCast(const SpellCastEnvironment * env, const AdventureSpel return adventureMechanics->adventureCast(env, parameters); } - -const CSpell::LevelInfo & CSpell::getLevelInfo(const int level) const +const CSpell::LevelInfo & CSpell::getLevelInfo(const int32_t level) const { if(level < 0 || level >= GameConstants::SPELL_SCHOOL_LEVELS) { - logGlobal->error("CSpell::getLevelInfo invalid school level %d", level); - throw std::runtime_error("Invalid school level"); + logGlobal->error("CSpell::getLevelInfo: invalid school level %d", level); + return levels.at(0); } return levels.at(level); @@ -146,7 +152,7 @@ const CSpell::LevelInfo & CSpell::getLevelInfo(const int level) const int64_t CSpell::calculateDamage(const spells::Caster * caster) const { //check if spell really does damage - if not, return 0 - if(!isDamageSpell()) + if(!isDamage()) return 0; auto rawDamage = calculateRawEffectValue(caster->getEffectLevel(this), caster->getEffectPower(this), 1); @@ -165,65 +171,9 @@ bool CSpell::canBeCast(spells::Problem & problem, const CBattleInfoCallback * cb spells::BattleCast event(cb, caster, mode, this); auto mechanics = battleMechanics(&event); - ESpellCastProblem::ESpellCastProblem genProblem = cb->battleCanCastSpell(caster, mode); - if(genProblem != ESpellCastProblem::OK) - return mechanics->adaptProblem(genProblem, problem); - - switch(mode) - { - case spells::Mode::HERO: - { - const CGHeroInstance * castingHero = dynamic_cast(caster);//todo: unify hero|creature spell cost - if(!castingHero) - { - logGlobal->debug("CSpell::canBeCast: invalid caster"); - genProblem = ESpellCastProblem::NO_HERO_TO_CAST_SPELL; - } - else if(!castingHero->getArt(ArtifactPosition::SPELLBOOK)) - genProblem = ESpellCastProblem::NO_SPELLBOOK; - else if(!castingHero->canCastThisSpell(this)) - genProblem = ESpellCastProblem::HERO_DOESNT_KNOW_SPELL; - else if(castingHero->mana < (si32)cb->battleGetSpellCost(this, castingHero)) //not enough mana - genProblem = ESpellCastProblem::NOT_ENOUGH_MANA; - } - break; - } - - if(genProblem != ESpellCastProblem::OK) - return mechanics->adaptProblem(genProblem, problem); - - if(!isCombatSpell()) - return mechanics->adaptProblem(ESpellCastProblem::ADVMAP_SPELL_INSTEAD_OF_BATTLE_SPELL, problem); - - const PlayerColor player = caster->getOwner(); - const auto side = cb->playerToSide(player); - - if(!side) - return mechanics->adaptProblem(ESpellCastProblem::INVALID, problem); - - //effect like Recanter's Cloak. Blocks also passive casting. - //TODO: check creature abilities to block - //TODO: check any possible caster - if(cb->battleMaxSpellLevel(side.get()) < level || cb->battleMinSpellLevel(side.get()) > level) - return mechanics->adaptProblem(ESpellCastProblem::SPELL_LEVEL_LIMIT_EXCEEDED, problem); - return mechanics->canBeCast(problem); } -std::vector CSpell::rangeInHexes(const CBattleInfoCallback * cb, spells::Mode mode, const spells::Caster * caster, BattleHex centralHex) const -{ - spells::BattleCast event(cb, caster, mode, this); - return battleMechanics(&event)->rangeInHexes(centralHex); -} - -std::vector CSpell::getAffectedStacks(const CBattleInfoCallback * cb, spells::Mode mode, const spells::Caster * caster, int spellLvl, const spells::Target & target) const -{ - //TODO: remove and add new method to BattleCast - spells::BattleCast event(cb, caster, mode, this); - event.setSpellLevel(spellLvl); - return battleMechanics(&event)->getAffectedStacks(target); -} - spells::AimType CSpell::getTargetType() const { return targetType; @@ -245,24 +195,44 @@ void CSpell::forEachSchool(const std::function & lst, const int level, const bool cu } } -bool CSpell::canBeCastAt(const CBattleInfoCallback * cb, spells::Mode mode, const spells::Caster * caster, BattleHex destination) const -{ - if(canBeCast(cb, mode, caster)) - { - spells::BattleCast event(cb, caster, mode, this); - spells::Target tmp; - tmp.emplace_back(destination); - return battleMechanics(&event)->canBeCastAt(tmp); - } - else - { - return false; - } -} - -bool CSpell::canBeCastAt(const CBattleInfoCallback * cb, spells::Mode mode, const spells::Caster * caster, const spells::Target & target) const -{ - if(canBeCast(cb, mode, caster)) - { - spells::BattleCast event(cb, caster, mode, this); - return battleMechanics(&event)->canBeCastAt(target); - } - else - { - return false; - } -} - int64_t CSpell::adjustRawDamage(const spells::Caster * caster, const battle::Unit * affectedCreature, int64_t rawDamage) const { auto ret = rawDamage; @@ -459,23 +426,23 @@ int64_t CSpell::adjustRawDamage(const spells::Caster * caster, const battle::Uni int64_t CSpell::calculateRawEffectValue(int32_t effectLevel, int32_t basePowerMultiplier, int32_t levelPowerMultiplier) const { - return basePowerMultiplier * power + levelPowerMultiplier * getPower(effectLevel); + return (int64_t)basePowerMultiplier * getBasePower() + levelPowerMultiplier * getLevelPower(effectLevel); } void CSpell::setIsOffensive(const bool val) { - isOffensive = val; + offensive = val; if(val) { positiveness = CSpell::NEGATIVE; - isDamage = true; + damage = true; } } void CSpell::setIsRising(const bool val) { - isRising = val; + rising = val; if(val) { @@ -534,6 +501,24 @@ std::unique_ptr CSpell::battleMechanics(const spells::IBattle return mechanics->create(event); } +void CSpell::registerIcons(const IconRegistar & cb) const +{ + cb(getIndex(), "SPELLS", iconBook); + cb(getIndex()+1, "SPELLINT", iconEffect); + cb(getIndex(), "SPELLBON", iconScenarioBonus); + cb(getIndex(), "SPELLSCR", iconScroll); +} + +void CSpell::updateFrom(const JsonNode & data) +{ + //todo:CSpell::updateFrom +} + +void CSpell::serializeJson(JsonSerializeFormat & handler) +{ + +} + ///CSpell::AnimationInfo CSpell::AnimationItem::AnimationItem() :resourceName(""),verticalPosition(VerticalPosition::TOP),pause(0) @@ -693,7 +678,7 @@ std::vector CSpellHandler::loadLegacyData(size_t dataSize) skip(3); read(true,true);//read creature abilities - //TODO: maybe move to config + //TODO: maybe move to config //clone Acid Breath attributes for Acid Breath damage effect JsonNode temp = legacyData[SpellID::ACID_BREATH_DEFENSE]; temp["index"].Integer() = SpellID::ACID_BREATH_DAMAGE; @@ -710,7 +695,7 @@ const std::vector & CSpellHandler::getTypeNames() const return typeNames; } -CSpell * CSpellHandler::loadFromJson(const JsonNode & json, const std::string & identifier, size_t index) +CSpell * CSpellHandler::loadFromJson(const std::string & scope, const JsonNode & json, const std::string & identifier, size_t index) { using namespace SpellConfig; @@ -725,12 +710,12 @@ CSpell * CSpellHandler::loadFromJson(const JsonNode & json, const std::string & if(type == "ability") { spell->creatureAbility = true; - spell->combatSpell = true; + spell->combat = true; } else { spell->creatureAbility = false; - spell->combatSpell = type == "combat"; + spell->combat = type == "combat"; } spell->name = json["name"].String(); @@ -753,7 +738,7 @@ CSpell * CSpellHandler::loadFromJson(const JsonNode & json, const std::string & { const int chance = static_cast(node.second.Integer()); - VLC->modh->identifiers.requestIdentifier(node.second.meta, "faction",node.first, [=](si32 factionID) + VLC->modh->identifiers.requestIdentifier(node.second.meta, "faction", node.first, [=](si32 factionID) { spell->probabilities[factionID] = chance; }); @@ -788,7 +773,7 @@ CSpell * CSpellHandler::loadFromJson(const JsonNode & json, const std::string & //by default all flags are set to false in constructor - spell->isDamage = flags["damage"].Bool(); //do this before "offensive" + spell->damage = flags["damage"].Bool(); //do this before "offensive" if(flags["offensive"].Bool()) { @@ -800,7 +785,7 @@ CSpell * CSpellHandler::loadFromJson(const JsonNode & json, const std::string & spell->setIsRising(true); } - const bool implicitPositiveness = spell->isOffensive || spell->isRising; //(!) "damage" does not mean NEGATIVE --AVS + const bool implicitPositiveness = spell->offensive || spell->rising; //(!) "damage" does not mean NEGATIVE --AVS if(flags["indifferent"].Bool()) { @@ -820,7 +805,7 @@ CSpell * CSpellHandler::loadFromJson(const JsonNode & json, const std::string & logMod->error("Spell %s: no positiveness specified, assumed NEUTRAL.", spell->name); } - spell->isSpecial = flags["special"].Bool(); + spell->special = flags["special"].Bool(); auto findBonus = [&](std::string name, std::vector & vec) { @@ -988,7 +973,7 @@ CSpell * CSpellHandler::loadFromJson(const JsonNode & json, const std::string & { levelObject.battleEffects = levelNode["battleEffects"]; - if(!levelObject.cumulativeEffects.empty() || !levelObject.effects.empty() || spell->isOffensiveSpell()) + if(!levelObject.cumulativeEffects.empty() || !levelObject.effects.empty() || spell->isOffensive()) logGlobal->error("Mixing %s special effects with old format effects gives unpredictable result", spell->name); } } @@ -1028,7 +1013,7 @@ std::vector CSpellHandler::getDefaultAllowed() const for(const CSpell * s : objects) { - allowedSpells.push_back( !(s->isSpecialSpell() || s->isCreatureAbility())); + allowedSpells.push_back( !(s->isSpecial() || s->isCreatureAbility())); } return allowedSpells; @@ -1036,7 +1021,7 @@ std::vector CSpellHandler::getDefaultAllowed() const void CSpellHandler::update780() { - static_assert(MINIMAL_SERIALIZATION_VERSION < 780, "No longer needed CSpellHandler::update780"); + static_assert(MINIMAL_SERIALIZATION_VERSION < 780, "No longer needed CSpellHandler::update780"); auto spellsContent = (*VLC->modh->content)["spells"]; diff --git a/lib/spells/CSpellHandler.h b/lib/spells/CSpellHandler.h index f86062721..a25d4de2c 100644 --- a/lib/spells/CSpellHandler.h +++ b/lib/spells/CSpellHandler.h @@ -9,7 +9,10 @@ */ #pragma once -#include "Magic.h" + +#include +#include +#include #include "../JsonNode.h" #include "../IHandlerBase.h" #include "../ConstTransitivePtr.h" @@ -18,32 +21,24 @@ #include "../battle/BattleHex.h" #include "../HeroBonus.h" -namespace spells -{ - class ISpellMechanicsFactory; - class IBattleCast; -} - -class CGObjectInstance; class CSpell; class IAdventureSpellMechanics; -class CLegacyConfigParser; -class CGHeroInstance; -class CStack; class CBattleInfoCallback; -class BattleInfo; -struct CPackForClient; -struct BattleSpellCast; -class CGameInfoCallback; -class CRandomGenerator; -class CMap; class AdventureSpellCastParameters; class SpellCastEnvironment; +class JsonSerializeFormat; +namespace test +{ + class CSpellTest; +} namespace spells { +class ISpellMechanicsFactory; +class IBattleCast; + struct SchoolInfo { ESpellSchool id; //backlink @@ -56,7 +51,6 @@ struct SchoolInfo } - enum class VerticalPosition : ui8{TOP, CENTER, BOTTOM}; class DLL_LINKAGE CSpell : public spells::Spell @@ -199,7 +193,7 @@ public: * \return Spell level info structure * */ - const CSpell::LevelInfo & getLevelInfo(const int level) const; + const CSpell::LevelInfo & getLevelInfo(const int32_t level) const; public: enum ESpellPositiveness { @@ -216,7 +210,7 @@ public: bool clearAffected; bool clearTarget; - TargetInfo(const CSpell * spell, const int level, spells::Mode mode); + TargetInfo(const CSpell * spell, const int32_t level, spells::Mode mode); }; using BTVector = std::vector; @@ -233,7 +227,7 @@ public: std::map probabilities; //% chance to gain for castles - bool combatSpell; //is this spell combat (true) or adventure (false) + bool combat; //is this spell combat (true) or adventure (false) bool creatureAbility; //if true, only creatures can use this spell si8 positiveness; //1 if spell is positive for influenced stacks, 0 if it is indifferent, -1 if it's negative @@ -244,61 +238,66 @@ public: CSpell(); ~CSpell(); - std::vector rangeInHexes(const CBattleInfoCallback * cb, spells::Mode mode, const spells::Caster * caster, BattleHex centralHex) const; - - spells::AimType getTargetType() const; - - bool isCombatSpell() const; - bool isAdventureSpell() const; - bool isCreatureAbility() const; - - bool isPositive() const; - bool isNegative() const; - bool isNeutral() const; - - boost::logic::tribool getPositiveness() const; - - bool isDamageSpell() const; - bool isRisingSpell() const; - bool isOffensiveSpell() const; - - bool isSpecialSpell() const; - - bool hasEffects() const; - void getEffects(std::vector & lst, const int level, const bool cumulative, const si32 duration, boost::optional maxDuration = boost::none) const; - - bool hasBattleEffects() const; - ///calculate spell damage on stack taking caster`s secondary skills and affectedCreature`s bonuses into account - int64_t calculateDamage(const spells::Caster * caster) const; - - ///selects from allStacks actually affected stacks - std::vector getAffectedStacks(const CBattleInfoCallback * cb, spells::Mode mode, const spells::Caster * caster, int spellLvl, const spells::Target & target) const; - - si32 getCost(const int skillLevel) const; - - /** - * Returns spell level power, base power ignored - */ - si32 getPower(const int skillLevel) const; - - si32 getProbability(const TFaction factionId) const; + int64_t calculateDamage(const spells::Caster * caster) const override; /** * Calls cb for each school this spell belongs to * * Set stop to true to abort looping */ - void forEachSchool(const std::function & cb) const override; + void forEachSchool(const std::function & cb) const override; + + spells::AimType getTargetType() const; + + bool hasEffects() const; + void getEffects(std::vector & lst, const int level, const bool cumulative, const si32 duration, boost::optional maxDuration = boost::none) const; + + bool hasBattleEffects() const; + + int32_t getCost(const int32_t skillLevel) const override; + + si32 getProbability(const TFaction factionId) const; + + int32_t getBasePower() const override; + int32_t getLevelPower(const int32_t skillLevel) const override; int32_t getIndex() const override; + int32_t getIconIndex() const override; + const std::string & getName() const override; + const std::string & getJsonKey() const override; + SpellID getId() const override; + int32_t getLevel() const override; - /** - * Returns resource name of icon for SPELL_IMMUNITY bonus - */ - const std::string& getIconImmune() const; + const std::string & getLevelDescription(const int32_t skillLevel) const override; - const std::string& getCastSound() const; + boost::logic::tribool getPositiveness() const override; + + bool isPositive() const override; + bool isNegative() const override; + bool isNeutral() const override; + + bool isDamage() const override; + bool isOffensive() const override; + + bool isSpecial() const override; + + bool isAdventure() const override; + bool isCombat() const override; + bool isCreatureAbility() const override; + + void registerIcons(const IconRegistar & cb) const override; + + const std::string & getIconImmune() const; ///< Returns resource name of icon for SPELL_IMMUNITY bonus + const std::string & getIconBook() const; + const std::string & getIconEffect() const; + const std::string & getIconScenarioBonus() const; + const std::string & getIconScroll() const; + + const std::string & getCastSound() const override; + + void updateFrom(const JsonNode & data); + void serializeJson(JsonSerializeFormat & handler); template void serialize(Handler & h, const int version) { @@ -309,13 +308,13 @@ public: h & power; h & probabilities; h & attributes; - h & combatSpell; + h & combat; h & creatureAbility; h & positiveness; h & counteredSpells; - h & isRising; - h & isDamage; - h & isOffensive; + h & rising; + h & damage; + h & offensive; h & targetType; if(version >= 780) @@ -340,7 +339,7 @@ public: h & iconImmune; h & defaultProbability; - h & isSpecial; + h & special; h & castSound; h & iconBook; h & iconEffect; @@ -362,6 +361,7 @@ public: } friend class CSpellHandler; friend class Graphics; + friend class test::CSpellTest; public: ///internal interface (for callbacks) @@ -369,14 +369,11 @@ public: bool canBeCast(const CBattleInfoCallback * cb, spells::Mode mode, const spells::Caster * caster) const; bool canBeCast(spells::Problem & problem, const CBattleInfoCallback * cb, spells::Mode mode, const spells::Caster * caster) const; - ///checks for creature immunity / anything that prevent casting *at given hex* - bool canBeCastAt(const CBattleInfoCallback * cb, spells::Mode mode, const spells::Caster * caster, BattleHex destination) const; //DEPREACTED - bool canBeCastAt(const CBattleInfoCallback * cb, spells::Mode mode, const spells::Caster * caster, const spells::Target & target) const; public: ///Server logic. Has write access to GameState via packets. ///May be executed on client side by (future) non-cheat-proof scripts. - bool adventureCast(const SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const; + bool adventureCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const; public://internal, for use only by Mechanics classes ///applies caster`s secondary skills and affectedCreature`s to raw damage @@ -394,13 +391,14 @@ private: //call this after load or deserialization. cant be done in constructor. void setupMechanics(); + private: si32 defaultProbability; - bool isRising; - bool isDamage; - bool isOffensive; - bool isSpecial; + bool rising; + bool damage; + bool offensive; + bool special; std::string attributes; //reference only attributes //todo: remove or include in configuration format, currently unused @@ -424,7 +422,7 @@ private: bool DLL_LINKAGE isInScreenRange(const int3 ¢er, const int3 &pos); //for spells like Dimension Door -class DLL_LINKAGE CSpellHandler: public CHandlerBase +class DLL_LINKAGE CSpellHandler: public CHandlerBase { public: CSpellHandler(); @@ -441,8 +439,6 @@ public: */ std::vector getDefaultAllowed() const override; - const std::vector & getTypeNames() const override; - template void serialize(Handler & h, const int version) { h & objects; @@ -458,7 +454,8 @@ public: } protected: - CSpell * loadFromJson(const JsonNode & json, const std::string & identifier, size_t index) override; + const std::vector & getTypeNames() const override; + CSpell * loadFromJson(const std::string & scope, const JsonNode & json, const std::string & identifier, size_t index) override; private: void update780(); }; diff --git a/lib/spells/ISpellMechanics.cpp b/lib/spells/ISpellMechanics.cpp index 4c26527b5..e5b5fdf45 100644 --- a/lib/spells/ISpellMechanics.cpp +++ b/lib/spells/ISpellMechanics.cpp @@ -30,12 +30,15 @@ #include "BattleSpellMechanics.h" #include "effects/Effects.h" +#include "effects/Registry.h" #include "effects/Damage.h" #include "effects/Timed.h" #include "CSpellHandler.h" +#include "../NetPacks.h" #include "../CHeroHandler.h"//todo: remove +#include "../IGameCallback.h"//todo: remove namespace spells { @@ -70,10 +73,10 @@ protected: void loadEffects(const JsonNode & config, const int level) { JsonDeserializer deser(nullptr, config); - effects->serializeJson(deser, level); + effects->serializeJson(VLC->spellEffects(), deser, level); } private: - std::shared_ptr targetCondition; + std::shared_ptr targetCondition; }; class ConfigurableMechanicsFactory : public CustomMechanicsFactory @@ -100,7 +103,7 @@ public: const CSpell::LevelInfo & levelInfo = s->getLevelInfo(level); assert(levelInfo.battleEffects.isNull()); - if(s->isOffensiveSpell()) + if(s->isOffensive()) { //default constructed object should be enough effects->add("directDamage", std::make_shared(), level); @@ -130,32 +133,10 @@ public: } }; -BattleStateProxy::BattleStateProxy(const PacketSender * server_) - : server(server_), - battleState(nullptr), - describe(true) -{ -} -BattleStateProxy::BattleStateProxy(IBattleState * battleState_) - : server(nullptr), - battleState(battleState_), - describe(false) -{ -} - -void BattleStateProxy::complain(const std::string & problem) const -{ - if(server) - server->complain(problem); - else - logGlobal->error(problem); -} - - -BattleCast::BattleCast(const CBattleInfoCallback * cb, const Caster * caster_, const Mode mode_, const CSpell * spell_) +BattleCast::BattleCast(const CBattleInfoCallback * cb_, const Caster * caster_, const Mode mode_, const CSpell * spell_) : spell(spell_), - cb(cb), + cb(cb_), caster(caster_), mode(mode_), magicSkillLevel(), @@ -165,12 +146,13 @@ BattleCast::BattleCast(const CBattleInfoCallback * cb, const Caster * caster_, c smart(boost::logic::indeterminate), massive(boost::logic::indeterminate) { - + gameCb = IObjectInterface::cb; //FIXME: pass player callback (problem is that BattleAI do not have one) } BattleCast::BattleCast(const BattleCast & orig, const Caster * caster_) : spell(orig.spell), cb(orig.cb), + gameCb(orig.gameCb), caster(caster_), mode(Mode::MAGIC_MIRROR), magicSkillLevel(orig.magicSkillLevel), @@ -204,6 +186,11 @@ const CBattleInfoCallback * BattleCast::getBattle() const return cb; } +const IGameInfoCallback * BattleCast::getGame() const +{ + return gameCb; +} + BattleCast::OptionalValue BattleCast::getSpellLevel() const { return magicSkillLevel; @@ -254,32 +241,18 @@ void BattleCast::setEffectValue(BattleCast::Value64 value) effectValue = boost::make_optional(value); } -void BattleCast::aimToHex(const BattleHex & destination) -{ - target.push_back(Destination(destination)); -} - -void BattleCast::aimToUnit(const battle::Unit * destination) -{ - if(nullptr == destination) - logGlobal->error("BattleCast::aimToUnit: invalid unit."); - else - target.push_back(Destination(destination)); -} - -void BattleCast::applyEffects(const SpellCastEnvironment * env, bool indirect, bool ignoreImmunity) const +void BattleCast::applyEffects(ServerCallback * server, Target target, bool indirect, bool ignoreImmunity) const { auto m = spell->battleMechanics(this); - BattleStateProxy proxy(env); - - m->applyEffects(&proxy, env->getRandomGenerator(), target, indirect, ignoreImmunity); + m->applyEffects(server, target, indirect, ignoreImmunity); } -void BattleCast::cast(const SpellCastEnvironment * env) +void BattleCast::cast(ServerCallback * server, Target target) { if(target.empty()) - aimToHex(BattleHex::INVALID); + target.emplace_back(); + auto m = spell->battleMechanics(this); const battle::Unit * mainTarget = nullptr; @@ -294,9 +267,9 @@ void BattleCast::cast(const SpellCastEnvironment * env) } bool tryMagicMirror = (mainTarget != nullptr) && (mode == Mode::HERO || mode == Mode::CREATURE_ACTIVE);//TODO: recheck - tryMagicMirror = tryMagicMirror && (mainTarget->unitOwner() != caster->getOwner()) && !spell->isPositive();//TODO: recheck + tryMagicMirror = tryMagicMirror && (mainTarget->unitOwner() != caster->getCasterOwner()) && !spell->isPositive();//TODO: recheck - m->cast(env, env->getRandomGenerator(), target); + m->cast(server, target); //Magic Mirror effect if(tryMagicMirror) @@ -304,7 +277,7 @@ void BattleCast::cast(const SpellCastEnvironment * env) const std::string magicMirrorCacheStr = "type_MAGIC_MIRROR"; static const auto magicMirrorSelector = Selector::type()(Bonus::MAGIC_MIRROR); - auto rangeGen = env->getRandomGenerator().getInt64Range(0, 99); + auto rangeGen = server->getRNG()->getInt64Range(0, 99); const int mirrorChance = mainTarget->valOfBonuses(magicMirrorSelector, magicMirrorCacheStr); @@ -313,40 +286,42 @@ void BattleCast::cast(const SpellCastEnvironment * env) auto mirrorTargets = cb->battleGetUnitsIf([this](const battle::Unit * unit) { //Get all caster stacks. Magic mirror can reflect to immune creature (with no effect) - return unit->unitOwner() == caster->getOwner() && unit->isValidTarget(true); + return unit->unitOwner() == caster->getCasterOwner() && unit->isValidTarget(true); }); if(!mirrorTargets.empty()) { - auto mirrorTarget = (*RandomGeneratorUtil::nextItem(mirrorTargets, env->getRandomGenerator())); + auto mirrorDesination = (*RandomGeneratorUtil::nextItem(mirrorTargets, *server->getRNG())); + + Target mirrorTarget; + mirrorTarget.emplace_back(mirrorDesination); BattleCast mirror(*this, mainTarget); - mirror.aimToUnit(mirrorTarget); - mirror.cast(env); + mirror.cast(server, mirrorTarget); } } } } -void BattleCast::cast(IBattleState * battleState, vstd::RNG & rng) +void BattleCast::castEval(ServerCallback * server, Target target) { //TODO: make equivalent to normal cast if(target.empty()) - aimToHex(BattleHex::INVALID); + target.emplace_back(); auto m = spell->battleMechanics(this); //TODO: reflection //TODO: random effects evaluation - m->cast(battleState, rng, target); + m->castEval(server, target); } -bool BattleCast::castIfPossible(const SpellCastEnvironment * env) +bool BattleCast::castIfPossible(ServerCallback * server, Target target) { if(spell->canBeCast(cb, mode, caster)) { - cast(env); + cast(server, target); return true; } return false; @@ -433,8 +408,7 @@ std::unique_ptr ISpellMechanicsFactory::get(const CSpell ///Mechanics Mechanics::Mechanics() - : cb(nullptr), - caster(nullptr), + : caster(nullptr), casterSide(0) { @@ -450,10 +424,12 @@ BaseMechanics::BaseMechanics(const IBattleCast * event) massive(event->isMassive()) { cb = event->getBattle(); + gameCb = event->getGame(); + caster = event->getCaster(); //FIXME: do not crash on invalid side - casterSide = cb->playerToSide(caster->getOwner()).get(); + casterSide = cb->playerToSide(caster->getCasterOwner()).get(); { auto value = event->getSpellLevel(); @@ -585,17 +561,17 @@ int32_t BaseMechanics::getSpellIndex() const SpellID BaseMechanics::getSpellId() const { - return owner->id; + return owner->getId(); } std::string BaseMechanics::getSpellName() const { - return owner->name; + return owner->getName(); } int32_t BaseMechanics::getSpellLevel() const { - return owner->level; + return owner->getLevel(); } bool BaseMechanics::isSmart() const @@ -650,7 +626,7 @@ int64_t BaseMechanics::adjustEffectValue(const battle::Unit * target) const return owner->adjustRawDamage(caster, target, getEffectValue()); } -int64_t BaseMechanics::applySpellBonus(int64_t value, const battle::Unit* target) const +int64_t BaseMechanics::applySpellBonus(int64_t value, const battle::Unit * target) const { return caster->getSpellBonus(owner, value, target); } @@ -684,7 +660,7 @@ bool BaseMechanics::ownerMatches(const battle::Unit * unit) const bool BaseMechanics::ownerMatches(const battle::Unit * unit, const boost::logic::tribool positivness) const { - return cb->battleMatchOwner(caster->getOwner(), unit, positivness); + return cb->battleMatchOwner(caster->getCasterOwner(), unit, positivness); } IBattleCast::Value BaseMechanics::getEffectLevel() const @@ -714,7 +690,7 @@ IBattleCast::Value64 BaseMechanics::getEffectValue() const PlayerColor BaseMechanics::getCasterColor() const { - return caster->getOwner(); + return caster->getCasterOwner(); } std::vector BaseMechanics::getTargetTypes() const @@ -737,6 +713,32 @@ std::vector BaseMechanics::getTargetTypes() const return ret; } +const CreatureService * BaseMechanics::creatures() const +{ + return VLC->creatures(); //todo: redirect +} + +const scripting::Service * BaseMechanics::scripts() const +{ + return VLC->scripts(); //todo: redirect +} + +const Service * BaseMechanics::spells() const +{ + return VLC->spells(); //todo: redirect +} + +const IGameInfoCallback * BaseMechanics::game() const +{ + return gameCb; +} + +const CBattleInfoCallback * BaseMechanics::battle() const +{ + return cb; +} + + } //namespace spells ///IAdventureSpellMechanics diff --git a/lib/spells/ISpellMechanics.h b/lib/spells/ISpellMechanics.h index c0b698a6d..d90fe5e12 100644 --- a/lib/spells/ISpellMechanics.h +++ b/lib/spells/ISpellMechanics.h @@ -10,7 +10,9 @@ #pragma once -#include "Magic.h" +#include +#include + #include "../battle/Destination.h" #include "../int3.h" #include "../GameConstants.h" @@ -22,6 +24,7 @@ class CRandomGenerator; class CMap; class CGameInfoCallback; class CBattleInfoCallback; +class IGameInfoCallback; class JsonNode; class CStack; class CGObjectInstance; @@ -32,48 +35,29 @@ namespace vstd class RNG; } +namespace scripting +{ + class Service; +} + + ///callback to be provided by server -class DLL_LINKAGE SpellCastEnvironment : public spells::PacketSender +class DLL_LINKAGE SpellCastEnvironment : public ServerCallback { public: virtual ~SpellCastEnvironment(){}; - virtual CRandomGenerator & getRandomGenerator() const = 0; - virtual const CMap * getMap() const = 0; virtual const CGameInfoCallback * getCb() const = 0; - virtual bool moveHero(ObjectInstanceID hid, int3 dst, bool teleporting) const = 0; //TODO: remove + virtual bool moveHero(ObjectInstanceID hid, int3 dst, bool teleporting) = 0; //TODO: remove - virtual void genericQuery(Query * request, PlayerColor color, std::function callback) const = 0;//TODO: type safety on query, use generic query packet when implemented + virtual void genericQuery(Query * request, PlayerColor color, std::function callback) = 0;//TODO: type safety on query, use generic query packet when implemented }; namespace spells { -class DLL_LINKAGE BattleStateProxy -{ -public: - const bool describe; - - BattleStateProxy(const PacketSender * server_); - BattleStateProxy(IBattleState * battleState_); - - template - void apply(P * pack) - { - if(server) - server->sendAndApply(pack); - else - pack->applyBattle(battleState); - } - - void complain(const std::string & problem) const; -private: - const PacketSender * server; - IBattleState * battleState; -}; - class DLL_LINKAGE IBattleCast { public: @@ -87,6 +71,7 @@ public: virtual Mode getMode() const = 0; virtual const Caster * getCaster() const = 0; virtual const CBattleInfoCallback * getBattle() const = 0; + virtual const IGameInfoCallback * getGame() const = 0; virtual OptionalValue getSpellLevel() const = 0; @@ -103,13 +88,11 @@ public: class DLL_LINKAGE BattleCast : public IBattleCast { public: - Target target; - boost::logic::tribool smart; boost::logic::tribool massive; //normal constructor - BattleCast(const CBattleInfoCallback * cb, const Caster * caster_, const Mode mode_, const CSpell * spell_); + BattleCast(const CBattleInfoCallback * cb_, const Caster * caster_, const Mode mode_, const CSpell * spell_); //magic mirror constructor BattleCast(const BattleCast & orig, const Caster * caster_); @@ -121,6 +104,7 @@ public: Mode getMode() const override; const Caster * getCaster() const override; const CBattleInfoCallback * getBattle() const override; + const IGameInfoCallback * getGame() const override; OptionalValue getSpellLevel() const override; @@ -139,20 +123,17 @@ public: void setEffectValue(Value64 value); - void aimToHex(const BattleHex & destination); - void aimToUnit(const battle::Unit * destination); - ///only apply effects to specified targets - void applyEffects(const SpellCastEnvironment * env, bool indirect = false, bool ignoreImmunity = false) const; + void applyEffects(ServerCallback * server, Target target, bool indirect = false, bool ignoreImmunity = false) const; ///normal cast - void cast(const SpellCastEnvironment * env); + void cast(ServerCallback * server, Target target); ///cast evaluation - void cast(IBattleState * battleState, vstd::RNG & rng); + void castEval(ServerCallback * server, Target target); ///cast with silent check for permitted cast - bool castIfPossible(const SpellCastEnvironment * env); + bool castIfPossible(ServerCallback * server, Target target); std::vector findPotentialTargets() const; @@ -171,6 +152,7 @@ private: Mode mode; const CSpell * spell; const CBattleInfoCallback * cb; + const IGameInfoCallback * gameCb; const Caster * caster; }; @@ -192,7 +174,6 @@ protected: class DLL_LINKAGE Mechanics { public: - Mechanics(); virtual ~Mechanics(); virtual bool adaptProblem(ESpellCastProblem::ESpellCastProblem source, Problem & target) const = 0; @@ -202,19 +183,21 @@ public: virtual std::vector getAffectedStacks(const Target & target) const = 0; virtual bool canBeCast(Problem & problem) const = 0; - virtual bool canBeCastAt(const Target & target) const = 0; + virtual bool canBeCastAt(Problem & problem, const Target & target) const = 0; - virtual void applyEffects(BattleStateProxy * battleState, vstd::RNG & rng, const Target & targets, bool indirect, bool ignoreImmunity) const = 0; + virtual void applyEffects(ServerCallback * server, const Target & targets, bool indirect, bool ignoreImmunity) const = 0; - virtual void cast(const PacketSender * server, vstd::RNG & rng, const Target & target) = 0; + virtual void cast(ServerCallback * server, const Target & target) = 0; - virtual void cast(IBattleState * battleState, vstd::RNG & rng, const Target & target) = 0; + virtual void castEval(ServerCallback * server, const Target & target) = 0; virtual bool isReceptive(const battle::Unit * target) const = 0; - virtual std::vector getTargetTypes() const = 0; + virtual std::vector getTargetTypes() const = 0; - virtual std::vector getPossibleDestinations(size_t index, AimType aimType, const Target & current) const = 0; + virtual std::vector getPossibleDestinations(size_t index, AimType aimType, const Target & current) const = 0; + + virtual const Spell * getSpell() const = 0; //Cast event facade @@ -253,16 +236,25 @@ public: virtual bool ownerMatches(const battle::Unit * unit) const = 0; virtual bool ownerMatches(const battle::Unit * unit, const boost::logic::tribool positivness) const = 0; - const CBattleInfoCallback * cb; + //Global environment facade + virtual const CreatureService * creatures() const = 0; + virtual const scripting::Service * scripts() const = 0; + virtual const Service * spells() const = 0; + + virtual const IGameInfoCallback * game() const = 0; + virtual const CBattleInfoCallback * battle() const = 0; + const Caster * caster; ui8 casterSide; + +protected: + Mechanics(); }; class DLL_LINKAGE BaseMechanics : public Mechanics { public: - BaseMechanics(const IBattleCast * event); virtual ~BaseMechanics(); bool adaptProblem(ESpellCastProblem::ESpellCastProblem source, Problem & target) const override; @@ -303,10 +295,19 @@ public: std::vector getTargetTypes() const override; + const CreatureService * creatures() const override; + const scripting::Service * scripts() const override; + const Service * spells() const override; + + const IGameInfoCallback * game() const override; + const CBattleInfoCallback * battle() const override; + protected: const CSpell * owner; Mode mode; + BaseMechanics(const IBattleCast * event); + private: IBattleCast::Value rangeLevel; IBattleCast::Value effectLevel; @@ -321,6 +322,9 @@ private: boost::logic::tribool smart; boost::logic::tribool massive; + + const IGameInfoCallback * gameCb; + const CBattleInfoCallback * cb; }; class DLL_LINKAGE IReceptiveCheck @@ -346,7 +350,7 @@ public: IAdventureSpellMechanics(const CSpell * s); virtual ~IAdventureSpellMechanics() = default; - virtual bool adventureCast(const SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const = 0; + virtual bool adventureCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const = 0; static std::unique_ptr createMechanics(const CSpell * s); protected: diff --git a/lib/spells/Magic.h b/lib/spells/Magic.h deleted file mode 100644 index 485a656a7..000000000 --- a/lib/spells/Magic.h +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Magic.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 - -/** - * High-level interface for spells subsystem - */ - - -class CSpell; -class PlayerColor; -struct MetaString; -struct CPackForClient; - -namespace battle -{ - class Unit; - class Destination; -} - -namespace spells -{ - -class Mechanics; -class BattleCast; -using Destination = ::battle::Destination; - -using Target = std::vector; - -struct SchoolInfo; - -enum class Mode -{ - //ACTIVE, //todo: use - HERO, //deprecated - MAGIC_MIRROR, - CREATURE_ACTIVE, //deprecated - ENCHANTER, - SPELL_LIKE_ATTACK, - PASSIVE//f.e. opening battle spells -}; - -enum class AimType -{ - NO_TARGET, - CREATURE, - OBSTACLE, - LOCATION -}; - -class DLL_LINKAGE PacketSender -{ -public: - virtual ~PacketSender(){}; - virtual void sendAndApply(CPackForClient * pack) const = 0; - virtual void complain(const std::string & problem) const = 0; -}; - -class DLL_LINKAGE Problem -{ -public: - typedef int Severity; - - enum ESeverity - { - LOWEST = std::numeric_limits::min(), - NORMAL = 0, - CRITICAL = std::numeric_limits::max() - }; - - virtual ~Problem() = default; - - virtual void add(MetaString && description, Severity severity = CRITICAL) = 0; - - virtual void getAll(std::vector & target) const = 0; -}; - -class DLL_LINKAGE Spell -{ -public: - virtual ~Spell() = default; - - virtual int32_t getIndex() const = 0; - - virtual int32_t getLevel() const = 0; - - virtual void forEachSchool(const std::function & cb) const = 0; -}; - -class DLL_LINKAGE Caster -{ -public: - virtual ~Caster() = default; - - virtual int32_t getCasterUnitId() const = 0; - - /// returns level on which given spell would be cast by this(0 - none, 1 - basic etc); - /// caster may not know this spell at all - /// optionally returns number of selected school by arg - 0 - air magic, 1 - fire magic, 2 - water magic, 3 - earth magic - virtual ui8 getSpellSchoolLevel(const Spell * spell, int * outSelectedSchool = nullptr) const = 0; - - ///default spell school level for effect calculation - virtual int getEffectLevel(const Spell * spell) const = 0; - - ///applying sorcery secondary skill etc - virtual int64_t getSpellBonus(const Spell * spell, int64_t base, const battle::Unit * affectedStack) const = 0; - - ///only bonus for particular spell - virtual int64_t getSpecificSpellBonus(const Spell * spell, int64_t base) const = 0; - - ///default spell-power for damage/heal calculation - virtual int getEffectPower(const Spell * spell) const = 0; - - ///default spell-power for timed effects duration - virtual int getEnchantPower(const Spell * spell) const = 0; - - ///damage/heal override(ignores spell configuration, effect level and effect power) - virtual int64_t getEffectValue(const Spell * spell) const = 0; - - virtual const PlayerColor getOwner() const = 0; - - ///only name substitution - virtual void getCasterName(MetaString & text) const = 0; - - ///full default text - virtual void getCastDescription(const Spell * spell, const std::vector & attacked, MetaString & text) const = 0; - - virtual void spendMana(const PacketSender * server, const int spellCost) const = 0; -}; - -} diff --git a/lib/spells/Problem.h b/lib/spells/Problem.h index 88cb85b4e..b5432d98f 100644 --- a/lib/spells/Problem.h +++ b/lib/spells/Problem.h @@ -10,7 +10,7 @@ #pragma once -#include "Magic.h" +#include #include "../NetPacksBase.h" diff --git a/lib/spells/ProxyCaster.cpp b/lib/spells/ProxyCaster.cpp index bbb9f9e08..1ae601b62 100644 --- a/lib/spells/ProxyCaster.cpp +++ b/lib/spells/ProxyCaster.cpp @@ -29,12 +29,12 @@ int32_t ProxyCaster::getCasterUnitId() const return actualCaster->getCasterUnitId(); } -ui8 ProxyCaster::getSpellSchoolLevel(const Spell * spell, int * outSelectedSchool) const +int32_t ProxyCaster::getSpellSchoolLevel(const Spell * spell, int32_t * outSelectedSchool) const { return actualCaster->getSpellSchoolLevel(spell, outSelectedSchool); } -int ProxyCaster::getEffectLevel(const Spell * spell) const +int32_t ProxyCaster::getEffectLevel(const Spell * spell) const { return actualCaster->getEffectLevel(spell); } @@ -49,12 +49,12 @@ int64_t ProxyCaster::getSpecificSpellBonus(const Spell * spell, int64_t base) co return actualCaster->getSpecificSpellBonus(spell, base); } -int ProxyCaster::getEffectPower(const Spell * spell) const +int32_t ProxyCaster::getEffectPower(const Spell * spell) const { return actualCaster->getEffectPower(spell); } -int ProxyCaster::getEnchantPower(const Spell * spell) const +int32_t ProxyCaster::getEnchantPower(const Spell * spell) const { return actualCaster->getEnchantPower(spell); } @@ -64,9 +64,9 @@ int64_t ProxyCaster::getEffectValue(const Spell * spell) const return actualCaster->getEffectValue(spell); } -const PlayerColor ProxyCaster::getOwner() const +PlayerColor ProxyCaster::getCasterOwner() const { - return actualCaster->getOwner(); + return actualCaster->getCasterOwner(); } void ProxyCaster::getCasterName(MetaString & text) const @@ -79,7 +79,7 @@ void ProxyCaster::getCastDescription(const Spell * spell, const std::vectorgetCastDescription(spell, attacked, text); } -void ProxyCaster::spendMana(const PacketSender * server, const int spellCost) const +void ProxyCaster::spendMana(ServerCallback * server, const int32_t spellCost) const { actualCaster->spendMana(server, spellCost); } diff --git a/lib/spells/ProxyCaster.h b/lib/spells/ProxyCaster.h index 77633f4e2..f3656e880 100644 --- a/lib/spells/ProxyCaster.h +++ b/lib/spells/ProxyCaster.h @@ -10,7 +10,7 @@ #pragma once -#include "Magic.h" +#include namespace spells { @@ -22,17 +22,17 @@ public: virtual ~ProxyCaster(); int32_t getCasterUnitId() const override; - ui8 getSpellSchoolLevel(const Spell * spell, int * outSelectedSchool = nullptr) const override; - int getEffectLevel(const Spell * spell) const override; + int32_t getSpellSchoolLevel(const Spell * spell, int32_t * outSelectedSchool = nullptr) const override; + int32_t getEffectLevel(const Spell * spell) const override; int64_t getSpellBonus(const Spell * spell, int64_t base, const battle::Unit * affectedStack) const override; int64_t getSpecificSpellBonus(const Spell * spell, int64_t base) const override; - int getEffectPower(const Spell * spell) const override; - int getEnchantPower(const Spell * spell) const override; + int32_t getEffectPower(const Spell * spell) const override; + int32_t getEnchantPower(const Spell * spell) const override; int64_t getEffectValue(const Spell * spell) const override; - const PlayerColor getOwner() const override; + PlayerColor getCasterOwner() const override; void getCasterName(MetaString & text) const override; void getCastDescription(const Spell * spell, const std::vector & attacked, MetaString & text) const override; - void spendMana(const PacketSender * server, const int spellCost) const override; + void spendMana(ServerCallback * server, const int32_t spellCost) const override; private: const Caster * actualCaster; diff --git a/lib/spells/effects/Catapult.cpp b/lib/spells/effects/Catapult.cpp index f94a32b97..f1ed4051f 100644 --- a/lib/spells/effects/Catapult.cpp +++ b/lib/spells/effects/Catapult.cpp @@ -40,7 +40,7 @@ Catapult::~Catapult() = default; bool Catapult::applicable(Problem & problem, const Mechanics * m) const { - auto town = m->cb->battleGetDefendedTown(); + auto town = m->battle()->battleGetDefendedTown(); if(nullptr == town) { @@ -58,12 +58,12 @@ bool Catapult::applicable(Problem & problem, const Mechanics * m) const return m->adaptProblem(ESpellCastProblem::NO_APPROPRIATE_TARGET, problem); } - const auto attackableBattleHexes = m->cb->getAttackableBattleHexes(); - + const auto attackableBattleHexes = m->battle()->getAttackableBattleHexes(); + return !attackableBattleHexes.empty() || m->adaptProblem(ESpellCastProblem::NO_APPROPRIATE_TARGET, problem); } -void Catapult::apply(BattleStateProxy * battleState, RNG & rng, const Mechanics * m, const EffectTarget & /* eTarget */) const +void Catapult::apply(ServerCallback * server, const Mechanics * m, const EffectTarget & /* eTarget */) const { //start with all destructible parts static const std::set possibleTargets = @@ -88,9 +88,9 @@ void Catapult::apply(BattleStateProxy * battleState, RNG & rng, const Mechanics for(int i = 0; i < targetsToAttack; i++) { //Any destructible part can be hit regardless of its HP. Multiple hit on same target is allowed. - EWallPart::EWallPart target = *RandomGeneratorUtil::nextItem(possibleTargets, rng); + EWallPart::EWallPart target = *RandomGeneratorUtil::nextItem(possibleTargets, *server->getRNG()); - auto state = m->cb->battleGetWallState(target); + auto state = m->battle()->battleGetWallState(target); if(state == EWallState::DESTROYED || state == EWallState::NONE) continue; @@ -99,7 +99,7 @@ void Catapult::apply(BattleStateProxy * battleState, RNG & rng, const Mechanics attackInfo.damageDealt = 1; attackInfo.attackedPart = target; - attackInfo.destinationTile = m->cb->wallPartToBattleHex(target); + attackInfo.destinationTile = m->battle()->wallPartToBattleHex(target); ca.attackedParts.push_back(attackInfo); @@ -121,7 +121,7 @@ void Catapult::apply(BattleStateProxy * battleState, RNG & rng, const Mechanics if(posRemove != BattleHex::INVALID && state - attackInfo.damageDealt <= 0) //HP enum subtraction not intuitive, consider using SiegeInfo::applyDamage { - auto all = m->cb->battleGetUnitsIf([=](const battle::Unit * unit) + auto all = m->battle()->battleGetUnitsIf([=](const battle::Unit * unit) { return !unit->isGhost(); }); @@ -137,10 +137,10 @@ void Catapult::apply(BattleStateProxy * battleState, RNG & rng, const Mechanics } } - battleState->apply(&ca); + server->apply(&ca); if(!removeUnits.changedStacks.empty()) - battleState->apply(&removeUnits); + server->apply(&removeUnits); } void Catapult::serializeJsonEffect(JsonSerializeFormat & handler) diff --git a/lib/spells/effects/Catapult.h b/lib/spells/effects/Catapult.h index 47e84fc7e..762cd49d6 100644 --- a/lib/spells/effects/Catapult.h +++ b/lib/spells/effects/Catapult.h @@ -24,7 +24,7 @@ public: virtual ~Catapult(); bool applicable(Problem & problem, const Mechanics * m) const override; - void apply(BattleStateProxy * battleState, RNG & rng, const Mechanics * m, const EffectTarget & target) const override; + void apply(ServerCallback * server, const Mechanics * m, const EffectTarget & target) const override; protected: void serializeJsonEffect(JsonSerializeFormat & handler) override; diff --git a/lib/spells/effects/Clone.cpp b/lib/spells/effects/Clone.cpp index 4a0a1da12..97e9e7ebd 100644 --- a/lib/spells/effects/Clone.cpp +++ b/lib/spells/effects/Clone.cpp @@ -34,7 +34,7 @@ Clone::Clone() Clone::~Clone() = default; -void Clone::apply(BattleStateProxy * battleState, RNG & rng, const Mechanics * m, const EffectTarget & target) const +void Clone::apply(ServerCallback * server, const Mechanics * m, const EffectTarget & target) const { for(const Destination & dest : target) { @@ -43,7 +43,7 @@ void Clone::apply(BattleStateProxy * battleState, RNG & rng, const Mechanics * m //we shall have all targets to be stacks if(!clonedStack) { - battleState->complain("No target stack to clone! Invalid effect target transformation."); + server->complain("No target stack to clone! Invalid effect target transformation."); continue; } @@ -51,15 +51,15 @@ void Clone::apply(BattleStateProxy * battleState, RNG & rng, const Mechanics * m if(clonedStack->getCount() < 1) continue; - auto hex = m->cb->getAvaliableHex(clonedStack->creatureId(), m->casterSide, clonedStack->getPosition()); + auto hex = m->battle()->getAvaliableHex(clonedStack->creatureId(), m->casterSide, clonedStack->getPosition()); if(!hex.isValid()) { - battleState->complain("No place to put new clone!"); + server->complain("No place to put new clone!"); break; } - auto unitId = m->cb->battleNextUnitId(); + auto unitId = m->battle()->battleNextUnitId(); battle::UnitInfo info; info.id = unitId; @@ -72,13 +72,20 @@ void Clone::apply(BattleStateProxy * battleState, RNG & rng, const Mechanics * m BattleUnitsChanged pack; pack.changedStacks.emplace_back(info.id, UnitChanges::EOperation::ADD); info.save(pack.changedStacks.back().data); - battleState->apply(&pack); + server->apply(&pack); //TODO: use BattleUnitsChanged with UPDATE operation BattleUnitsChanged cloneFlags; - auto cloneUnit = m->cb->battleGetUnitByID(unitId); + auto cloneUnit = m->battle()->battleGetUnitByID(unitId); + + if(!cloneUnit) + { + server->complain("[Internal error] Cloned unit missing."); + continue; + } + auto cloneState = cloneUnit->acquireState(); cloneState->cloned = true; cloneFlags.changedStacks.emplace_back(cloneState->unitId(), UnitChanges::EOperation::RESET_STATE); @@ -89,7 +96,7 @@ void Clone::apply(BattleStateProxy * battleState, RNG & rng, const Mechanics * m cloneFlags.changedStacks.emplace_back(originalState->unitId(), UnitChanges::EOperation::RESET_STATE); originalState->save(cloneFlags.changedStacks.back().data); - battleState->apply(&cloneFlags); + server->apply(&cloneFlags); SetStackEffect sse; Bonus lifeTimeMarker(Bonus::N_TURNS, Bonus::NONE, Bonus::SPELL_EFFECT, 0, SpellID::CLONE); //TODO: use special bonus type @@ -97,7 +104,7 @@ void Clone::apply(BattleStateProxy * battleState, RNG & rng, const Mechanics * m std::vector buffer; buffer.push_back(lifeTimeMarker); sse.toAdd.push_back(std::make_pair(unitId, buffer)); - battleState->apply(&sse); + server->apply(&sse); } } diff --git a/lib/spells/effects/Clone.h b/lib/spells/effects/Clone.h index 3dcab7755..8047a0e51 100644 --- a/lib/spells/effects/Clone.h +++ b/lib/spells/effects/Clone.h @@ -23,7 +23,7 @@ public: Clone(); virtual ~Clone(); - void apply(BattleStateProxy * battleState, RNG & rng, const Mechanics * m, const EffectTarget & target) const override; + void apply(ServerCallback * server, const Mechanics * m, const EffectTarget & target) const override; protected: bool isReceptive(const Mechanics * m, const battle::Unit * s) const override; bool isValidTarget(const Mechanics * m, const battle::Unit * s) const override; diff --git a/lib/spells/effects/Damage.cpp b/lib/spells/effects/Damage.cpp index 15c9a1c15..2db14bf46 100644 --- a/lib/spells/effects/Damage.cpp +++ b/lib/spells/effects/Damage.cpp @@ -39,12 +39,60 @@ Damage::Damage() Damage::~Damage() = default; -void Damage::apply(BattleStateProxy * battleState, RNG & rng, const Mechanics * m, const EffectTarget & target) const +void Damage::apply(ServerCallback * server, const Mechanics * m, const EffectTarget & target) const { StacksInjured stacksInjured; - prepareEffects(stacksInjured, rng, m, target, battleState->describe); + BattleLogMessage blm; + size_t targetIndex = 0; + const battle::Unit * firstTarget = nullptr; + const bool describe = server->describeChanges(); + + int64_t damageToDisplay = 0; + uint32_t killed = 0; + bool multiple = false; + + for(auto & t : target) + { + const battle::Unit * unit = t.unitValue; + if(unit && unit->alive()) + { + BattleStackAttacked bsa; + bsa.damageAmount = damageForTarget(targetIndex, m, unit); + bsa.stackAttacked = unit->unitId(); + bsa.attackerID = -1; + auto newState = unit->acquireState(); + CStack::prepareAttacked(bsa, *server->getRNG(), newState); + + if(describe) + { + if(!firstTarget) + firstTarget = unit; + else + multiple = true; + damageToDisplay += bsa.damageAmount; + killed += bsa.killedAmount; + } + if(customEffectId >= 0) + { + bsa.effect = 82; + bsa.flags |= BattleStackAttacked::EFFECT; + } + + stacksInjured.stacks.push_back(bsa); + } + targetIndex++; + } + + if(describe && firstTarget && damageToDisplay > 0) + { + describeEffect(blm.lines, m, firstTarget, killed, damageToDisplay, multiple); + } + if(!stacksInjured.stacks.empty()) - battleState->apply(&stacksInjured); + server->apply(&stacksInjured); + + if(!blm.lines.empty()) + server->apply(&blm); } bool Damage::isReceptive(const Mechanics * m, const battle::Unit * unit) const @@ -175,50 +223,5 @@ void Damage::describeEffect(std::vector & log, const Mechanics * m, } } -void Damage::prepareEffects(StacksInjured & stacksInjured, RNG & rng, const Mechanics * m, const EffectTarget & target, bool describe) const -{ - size_t targetIndex = 0; - const battle::Unit * firstTarget = nullptr; - - int64_t damageToDisplay = 0; - uint32_t killed = 0; - bool multiple = false; - - for(auto & t : target) - { - const battle::Unit * unit = t.unitValue; - if(unit && unit->alive()) - { - BattleStackAttacked bsa; - bsa.damageAmount = damageForTarget(targetIndex, m, unit); - bsa.stackAttacked = unit->unitId(); - bsa.attackerID = -1; - auto newState = unit->acquireState(); - CStack::prepareAttacked(bsa, rng, newState); - - if(describe) - { - if(!firstTarget) - firstTarget = unit; - else - multiple = true; - damageToDisplay += bsa.damageAmount; - killed += bsa.killedAmount; - } - if(customEffectId >= 0) - { - bsa.effect = 82; - bsa.flags |= BattleStackAttacked::EFFECT; - } - - stacksInjured.stacks.push_back(bsa); - } - targetIndex++; - } - - if(describe && firstTarget && damageToDisplay > 0) - describeEffect(stacksInjured.battleLog, m, firstTarget, killed, damageToDisplay, multiple); -} - } } diff --git a/lib/spells/effects/Damage.h b/lib/spells/effects/Damage.h index 6452f7db4..27e6bb5af 100644 --- a/lib/spells/effects/Damage.h +++ b/lib/spells/effects/Damage.h @@ -25,7 +25,7 @@ public: Damage(); virtual ~Damage(); - void apply(BattleStateProxy * battleState, RNG & rng, const Mechanics * m, const EffectTarget & target) const override; + void apply(ServerCallback * server, const Mechanics * m, const EffectTarget & target) const override; protected: bool isReceptive(const Mechanics * m, const battle::Unit * unit) const override; @@ -40,8 +40,6 @@ private: int32_t customEffectId; bool killByPercentage; bool killByCount; - - void prepareEffects(StacksInjured & stacksInjured, RNG & rng, const Mechanics * m, const EffectTarget & target, bool describe) const; }; } diff --git a/lib/spells/effects/Dispel.cpp b/lib/spells/effects/Dispel.cpp index 590436a79..06d41dfba 100644 --- a/lib/spells/effects/Dispel.cpp +++ b/lib/spells/effects/Dispel.cpp @@ -11,8 +11,10 @@ #include "Dispel.h" #include "Registry.h" + +#include + #include "../ISpellMechanics.h" -#include "../CSpellHandler.h" #include "../../NetPacks.h" #include "../../battle/IBattleState.h" @@ -36,13 +38,42 @@ Dispel::Dispel() Dispel::~Dispel() = default; -void Dispel::apply(BattleStateProxy * battleState, RNG & rng, const Mechanics * m, const EffectTarget & target) const +void Dispel::apply(ServerCallback * server, const Mechanics * m, const EffectTarget & target) const { + const bool describe = server->describeChanges(); SetStackEffect sse; - prepareEffects(sse, rng, m, target, battleState->describe); + BattleLogMessage blm; + + for(auto & t : target) + { + const battle::Unit * unit = t.unitValue; + if(unit) + { + //special case for DISPEL_HELPFUL_SPELLS + if(describe && positive && !negative && !neutral) + { + MetaString line; + unit->addText(line, MetaString::GENERAL_TXT, -555, true); + unit->addNameReplacement(line, true); + blm.lines.push_back(std::move(line)); + } + + std::vector buffer; + auto bl = getBonuses(m, unit); + + for(auto item : *bl) + buffer.emplace_back(*item); + + if(!buffer.empty()) + sse.toRemove.push_back(std::make_pair(unit->unitId(), buffer)); + } + } if(!sse.toRemove.empty()) - battleState->apply(&sse); + server->apply(&sse); + + if(describe && !blm.lines.empty()) + server->apply(&blm); } bool Dispel::isValidTarget(const Mechanics * m, const battle::Unit * unit) const @@ -62,79 +93,52 @@ void Dispel::serializeJsonUnitEffect(JsonSerializeFormat & handler) std::shared_ptr Dispel::getBonuses(const Mechanics * m, const battle::Unit * unit) const { - auto addSelector = [=](const Bonus * bonus) + auto sel = [=](const Bonus * bonus) { if(bonus->source == Bonus::SPELL_EFFECT) { - const CSpell * sourceSpell = SpellID(bonus->sid).toSpell(); + const Spell * sourceSpell = SpellID(bonus->sid).toSpell(m->spells()); if(!sourceSpell) return false;//error - if(bonus->sid == m->getSpellIndex()) + + //Special case: DISRUPTING_RAY and ACID_BREATH_DEFENSE are "immune" to dispell + //Other even PERMANENT effects can be removed (f.e. BIND) + if(sourceSpell->getIndex() == SpellID::DISRUPTING_RAY || sourceSpell->getIndex() == SpellID::ACID_BREATH_DEFENSE) + return false; + //Special case: do not remove lifetime marker + if(sourceSpell->getIndex() == SpellID::CLONE) + return false; + //stack may have inherited effects + if(sourceSpell->isAdventure()) return false; - if(positive && sourceSpell->isPositive()) - return true; - if(negative && sourceSpell->isNegative()) - return true; - if(neutral && sourceSpell->isNeutral()) - return true; + if(sourceSpell->getIndex() == m->getSpellIndex()) + return false; + auto positiveness = sourceSpell->getPositiveness(); + + if(boost::logic::indeterminate(positiveness)) + { + if(neutral) + return true; + } + else if(positiveness) + { + if(positive) + return true; + } + else + { + if(negative) + return true; + } } return false; }; - CSelector selector = CSelector(mainSelector).And(CSelector(addSelector)); + CSelector selector(sel); return unit->getBonuses(selector); } -bool Dispel::mainSelector(const Bonus * bonus) -{ - if(bonus->source == Bonus::SPELL_EFFECT) - { - const CSpell * sourceSpell = SpellID(bonus->sid).toSpell(); - if(!sourceSpell) - return false;//error - //Special case: DISRUPTING_RAY and ACID_BREATH_DEFENSE are "immune" to dispell - //Other even PERMANENT effects can be removed (f.e. BIND) - if(sourceSpell->id == SpellID::DISRUPTING_RAY || sourceSpell->id == SpellID::ACID_BREATH_DEFENSE) - return false; - //Special case:do not remove lifetime marker - if(sourceSpell->id == SpellID::CLONE) - return false; - //stack may have inherited effects - return sourceSpell->isCombatSpell(); - } - //not spell effect - return false; -} - -void Dispel::prepareEffects(SetStackEffect & pack, RNG & rng, const Mechanics * m, const EffectTarget & target, bool describe) const -{ - for(auto & t : target) - { - const battle::Unit * unit = t.unitValue; - if(unit) - { - //special case for DISPEL_HELPFUL_SPELLS - if(describe && positive && !negative && !neutral) - { - MetaString line; - unit->addText(line, MetaString::GENERAL_TXT, -555, true); - unit->addNameReplacement(line, true); - pack.battleLog.push_back(std::move(line)); - } - - std::vector buffer; - auto bl = getBonuses(m, unit); - - for(auto item : *bl) - buffer.emplace_back(*item); - - if(!buffer.empty()) - pack.toRemove.push_back(std::make_pair(unit->unitId(), buffer)); - } - } -} - } } diff --git a/lib/spells/effects/Dispel.h b/lib/spells/effects/Dispel.h index 71c360010..bebb1200a 100644 --- a/lib/spells/effects/Dispel.h +++ b/lib/spells/effects/Dispel.h @@ -28,7 +28,7 @@ public: Dispel(); virtual ~Dispel(); - void apply(BattleStateProxy * battleState, RNG & rng, const Mechanics * m, const EffectTarget & target) const override; + void apply(ServerCallback * server, const Mechanics * m, const EffectTarget & target) const override; protected: bool isValidTarget(const Mechanics * m, const battle::Unit * unit) const override; @@ -40,9 +40,6 @@ private: bool neutral = false; std::shared_ptr getBonuses(const Mechanics * m, const battle::Unit * unit) const; - - static bool mainSelector(const Bonus * bonus); - void prepareEffects(SetStackEffect & pack, RNG & rng, const Mechanics * m, const EffectTarget & target, bool describe) const; }; } diff --git a/lib/spells/effects/Effect.cpp b/lib/spells/effects/Effect.cpp index fcba715a5..77f240f51 100644 --- a/lib/spells/effects/Effect.cpp +++ b/lib/spells/effects/Effect.cpp @@ -45,9 +45,9 @@ void Effect::serializeJson(JsonSerializeFormat & handler) serializeJsonEffect(handler); } -std::shared_ptr Effect::create(const std::string & type) +std::shared_ptr Effect::create(const Registry * registry, const std::string & type) { - auto factory = Registry::get()->find(type); + auto factory = registry->find(type); if(factory) { diff --git a/lib/spells/effects/Effect.h b/lib/spells/effects/Effect.h index befce5339..8ebf0f332 100644 --- a/lib/spells/effects/Effect.h +++ b/lib/spells/effects/Effect.h @@ -10,12 +10,12 @@ #pragma once -#include "../Magic.h" +#include struct BattleHex; class CBattleInfoCallback; class JsonSerializeFormat; -class IBattleState; +class ServerCallback; namespace vstd { @@ -26,8 +26,6 @@ namespace spells { using EffectTarget = Target; -class BattleStateProxy; - namespace effects { using RNG = ::vstd::RNG; @@ -58,7 +56,7 @@ public: virtual bool applicable(Problem & problem, const Mechanics * m) const; virtual bool applicable(Problem & problem, const Mechanics * m, const EffectTarget & target) const; - virtual void apply(BattleStateProxy * battleState, RNG & rng, const Mechanics * m, const EffectTarget & target) const = 0; + virtual void apply(ServerCallback * server, const Mechanics * m, const EffectTarget & target) const = 0; virtual EffectTarget filterTarget(const Mechanics * m, const EffectTarget & target) const = 0; @@ -66,7 +64,7 @@ public: void serializeJson(JsonSerializeFormat & handler); - static std::shared_ptr create(const std::string & type); + static std::shared_ptr create(const Registry * registry, const std::string & type); protected: virtual void serializeJsonEffect(JsonSerializeFormat & handler) = 0; diff --git a/lib/spells/effects/Effects.cpp b/lib/spells/effects/Effects.cpp index cffca43d4..4a300d49b 100644 --- a/lib/spells/effects/Effects.cpp +++ b/lib/spells/effects/Effects.cpp @@ -11,6 +11,8 @@ #include "Effects.h" +#include + #include "../ISpellMechanics.h" #include "../../serializer/JsonSerializeFormat.h" @@ -128,7 +130,7 @@ Effects::EffectsToApply Effects::prepare(const Mechanics * m, const Target & aim return effectsToApply; } -void Effects::serializeJson(JsonSerializeFormat & handler, const int level) +void Effects::serializeJson(const Registry * registry, JsonSerializeFormat & handler, const int level) { assert(!handler.saving); @@ -143,7 +145,7 @@ void Effects::serializeJson(JsonSerializeFormat & handler, const int level) std::string type; handler.serializeString("type", type); - auto effect = Effect::create(type); + auto effect = Effect::create(registry, type); if(effect) { effect->serializeJson(handler); diff --git a/lib/spells/effects/Effects.h b/lib/spells/effects/Effects.h index 909678e6a..f9bc264df 100644 --- a/lib/spells/effects/Effects.h +++ b/lib/spells/effects/Effects.h @@ -40,7 +40,7 @@ public: EffectsToApply prepare(const Mechanics * m, const Target & aimPoint, const Target & spellTarget) const; - void serializeJson(JsonSerializeFormat & handler, const int level); + void serializeJson(const Registry * registry, JsonSerializeFormat & handler, const int level); }; diff --git a/lib/spells/effects/Heal.cpp b/lib/spells/effects/Heal.cpp index cf8b3a63a..4d07e15dd 100644 --- a/lib/spells/effects/Heal.cpp +++ b/lib/spells/effects/Heal.cpp @@ -40,17 +40,17 @@ Heal::Heal() Heal::~Heal() = default; -void Heal::apply(BattleStateProxy * battleState, RNG & rng, const Mechanics * m, const EffectTarget & target) const +void Heal::apply(ServerCallback * server, const Mechanics * m, const EffectTarget & target) const { - apply(m->getEffectValue(), battleState, rng, m, target); + apply(m->getEffectValue(), server, m, target); } -void Heal::apply(int64_t value, BattleStateProxy * battleState, RNG & rng, const Mechanics * m, const EffectTarget & target) const +void Heal::apply(int64_t value, ServerCallback * server, const Mechanics * m, const EffectTarget & target) const { BattleUnitsChanged pack; - prepareHealEffect(value, pack, rng, m, target); + prepareHealEffect(value, pack, *server->getRNG(), m, target); if(!pack.changedStacks.empty()) - battleState->apply(&pack); + server->apply(&pack); } bool Heal::isValidTarget(const Mechanics * m, const battle::Unit * unit) const @@ -78,7 +78,7 @@ bool Heal::isValidTarget(const Mechanics * m, const battle::Unit * unit) const //check if alive unit blocks resurrection for(const BattleHex & hex : battle::Unit::getHexes(unit->getPosition(), unit->doubleWide(), unit->unitSide())) { - auto blocking = m->cb->battleGetUnitsIf([hex, unit](const battle::Unit * other) + auto blocking = m->battle()->battleGetUnitsIf([hex, unit](const battle::Unit * other) { return other->isValidTarget(false) && other->coversPos(hex) && other != unit; }); diff --git a/lib/spells/effects/Heal.h b/lib/spells/effects/Heal.h index daacff6fb..0160be35f 100644 --- a/lib/spells/effects/Heal.h +++ b/lib/spells/effects/Heal.h @@ -26,10 +26,10 @@ public: Heal(); virtual ~Heal(); - void apply(BattleStateProxy * battleState, RNG & rng, const Mechanics * m, const EffectTarget & target) const override; + void apply(ServerCallback * server, const Mechanics * m, const EffectTarget & target) const override; protected: - void apply(int64_t value, BattleStateProxy * battleState, RNG & rng, const Mechanics * m, const EffectTarget & target) const; + void apply(int64_t value, ServerCallback * server, const Mechanics * m, const EffectTarget & target) const; bool isValidTarget(const Mechanics * m, const battle::Unit * unit) const override; void serializeJsonUnitEffect(JsonSerializeFormat & handler) override final; diff --git a/lib/spells/effects/Obstacle.cpp b/lib/spells/effects/Obstacle.cpp index d848a4567..99b4a7f5b 100644 --- a/lib/spells/effects/Obstacle.cpp +++ b/lib/spells/effects/Obstacle.cpp @@ -154,7 +154,7 @@ bool Obstacle::applicable(Problem & problem, const Mechanics * m, const EffectTa for(auto direction : trasformation) hex.moveInDirection(direction, false); - if(!isHexAvailable(m->cb, hex, requiresClearTiles)) + if(!isHexAvailable(m->battle(), hex, requiresClearTiles)) return noRoomToPlace(problem, m); } } @@ -188,7 +188,7 @@ EffectTarget Obstacle::transformTarget(const Mechanics * m, const Target & aimPo return ret; } -void Obstacle::apply(BattleStateProxy * battleState, RNG & rng, const Mechanics * m, const EffectTarget & target) const +void Obstacle::apply(ServerCallback * server, const Mechanics * m, const EffectTarget & target) const { if(m->isMassive()) { @@ -196,10 +196,10 @@ void Obstacle::apply(BattleStateProxy * battleState, RNG & rng, const Mechanics for(int i = 0; i < GameConstants::BFIELD_SIZE; i++) { BattleHex hex = i; - if(isHexAvailable(m->cb, hex, true)) + if(isHexAvailable(m->battle(), hex, true)) availableTiles.push_back(hex); } - RandomGeneratorUtil::randomShuffle(availableTiles, rng); + RandomGeneratorUtil::randomShuffle(availableTiles, *server->getRNG()); const int patchesToPut = std::min(patchCount, (int)availableTiles.size()); @@ -208,11 +208,11 @@ void Obstacle::apply(BattleStateProxy * battleState, RNG & rng, const Mechanics for(int i = 0; i < patchesToPut; i++) randomTarget.emplace_back(availableTiles.at(i)); - placeObstacles(battleState, m, randomTarget); + placeObstacles(server, m, randomTarget); } else { - placeObstacles(battleState, m, target); + placeObstacles(server, m, target); } } @@ -274,7 +274,7 @@ bool Obstacle::noRoomToPlace(Problem & problem, const Mechanics * m) return false; } -void Obstacle::placeObstacles(BattleStateProxy * battleState, const Mechanics * m, const EffectTarget & target) const +void Obstacle::placeObstacles(ServerCallback * server, const Mechanics * m, const EffectTarget & target) const { const ObstacleSideOptions & options = sideOptions.at(m->casterSide); @@ -282,10 +282,10 @@ void Obstacle::placeObstacles(BattleStateProxy * battleState, const Mechanics * boost::optional perspective; - if(!m->cb->getPlayerID()) + if(!m->battle()->getPlayerID()) perspective = boost::make_optional(BattlePerspective::ALL_KNOWING); - auto all = m->cb->battleGetAllObstacles(perspective); + auto all = m->battle()->battleGetAllObstacles(perspective); int obstacleIdToGive = 1; for(auto & one : all) @@ -334,7 +334,7 @@ void Obstacle::placeObstacles(BattleStateProxy * battleState, const Mechanics * } if(!pack.changes.empty()) - battleState->apply(&pack); + server->apply(&pack); } diff --git a/lib/spells/effects/Obstacle.h b/lib/spells/effects/Obstacle.h index 8e9cfb82f..bea14ee1c 100644 --- a/lib/spells/effects/Obstacle.h +++ b/lib/spells/effects/Obstacle.h @@ -53,7 +53,7 @@ public: EffectTarget transformTarget(const Mechanics * m, const Target & aimPoint, const Target & spellTarget) const override; - void apply(BattleStateProxy * battleState, RNG & rng, const Mechanics * m, const EffectTarget & target) const override; + void apply(ServerCallback * server, const Mechanics * m, const EffectTarget & target) const override; protected: void serializeJsonEffect(JsonSerializeFormat & handler) override; @@ -72,7 +72,7 @@ private: static bool isHexAvailable(const CBattleInfoCallback * cb, const BattleHex & hex, const bool mustBeClear); static bool noRoomToPlace(Problem & problem, const Mechanics * m); - void placeObstacles(BattleStateProxy * battleState, const Mechanics * m, const EffectTarget & target) const; + void placeObstacles(ServerCallback * server, const Mechanics * m, const EffectTarget & target) const; }; } diff --git a/lib/spells/effects/Registry.cpp b/lib/spells/effects/Registry.cpp index 9584f93a8..5d12c2283 100644 --- a/lib/spells/effects/Registry.cpp +++ b/lib/spells/effects/Registry.cpp @@ -20,7 +20,6 @@ namespace detail { class RegistryImpl : public Registry { - using DataPtr = std::shared_ptr; public: RegistryImpl() = default; ~RegistryImpl() = default; @@ -34,13 +33,13 @@ public: return iter->second.get(); } - void add(const std::string & name, IEffectFactory * item) override + void add(const std::string & name, FactoryPtr item) override { - data[name].reset(item); + data[name] = item; } private: - std::map data; + std::map data; }; } @@ -49,7 +48,7 @@ Registry::Registry() = default; Registry::~Registry() = default; -Registry * Registry::get() +Registry * GlobalRegistry::get() { static std::unique_ptr Instance = make_unique(); return Instance.get(); diff --git a/lib/spells/effects/Registry.h b/lib/spells/effects/Registry.h index 33f580e34..8e0eeac17 100644 --- a/lib/spells/effects/Registry.h +++ b/lib/spells/effects/Registry.h @@ -24,7 +24,7 @@ namespace spells namespace effects { -class IEffectFactory +class DLL_LINKAGE IEffectFactory { public: IEffectFactory() = default; @@ -33,16 +33,22 @@ public: virtual Effect * create() const = 0; }; -class Registry +class DLL_LINKAGE Registry { public: + using FactoryPtr = std::shared_ptr; + + Registry(); virtual ~Registry(); virtual const IEffectFactory * find(const std::string & name) const = 0; - virtual void add(const std::string & name, IEffectFactory * item) = 0; + virtual void add(const std::string & name, FactoryPtr item) = 0; +}; +class DLL_LINKAGE GlobalRegistry +{ + GlobalRegistry() = default; +public: static Registry * get(); -protected: - Registry(); }; template @@ -64,8 +70,8 @@ class RegisterEffect public: RegisterEffect(const std::string & name) { - IEffectFactory * f = new EffectFactory(); - Registry::get()->add(name, f); + auto f = std::make_shared>(); + GlobalRegistry::get()->add(name, f); } }; diff --git a/lib/spells/effects/RemoveObstacle.cpp b/lib/spells/effects/RemoveObstacle.cpp index ec20aae31..7e12568ba 100644 --- a/lib/spells/effects/RemoveObstacle.cpp +++ b/lib/spells/effects/RemoveObstacle.cpp @@ -49,7 +49,7 @@ bool RemoveObstacle::applicable(Problem & problem, const Mechanics * m, const Ef return !getTargets(m, target, false).empty(); } -void RemoveObstacle::apply(BattleStateProxy * battleState, RNG & rng, const Mechanics * m, const EffectTarget & target) const +void RemoveObstacle::apply(ServerCallback * server, const Mechanics * m, const EffectTarget & target) const { BattleObstaclesChanged pack; @@ -57,7 +57,7 @@ void RemoveObstacle::apply(BattleStateProxy * battleState, RNG & rng, const Mech pack.changes.emplace_back(obstacle->uniqueID, BattleChanges::EOperation::REMOVE); if(!pack.changes.empty()) - battleState->apply(&pack); + server->apply(&pack); } void RemoveObstacle::serializeJsonEffect(JsonSerializeFormat & handler) @@ -93,7 +93,7 @@ std::set RemoveObstacle::getTargets(const Mechanics * std::set possibleTargets; if(m->isMassive() || alwaysMassive) { - for(const auto & obstacle : m->cb->battleGetAllObstacles()) + for(const auto & obstacle : m->battle()->battleGetAllObstacles()) if(canRemove(obstacle.get())) possibleTargets.insert(obstacle.get()); } @@ -101,7 +101,7 @@ std::set RemoveObstacle::getTargets(const Mechanics * { for(const auto & destination : target) if(destination.hexValue.isValid()) - for(const auto & obstacle : m->cb->battleGetAllObstaclesOnPos(destination.hexValue, false)) + for(const auto & obstacle : m->battle()->battleGetAllObstaclesOnPos(destination.hexValue, false)) if(canRemove(obstacle.get())) possibleTargets.insert(obstacle.get()); } diff --git a/lib/spells/effects/RemoveObstacle.h b/lib/spells/effects/RemoveObstacle.h index 2193c6347..bdbc2dd31 100644 --- a/lib/spells/effects/RemoveObstacle.h +++ b/lib/spells/effects/RemoveObstacle.h @@ -31,7 +31,7 @@ public: bool applicable(Problem & problem, const Mechanics * m) const override; bool applicable(Problem & problem, const Mechanics * m, const EffectTarget & target) const override; - void apply(BattleStateProxy * battleState, RNG & rng, const Mechanics * m, const EffectTarget & target) const override; + void apply(ServerCallback * server, const Mechanics * m, const EffectTarget & target) const override; protected: void serializeJsonEffect(JsonSerializeFormat & handler) override; diff --git a/lib/spells/effects/Sacrifice.cpp b/lib/spells/effects/Sacrifice.cpp index a0d888335..52e60920d 100644 --- a/lib/spells/effects/Sacrifice.cpp +++ b/lib/spells/effects/Sacrifice.cpp @@ -64,7 +64,7 @@ bool Sacrifice::applicable(Problem & problem, const Mechanics * m) const auto mainFilter = std::bind(&UnitEffect::getStackFilter, this, m, true, _1); auto predicate = std::bind(&UnitEffect::eraseByImmunityFilter, this, m, _1); - auto targets = m->cb->battleGetUnitsIf(mainFilter); + auto targets = m->battle()->battleGetUnitsIf(mainFilter); vstd::erase_if(targets, predicate); bool targetExists = false; @@ -117,7 +117,7 @@ bool Sacrifice::applicable(Problem & problem, const Mechanics * m, const EffectT return true; } -void Sacrifice::apply(BattleStateProxy * battleState, RNG & rng, const Mechanics * m, const EffectTarget & target) const +void Sacrifice::apply(ServerCallback * server, const Mechanics * m, const EffectTarget & target) const { if(target.size() != 2) { @@ -136,11 +136,11 @@ void Sacrifice::apply(BattleStateProxy * battleState, RNG & rng, const Mechanics EffectTarget healTarget; healTarget.emplace_back(target.front()); - Heal::apply(calculateHealEffectValue(m, victim), battleState, rng, m, healTarget); + Heal::apply(calculateHealEffectValue(m, victim), server, m, healTarget); BattleUnitsChanged removeUnits; removeUnits.changedStacks.emplace_back(victim->unitId(), UnitChanges::EOperation::REMOVE); - battleState->apply(&removeUnits); + server->apply(&removeUnits); } bool Sacrifice::isValidTarget(const Mechanics * m, const battle::Unit * unit) const diff --git a/lib/spells/effects/Sacrifice.h b/lib/spells/effects/Sacrifice.h index 45b5e1ea3..e89ef0c48 100644 --- a/lib/spells/effects/Sacrifice.h +++ b/lib/spells/effects/Sacrifice.h @@ -28,7 +28,7 @@ public: bool applicable(Problem & problem, const Mechanics * m) const override; bool applicable(Problem & problem, const Mechanics * m, const EffectTarget & target) const override; - void apply(BattleStateProxy * battleState, RNG & rng, const Mechanics * m, const EffectTarget & target) const override; + void apply(ServerCallback * server, const Mechanics * m, const EffectTarget & target) const override; EffectTarget transformTarget(const Mechanics * m, const Target & aimPoint, const Target & spellTarget) const override; diff --git a/lib/spells/effects/Summon.cpp b/lib/spells/effects/Summon.cpp index 4382c4d2b..2601f7d52 100644 --- a/lib/spells/effects/Summon.cpp +++ b/lib/spells/effects/Summon.cpp @@ -60,7 +60,7 @@ bool Summon::applicable(Problem & problem, const Mechanics * m) const { //check if there are summoned creatures of other type - auto otherSummoned = m->cb->battleGetUnitsIf([m, this](const battle::Unit * unit) + auto otherSummoned = m->battle()->battleGetUnitsIf([m, this](const battle::Unit * unit) { return (unit->unitOwner() == m->getCasterColor()) && (unit->unitSlot() == SlotID::SUMMONED_SLOT_PLACEHOLDER) @@ -96,7 +96,7 @@ bool Summon::applicable(Problem & problem, const Mechanics * m) const return true; } -void Summon::apply(BattleStateProxy * battleState, RNG & rng, const Mechanics * m, const EffectTarget & target) const +void Summon::apply(ServerCallback * server, const Mechanics * m, const EffectTarget & target) const { //new feature - percentage bonus auto valueWithBonus = m->applySpecificSpellBonus(m->calculateRawEffectValue(0, m->getEffectPower()));//TODO: consider use base power too @@ -120,8 +120,8 @@ void Summon::apply(BattleStateProxy * battleState, RNG & rng, const Mechanics * if(summonByHealth) { - auto creatureType = creature.toCreature(); - auto creatureMaxHealth = creatureType->MaxHealth(); + auto creatureType = creature.toCreature(m->creatures()); + auto creatureMaxHealth = creatureType->getMaxHealth(); amount = static_cast(valueWithBonus / creatureMaxHealth); } else @@ -131,12 +131,12 @@ void Summon::apply(BattleStateProxy * battleState, RNG & rng, const Mechanics * if(amount < 1) { - battleState->complain("Summoning didn't summon any!"); + server->complain("Summoning didn't summon any!"); continue; } battle::UnitInfo info; - info.id = m->cb->battleNextUnitId(); + info.id = m->battle()->battleNextUnitId(); info.count = amount; info.type = creature; info.side = m->casterSide; @@ -149,7 +149,7 @@ void Summon::apply(BattleStateProxy * battleState, RNG & rng, const Mechanics * } if(!pack.changedStacks.empty()) - battleState->apply(&pack); + server->apply(&pack); } EffectTarget Summon::filterTarget(const Mechanics * m, const EffectTarget & target) const @@ -168,7 +168,7 @@ void Summon::serializeJsonEffect(JsonSerializeFormat & handler) EffectTarget Summon::transformTarget(const Mechanics * m, const Target & aimPoint, const Target & spellTarget) const { - auto sameSummoned = m->cb->battleGetUnitsIf([m, this](const battle::Unit * unit) + auto sameSummoned = m->battle()->battleGetUnitsIf([m, this](const battle::Unit * unit) { return (unit->unitOwner() == m->getCasterColor()) && (unit->unitSlot() == SlotID::SUMMONED_SLOT_PLACEHOLDER) @@ -181,7 +181,7 @@ EffectTarget Summon::transformTarget(const Mechanics * m, const Target & aimPoin if(sameSummoned.empty() || !summonSameUnit) { - BattleHex hex = m->cb->getAvaliableHex(creature, m->casterSide); + BattleHex hex = m->battle()->getAvaliableHex(creature, m->casterSide); if(!hex.isValid()) logGlobal->error("No free space to summon creature!"); else diff --git a/lib/spells/effects/Summon.h b/lib/spells/effects/Summon.h index 925a74c63..d12d5373b 100644 --- a/lib/spells/effects/Summon.h +++ b/lib/spells/effects/Summon.h @@ -29,7 +29,7 @@ public: bool applicable(Problem & problem, const Mechanics * m) const override; - void apply(BattleStateProxy * battleState, RNG & rng, const Mechanics * m, const EffectTarget & target) const override; + void apply(ServerCallback * server, const Mechanics * m, const EffectTarget & target) const override; EffectTarget filterTarget(const Mechanics * m, const EffectTarget & target) const override; diff --git a/lib/spells/effects/Teleport.cpp b/lib/spells/effects/Teleport.cpp index 176394f2a..5501a189e 100644 --- a/lib/spells/effects/Teleport.cpp +++ b/lib/spells/effects/Teleport.cpp @@ -61,57 +61,45 @@ bool Teleport::applicable(Problem & problem, const Mechanics * m) const return UnitEffect::applicable(problem, m); } -void Teleport::apply(BattleStateProxy * battleState, RNG & rng, const Mechanics * m, const EffectTarget & target) const -{ - BattleStackMoved pack; - std::string errorMessage; - - if(prepareEffects(errorMessage, pack, m, target)) - battleState->apply(&pack); - else - battleState->complain(errorMessage); -} - -bool Teleport::prepareEffects(std::string & errorMessage, BattleStackMoved & pack, const Mechanics * m, const EffectTarget & target) const +void Teleport::apply(ServerCallback * server, const Mechanics * m, const EffectTarget & target) const { if(target.size() != 2) { - errorMessage = "Teleport requires 2 destinations."; - return false; + server->complain("Teleport requires 2 destinations."); + return; } auto targetUnit = target[0].unitValue; if(nullptr == targetUnit) { - errorMessage = "No unit to teleport"; - return false; + server->complain("No unit to teleport"); + return; } const BattleHex destination = target[1].hexValue; if(!destination.isValid()) { - errorMessage = "Invalid teleport destination"; - return false; + server->complain("Invalid teleport destination"); + return; } //TODO: move here all teleport checks - if(!m->cb->battleCanTeleportTo(targetUnit, destination, m->getEffectLevel())) + if(!m->battle()->battleCanTeleportTo(targetUnit, destination, m->getEffectLevel())) { - errorMessage = "Forbidden teleport."; - return false; + server->complain("Forbidden teleport."); + return; } + BattleStackMoved pack; pack.distance = 0; pack.stack = targetUnit->unitId(); std::vector tiles; tiles.push_back(destination); pack.tilesToMove = tiles; pack.teleporting = true; - - return true; + server->apply(&pack); } - void Teleport::serializeJsonUnitEffect(JsonSerializeFormat & handler) { //TODO: teleport options diff --git a/lib/spells/effects/Teleport.h b/lib/spells/effects/Teleport.h index 93b281d01..b547bf7f6 100644 --- a/lib/spells/effects/Teleport.h +++ b/lib/spells/effects/Teleport.h @@ -29,15 +29,12 @@ public: bool applicable(Problem & problem, const Mechanics * m) const override; - void apply(BattleStateProxy * battleState, RNG & rng, const Mechanics * m, const EffectTarget & target) const override; + void apply(ServerCallback * server, const Mechanics * m, const EffectTarget & target) const override; EffectTarget transformTarget(const Mechanics * m, const Target & aimPoint, const Target & spellTarget) const override; protected: void serializeJsonUnitEffect(JsonSerializeFormat & handler) override; - -private: - bool prepareEffects(std::string & errorMessage, BattleStackMoved & pack, const Mechanics * m, const EffectTarget & target) const; }; } diff --git a/lib/spells/effects/Timed.cpp b/lib/spells/effects/Timed.cpp index a82186de7..300fbedce 100644 --- a/lib/spells/effects/Timed.cpp +++ b/lib/spells/effects/Timed.cpp @@ -36,13 +36,125 @@ Timed::Timed() Timed::~Timed() = default; -void Timed::apply(BattleStateProxy * battleState, RNG & rng, const Mechanics * m, const EffectTarget & target) const +void Timed::apply(ServerCallback * server, const Mechanics * m, const EffectTarget & target) const { + const bool describe = server->describeChanges(); + int32_t duration = m->getEffectDuration(); + + std::vector converted; + convertBonus(m, duration, converted); + + std::shared_ptr peculiarBonus = nullptr; + std::shared_ptr addedValueBonus = nullptr; + std::shared_ptr fixedValueBonus = nullptr; + + auto casterHero = dynamic_cast(m->caster); + if(casterHero) + { + peculiarBonus = casterHero->getBonusLocalFirst(Selector::typeSubtype(Bonus::SPECIAL_PECULIAR_ENCHANT, m->getSpellIndex())); + addedValueBonus = casterHero->getBonusLocalFirst(Selector::typeSubtype(Bonus::SPECIAL_ADD_VALUE_ENCHANT, m->getSpellIndex())); + fixedValueBonus = casterHero->getBonusLocalFirst(Selector::typeSubtype(Bonus::SPECIAL_FIXED_VALUE_ENCHANT, m->getSpellIndex())); + } + //TODO: does hero specialty should affects his stack casting spells? + SetStackEffect sse; - prepareEffects(sse, m, target, battleState->describe); + BattleLogMessage blm; + + for(auto & t : target) + { + std::vector buffer; + std::copy(converted.begin(), converted.end(), std::back_inserter(buffer)); + + const battle::Unit * affected = t.unitValue; + if(!affected) + { + logGlobal->error("[Internal error] Invalid target for timed effect"); + continue; + } + + if(!affected->alive()) + continue; + + if(describe) + describeEffect(blm.lines, m, converted, affected); + + + //Apply hero specials - peculiar enchants + const auto tier = std::max(affected->creatureLevel(), 1); //don't divide by 0 for certain creatures (commanders, war machines) + if(peculiarBonus) + { + si32 power = 0; + switch (peculiarBonus->additionalInfo[0]) + { + case 0: //normal + switch (tier) + { + case 1: + case 2: + power = 3; + break; + case 3: + case 4: + power = 2; + break; + case 5: + case 6: + power = 1; + break; + } + break; + case 1: + //Coronius style specialty bonus. + //Please note that actual Coronius isnt here, because Slayer is a spell that doesnt affect monster stats and is used only in calculateDmgRange + power = std::max(5 - tier, 0); + break; + } + if(m->isNegativeSpell()) + { + //negative spells like weakness are defined in json with negative numbers, so we need do same here + power = -1 * power; + } + for(Bonus& b : buffer) + { + b.val += power; + } + + } + + if(addedValueBonus) + { + for(Bonus & b : buffer) + { + b.val += addedValueBonus->additionalInfo[0]; + } + } + if(fixedValueBonus) + { + for(Bonus & b : buffer) + { + b.val = fixedValueBonus->additionalInfo[0]; + } + } + + if(casterHero && casterHero->hasBonusOfType(Bonus::SPECIAL_BLESS_DAMAGE, m->getSpellIndex())) //TODO: better handling of bonus percentages + { + int damagePercent = casterHero->valOfBonuses(Bonus::SPECIAL_BLESS_DAMAGE, m->getSpellIndex()) / tier; + Bonus specialBonus(Bonus::N_TURNS, Bonus::CREATURE_DAMAGE, Bonus::SPELL_EFFECT, damagePercent, m->getSpellIndex(), 0, Bonus::PERCENT_TO_ALL); + specialBonus.turnsRemain = duration; + buffer.push_back(specialBonus); + } + + if(cumulative) + sse.toAdd.push_back(std::make_pair(affected->unitId(), buffer)); + else + sse.toUpdate.push_back(std::make_pair(affected->unitId(), buffer)); + } if(!(sse.toAdd.empty() && sse.toUpdate.empty())) - battleState->apply(&sse); + server->apply(&sse); + + if(describe && !blm.lines.empty()) + server->apply(&blm); } void Timed::convertBonus(const Mechanics * m, int32_t & duration, std::vector & converted) const @@ -143,115 +255,6 @@ void Timed::describeEffect(std::vector & log, const Mechanics * m, c } } -void Timed::prepareEffects(SetStackEffect & sse, const Mechanics * m, const EffectTarget & target, bool describe) const -{ - //get default spell duration (spell power with bonuses for heroes) - int32_t duration = m->getEffectDuration(); - - std::vector converted; - convertBonus(m, duration, converted); - - std::shared_ptr peculiarBonus = nullptr; - std::shared_ptr addedValueBonus = nullptr; - std::shared_ptr fixedValueBonus = nullptr; - auto casterHero = dynamic_cast(m->caster); - if(casterHero) - { - peculiarBonus = casterHero->getBonusLocalFirst(Selector::typeSubtype(Bonus::SPECIAL_PECULIAR_ENCHANT, m->getSpellIndex())); - addedValueBonus = casterHero->getBonusLocalFirst(Selector::typeSubtype(Bonus::SPECIAL_ADD_VALUE_ENCHANT, m->getSpellIndex())); - fixedValueBonus = casterHero->getBonusLocalFirst(Selector::typeSubtype(Bonus::SPECIAL_FIXED_VALUE_ENCHANT, m->getSpellIndex())); - } - //TODO: does hero specialty should affects his stack casting spells? - - for(auto & t : target) - { - std::vector buffer; - std::copy(converted.begin(), converted.end(), std::back_inserter(buffer)); - - const battle::Unit * affected = t.unitValue; - if(!affected) - { - logGlobal->error("[Internal error] Invalid target for timed effect"); - continue; - } - - if(!affected->alive()) - continue; - - if(describe) - describeEffect(sse.battleLog, m, converted, affected); - - const auto tier = std::max(affected->creatureLevel(), 1); //don't divide by 0 for certain creatures (commanders, war machines) - - //Apply hero specials - peculiar enchants - if(peculiarBonus) - { - - si32 power = 0; - switch (peculiarBonus->additionalInfo[0]) - { - case 0: //normal - switch (tier) - { - case 1: - case 2: - power = 3; - break; - case 3: - case 4: - power = 2; - break; - case 5: - case 6: - power = 1; - break; - } - break; - case 1: - //Coronius style specialty bonus. - //Please note that actual Coronius isnt here, because Slayer is a spell that doesnt affect monster stats and is used only in calculateDmgRange - power = std::max(5 - tier, 0); - break; - } - if(m->isNegativeSpell()) - { - //negative spells like weakness are defined in json with negative numbers, so we need do same here - power = -1 * power; - } - for(Bonus& b : buffer) - { - b.val += power; - } - } - if(addedValueBonus) - { - for(Bonus& b : buffer) - { - b.val += addedValueBonus->additionalInfo[0]; - } - } - if(fixedValueBonus) - { - for(Bonus& b : buffer) - { - b.val = fixedValueBonus->additionalInfo[0]; - } - } - if(casterHero && casterHero->hasBonusOfType(Bonus::SPECIAL_BLESS_DAMAGE, m->getSpellIndex())) //TODO: better handling of bonus percentages - { - int damagePercent = casterHero->valOfBonuses(Bonus::SPECIAL_BLESS_DAMAGE, m->getSpellIndex()) / tier; - Bonus specialBonus(Bonus::N_TURNS, Bonus::CREATURE_DAMAGE, Bonus::SPELL_EFFECT, damagePercent, m->getSpellIndex(), 0, Bonus::PERCENT_TO_ALL); - specialBonus.turnsRemain = duration; - buffer.push_back(specialBonus); - } - - if(cumulative) - sse.toAdd.push_back(std::make_pair(affected->unitId(), buffer)); - else - sse.toUpdate.push_back(std::make_pair(affected->unitId(), buffer)); - } -} - void Timed::serializeJsonUnitEffect(JsonSerializeFormat & handler) { assert(!handler.saving); diff --git a/lib/spells/effects/Timed.h b/lib/spells/effects/Timed.h index d6a33543e..34bbde91c 100644 --- a/lib/spells/effects/Timed.h +++ b/lib/spells/effects/Timed.h @@ -30,7 +30,7 @@ public: Timed(); virtual ~Timed(); - void apply(BattleStateProxy * battleState, RNG & rng, const Mechanics * m, const EffectTarget & target) const override; + void apply(ServerCallback * server, const Mechanics * m, const EffectTarget & target) const override; protected: void serializeJsonUnitEffect(JsonSerializeFormat & handler) override final; @@ -38,7 +38,7 @@ protected: private: void convertBonus(const Mechanics * m, int32_t & duration, std::vector & converted) const; void describeEffect(std::vector & log, const Mechanics * m, const std::vector & bonuses, const battle::Unit * target) const; - void prepareEffects(SetStackEffect & sse, const Mechanics * m, const EffectTarget & target, bool describe) const; + }; } diff --git a/lib/spells/effects/UnitEffect.cpp b/lib/spells/effects/UnitEffect.cpp index 83daf36c5..583d86b24 100644 --- a/lib/spells/effects/UnitEffect.cpp +++ b/lib/spells/effects/UnitEffect.cpp @@ -51,7 +51,7 @@ bool UnitEffect::applicable(Problem & problem, const Mechanics * m) const auto mainFilter = std::bind(&UnitEffect::getStackFilter, this, m, true, _1); auto predicate = std::bind(&UnitEffect::eraseByImmunityFilter, this, m, _1); - auto targets = m->cb->battleGetUnitsIf(mainFilter); + auto targets = m->battle()->battleGetUnitsIf(mainFilter); vstd::erase_if(targets, predicate); if(targets.empty()) { @@ -123,7 +123,7 @@ EffectTarget UnitEffect::transformTargetByRange(const Mechanics * m, const Targe if(m->isMassive()) { //ignore spellTarget and add all stacks - auto units = m->cb->battleGetUnitsIf(mainFilter); + auto units = m->battle()->battleGetUnitsIf(mainFilter); for(auto unit : units) targets.insert(unit); } @@ -147,7 +147,7 @@ EffectTarget UnitEffect::transformTargetByRange(const Mechanics * m, const Targe return unit->coversPos(dest.hexValue) && mainFilter(unit); }; - auto units = m->cb->battleGetUnitsIf(predicate); + auto units = m->battle()->battleGetUnitsIf(predicate); for(auto unit : units) { @@ -209,7 +209,7 @@ EffectTarget UnitEffect::transformTargetByChain(const Mechanics * m, const Targe std::set possibleHexes; - auto possibleTargets = m->cb->battleGetUnitsIf([&](const battle::Unit * unit) -> bool + auto possibleTargets = m->battle()->battleGetUnitsIf([&](const battle::Unit * unit) -> bool { return isValidTarget(m, unit); }); @@ -225,7 +225,7 @@ EffectTarget UnitEffect::transformTargetByChain(const Mechanics * m, const Targe for(int32_t targetIndex = 0; targetIndex < chainLength; ++targetIndex) { - auto unit = m->cb->battleGetUnitByPos(destHex, true); + auto unit = m->battle()->battleGetUnitByPos(destHex, true); if(!unit) break; diff --git a/lib/vstd/StringUtils.cpp b/lib/vstd/StringUtils.cpp index 97c0faf59..e46779026 100644 --- a/lib/vstd/StringUtils.cpp +++ b/lib/vstd/StringUtils.cpp @@ -11,4 +11,22 @@ namespace vstd return result; } + DLL_LINKAGE std::pair splitStringToPair(std::string input, char separator) + { + std::pair ret; + size_t splitPos = input.find(separator); + + if (splitPos == std::string::npos) + { + ret.first.clear(); + ret.second = input; + } + else + { + ret.first = input.substr(0, splitPos); + ret.second = input.substr(splitPos + 1); + } + return ret; + } + } diff --git a/osx/CMakeLists.txt b/osx/CMakeLists.txt index 462504805..3a100e321 100644 --- a/osx/CMakeLists.txt +++ b/osx/CMakeLists.txt @@ -42,7 +42,7 @@ if(WIN32) file(COPY ${CMAKE_BINARY_DIR}/../../vcpkg/installed/x86-windows/plugins/platforms DESTINATION \${CMAKE_INSTALL_PREFIX}) ") endif() - else() #not vcpkg build - lines below do not work properly + else() #not appveyor build - lines below do not work properly install(CODE " execute_process( COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_BINARY_DIR}/bin/\${BUILD_TYPE}/bearer \${CMAKE_INSTALL_PREFIX}/bearer @@ -54,6 +54,17 @@ if(WIN32) endif() endif() + + #TODO: check if some equivalent of block below can be used for above block (easy qt dependencies copy) + if(ENABLE_LUA) + if(EXISTS ${LUA_INCLUDE_DIR}/../../bin/lua51.dll) + install(CODE " + file(COPY ${LUA_INCLUDE_DIR}/../../bin/lua51.dll DESTINATION \${CMAKE_INSTALL_PREFIX}) + ") + endif() + endif() + + #LuaJIT will not be copied automatically by not meeting criteria for this block of code install(CODE " if(\"\${BUILD_TYPE}\" STREQUAL \"Debug\") set(dirs \"${CMAKE_PREFIX_PATH}/debug/bin/\") diff --git a/scripting/erm/CMakeLists.txt b/scripting/erm/CMakeLists.txt index f0fb40748..0d58877a2 100644 --- a/scripting/erm/CMakeLists.txt +++ b/scripting/erm/CMakeLists.txt @@ -1,14 +1,16 @@ -include_directories(${CMAKE_HOME_DIRECTORY} ${CMAKE_HOME_DIRECTORY}/include ${CMAKE_CURRENT_SOURCE_DIRECTORY}) +include_directories(${Boost_INCLUDE_DIRS} ${CMAKE_HOME_DIRECTORY} ${CMAKE_HOME_DIRECTORY}/include ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_HOME_DIRECTORY}/lib) set(lib_SRCS StdInc.cpp - ERMParser.cpp - ERMInterpreter.cpp - ERMScriptModule.cpp + ERMParser.cpp + ERMInterpreter.cpp + ERMScriptModule.cpp ) add_library(vcmiERM SHARED ${lib_SRCS}) -target_link_libraries(vcmiERM ${Boost_LIBRARIES}) +target_link_libraries(vcmiERM ${Boost_LIBRARIES} vcmi) + +vcmi_set_output_dir(vcmiERM "scripting") set_target_properties(vcmiERM PROPERTIES ${PCH_PROPERTIES}) cotire(vcmiERM) diff --git a/scripting/erm/ERM.cbp b/scripting/erm/ERM.cbp index 8a141f142..bf75db9ed 100644 --- a/scripting/erm/ERM.cbp +++ b/scripting/erm/ERM.cbp @@ -8,7 +8,7 @@