From a9cd33fee31990ee50c91874328a7a6a6fa8395c Mon Sep 17 00:00:00 2001 From: afwbkbc Date: Tue, 5 Nov 2024 02:11:33 +0200 Subject: [PATCH 01/13] added universal events handling to wrappables, #system global object, state (wip) --- GLSMAC_data/default/main.gls.js | 245 +++++++++++------------ src/game/backend/CMakeLists.txt | 1 + src/game/backend/Game.cpp | 8 + src/game/backend/Game.h | 4 +- src/game/backend/State.cpp | 19 +- src/game/backend/State.h | 8 +- src/game/backend/System.cpp | 15 ++ src/game/backend/System.h | 17 ++ src/game/backend/base/Base.cpp | 2 +- src/game/backend/base/Pop.cpp | 2 +- src/game/backend/bindings/Animations.cpp | 6 +- src/game/backend/bindings/Bases.cpp | 4 +- src/game/backend/bindings/Binding.h | 2 - src/game/backend/bindings/Bindings.cpp | 20 +- src/game/backend/bindings/Bindings.h | 5 +- src/game/backend/bindings/Factions.cpp | 4 +- src/game/backend/bindings/Map.cpp | 10 +- src/game/backend/bindings/Random.cpp | 8 +- src/game/backend/bindings/Resources.cpp | 2 +- src/game/backend/bindings/Units.cpp | 26 +-- src/game/backend/map/tile/Tile.cpp | 2 +- src/game/backend/rules/Faction.cpp | 2 +- src/game/backend/slot/Slot.cpp | 2 +- src/game/backend/unit/Def.cpp | 2 +- src/game/backend/unit/StaticDef.cpp | 2 +- src/gse/Value.h | 46 ++++- src/gse/Wrappable.cpp | 51 +++++ src/gse/Wrappable.h | 22 +- src/gse/callable/Native.h | 7 +- src/gse/type/Object.cpp | 12 ++ src/gse/type/Object.h | 3 + src/types/Color.cpp | 2 +- 32 files changed, 387 insertions(+), 174 deletions(-) create mode 100644 src/game/backend/System.cpp create mode 100644 src/game/backend/System.h diff --git a/GLSMAC_data/default/main.gls.js b/GLSMAC_data/default/main.gls.js index 38827ec4..c6e038b2 100644 --- a/GLSMAC_data/default/main.gls.js +++ b/GLSMAC_data/default/main.gls.js @@ -4,149 +4,146 @@ const map = #include('map'); const units = #include('units'); const bases = #include('bases'); -#game.on.configure((e) => { +#system.on('configure', (e) => { factions.define(); - // TODO - const rules = #include('rules'); + // will be populated on start + let players = []; + let players_sz = 0; + let random_player = () => { + return players[(#game.random.get_int(0, players_sz - 1))]; + }; + + let random_morale = () => { + return #game.random.get_int(0, 6); // TODO: get some constants from api + }; + + let random_health = () => { + return #game.random.get_float(#to_float(0.1), #to_float(1)); + }; + + const pop_types = [ + 'Worker', + 'Talent', + 'Doctor', + 'Librarian', + ]; + + let add_pops = ( base, count ) => { + for (let i = 0; i < count; i++) { + const type = pop_types[(#game.random.get_int(0, #size(pop_types) - 1))]; + base.create_pop({ + type: type, + }); + } + }; -}); + units.init(); -// will be populated on start -let players = []; -let players_sz = 0; -let random_player = () => { - return players[(#game.random.get_int(0, players_sz - 1))]; -}; - -let random_morale = () => { - return #game.random.get_int(0, 6); // TODO: get some constants from api -}; - -let random_health = () => { - return #game.random.get_float(#to_float(0.1), #to_float(1)); -}; - -const pop_types = [ - 'Worker', - 'Talent', - 'Doctor', - 'Librarian', -]; - -let add_pops = ( base, count ) => { - for (let i = 0; i < count; i++) { - const type = pop_types[(#game.random.get_int(0, #size(pop_types) - 1))]; - base.create_pop({ - type: type, - }); - } -}; - -units.init(); - -let all_bases = []; - -#game.on.start((e) => { - - // init game data - resources.define(); - map.define(); - units.define(); - bases.define(); - - // init players - players = #game.players.get_all(); - players_sz = #size(players); - - // spawn units - - let units_spawned = 0; - let bases_spawned = 0; - - let w = #game.map.get_width(); - let h = #game.map.get_height(); - - for (let y = 0; y < h; y++) { - for (let x = 0; x < w; x++) { - if (x % 2 == y % 2) { - let owner = random_player(); - let tile = #game.map.get_tile(x, y); - if (#game.random.get_int(0, 6) == 0) { - let units_count = #game.random.get_int(1, 2); - for (let i = 0; i < units_count; i++) { - if (tile.is_land) { - if (#game.random.get_int(0, 4) != 0) { - #game.units.spawn('MindWorms', owner, tile, random_morale(), random_health()); - units_spawned++; - } else { - if (tile.has_fungus && #game.random.get_int(0, 3) == 0) { - // morale depends on count of fungus tiles around - let morale = 1; - for (neighbour of tile.get_surrounding_tiles()) { - if (morale >= 6) { - break; - } - if (neighbour.has_fungus) { - morale++; - } - } - #game.units.spawn('FungalTower', owner, tile, morale, random_health()); + let all_bases = []; + + #game.on.start((e) => { + + // init game data + resources.define(); + map.define(); + units.define(); + bases.define(); + + // init players + players = #game.players.get_all(); + players_sz = #size(players); + + // spawn units + + let units_spawned = 0; + let bases_spawned = 0; + + let w = #game.map.get_width(); + let h = #game.map.get_height(); + + for (let y = 0; y < h; y++) { + for (let x = 0; x < w; x++) { + if (x % 2 == y % 2) { + let owner = random_player(); + let tile = #game.map.get_tile(x, y); + if (#game.random.get_int(0, 6) == 0) { + let units_count = #game.random.get_int(1, 2); + for (let i = 0; i < units_count; i++) { + if (tile.is_land) { + if (#game.random.get_int(0, 4) != 0) { + #game.units.spawn('MindWorms', owner, tile, random_morale(), random_health()); units_spawned++; } else { - #game.units.spawn('SporeLauncher', owner, tile, random_morale(), random_health()); + if (tile.has_fungus && #game.random.get_int(0, 3) == 0) { + // morale depends on count of fungus tiles around + let morale = 1; + for (neighbour of tile.get_surrounding_tiles()) { + if (morale >= 6) { + break; + } + if (neighbour.has_fungus) { + morale++; + } + } + #game.units.spawn('FungalTower', owner, tile, morale, random_health()); + units_spawned++; + } else { + #game.units.spawn('SporeLauncher', owner, tile, random_morale(), random_health()); + units_spawned++; + } + } + } else { + if (#game.random.get_int(0, 3) == 0) { + #game.units.spawn('SeaLurk', owner, tile, random_morale(), random_health()); units_spawned++; } } - } else { - if (#game.random.get_int(0, 3) == 0) { - #game.units.spawn('SeaLurk', owner, tile, random_morale(), random_health()); - units_spawned++; - } - } - } - } - if (#game.random.get_int(0, 5) == 0) { - let has_adjactent_bases = false; - for (neighbour of tile.get_surrounding_tiles()) { - if (neighbour.get_base() != null) { - has_adjactent_bases = true; - break; } } - if (!has_adjactent_bases) { - let base = #game.bases.spawn( - owner, - tile, - { - // name: 'base name', + if (#game.random.get_int(0, 5) == 0) { + let has_adjactent_bases = false; + for (neighbour of tile.get_surrounding_tiles()) { + if (neighbour.get_base() != null) { + has_adjactent_bases = true; + break; } - ); - add_pops(base, #game.random.get_int(1, 7)); - all_bases []= base; - bases_spawned++; + } + if (!has_adjactent_bases) { + let base = #game.bases.spawn( + owner, + tile, + { + // name: 'base name', + } + ); + add_pops(base, #game.random.get_int(1, 7)); + all_bases []= base; + bases_spawned++; + } } } } } - } - #game.message('Total units spawned: ' + #to_string(units_spawned)); - #game.message('Total bases spawned: ' + #to_string(bases_spawned)); + #game.message('Total units spawned: ' + #to_string(units_spawned)); + #game.message('Total bases spawned: ' + #to_string(bases_spawned)); -}); + }); -#game.on.turn((e) => { - for ( base of all_bases ) { - add_pops(base, 1); - } - // -}); + #game.on.turn((e) => { + for ( base of all_bases ) { + add_pops(base, 1); + } + // + }); -#game.on.unit_spawn((e) => { - // -}); + #game.on.unit_spawn((e) => { + // + }); -#game.on.unit_despawn((e) => { - // + #game.on.unit_despawn((e) => { + // + }); + }); diff --git a/src/game/backend/CMakeLists.txt b/src/game/backend/CMakeLists.txt index 34974441..d6fbe103 100644 --- a/src/game/backend/CMakeLists.txt +++ b/src/game/backend/CMakeLists.txt @@ -13,6 +13,7 @@ SUBDIR( turn ) SET( SRC ${SRC} + ${PWD}/System.cpp ${PWD}/Game.cpp ${PWD}/State.cpp ${PWD}/Account.cpp diff --git a/src/game/backend/Game.cpp b/src/game/backend/Game.cpp index edde47aa..eb2ca7a5 100644 --- a/src/game/backend/Game.cpp +++ b/src/game/backend/Game.cpp @@ -449,6 +449,14 @@ const size_t Game::GetSlotNum() const { return m_slot_num; } +WRAPIMPL_BEGIN( Game, CLASS_GAME ) + WRAPIMPL_PROPS + + }; +WRAPIMPL_END_PTR( Game ) + +UNWRAPIMPL_PTR( Game ) + const MT_Response Game::ProcessRequest( const MT_Request& request, MT_CANCELABLE ) { MT_Response response = {}; response.op = request.op; diff --git a/src/game/backend/Game.h b/src/game/backend/Game.h index 3581f8b0..3846d5b5 100644 --- a/src/game/backend/Game.h +++ b/src/game/backend/Game.h @@ -262,7 +262,7 @@ class InvalidEvent : public types::Exception { InvalidEvent( const std::string& reason, const event::Event* event ); }; -CLASS( Game, MTModule ) +CLASS2( Game, MTModule, gse::Wrappable ) // returns success as soon as this thread is ready (not busy with previous requests) common::mt_id_t MT_Ping(); @@ -311,6 +311,8 @@ CLASS( Game, MTModule ) const Player* GetPlayer() const; const size_t GetSlotNum() const; + WRAPDEFS_PTR( Game ) + protected: const MT_Response ProcessRequest( const MT_Request& request, MT_CANCELABLE ) override; diff --git a/src/game/backend/State.cpp b/src/game/backend/State.cpp index a2eb6421..41b4aa0d 100644 --- a/src/game/backend/State.cpp +++ b/src/game/backend/State.cpp @@ -1,5 +1,6 @@ #include "State.h" +#include "System.h" #include "Game.h" #include "game/backend/connection/Connection.h" #include "game/backend/bindings/Bindings.h" @@ -11,7 +12,7 @@ namespace backend { State::State() : m_slots( new slot::Slots( this ) ) { - + NEW( m_system, System ); } State::~State() { @@ -20,6 +21,7 @@ State::~State() { delete m_bindings; } delete m_slots; + delete m_system; } void State::SetGame( Game* game ) { @@ -129,7 +131,8 @@ void State::Configure() { m_settings.global.game_rules.m_factions_order.clear(); // configure - m_bindings->Call( bindings::Bindings::CS_ON_CONFIGURE ); + m_bindings->Configure(); + //m_bindings->Call( bindings::Bindings::CS_ON_CONFIGURE ); // check ASSERT( !m_settings.global.game_rules.m_factions.empty(), "no factions were defined" ); @@ -163,5 +166,17 @@ void State::DetachConnection() { m_connection = nullptr; } +System* State::GetSystem() const { + return m_system; +} + +WRAPIMPL_BEGIN( State, CLASS_STATE ) + WRAPIMPL_PROPS + + }; +WRAPIMPL_END_PTR( State ) + +UNWRAPIMPL_PTR( State ) + } } diff --git a/src/game/backend/State.h b/src/game/backend/State.h index 52d70c9a..da0167d9 100644 --- a/src/game/backend/State.h +++ b/src/game/backend/State.h @@ -14,6 +14,7 @@ namespace game { namespace backend { +class System; class Game; class Player; @@ -27,7 +28,7 @@ namespace slot { class Slots; } -CLASS( State, common::Class ) +CLASS2( State, common::Class, gse::Wrappable ) State(); virtual ~State(); @@ -64,7 +65,12 @@ CLASS( State, common::Class ) void Reset(); void DetachConnection(); + System* GetSystem() const; + + WRAPDEFS_PTR( State ) + private: + System* m_system = nullptr; Game* m_game = nullptr; std::unordered_set< Player* > m_players = {}; // persistent diff --git a/src/game/backend/System.cpp b/src/game/backend/System.cpp new file mode 100644 index 00000000..634988cd --- /dev/null +++ b/src/game/backend/System.cpp @@ -0,0 +1,15 @@ +#include "System.h" + +namespace game { +namespace backend { + +WRAPIMPL_BEGIN( System, CLASS_SYSTEM ) + WRAPIMPL_PROPS + + }; +WRAPIMPL_END_PTR( System ) + +UNWRAPIMPL_PTR( System ) + +} +} diff --git a/src/game/backend/System.h b/src/game/backend/System.h new file mode 100644 index 00000000..3610b843 --- /dev/null +++ b/src/game/backend/System.h @@ -0,0 +1,17 @@ +#pragma once + +#include "gse/Wrappable.h" +#include "gse/type/Object.h" + +namespace game { +namespace backend { + +class System : public gse::Wrappable { +public: + + WRAPDEFS_PTR( System ) + +}; + +} +} diff --git a/src/game/backend/base/Base.cpp b/src/game/backend/base/Base.cpp index 54b8a304..5d56453a 100644 --- a/src/game/backend/base/Base.cpp +++ b/src/game/backend/base/Base.cpp @@ -106,7 +106,7 @@ WRAPIMPL_DYNAMIC_GETTERS( Base, CLASS_BASE ) N_GETPROP( poptype, data, "type", String ); auto* def = m_game->GetPopDef( poptype ); if ( !def ) { - ERROR( gse::EC.INVALID_DEFINITION, "Unknown pop type: " + poptype ); + GSE_ERROR( gse::EC.INVALID_DEFINITION, "Unknown pop type: " + poptype ); } const auto max_variants = (m_faction->m_flags & rules::Faction::FF_PROGENITOR) ? 1 // aliens have 1 gender diff --git a/src/game/backend/base/Pop.cpp b/src/game/backend/base/Pop.cpp index aab6094d..15d8d5dd 100644 --- a/src/game/backend/base/Pop.cpp +++ b/src/game/backend/base/Pop.cpp @@ -40,7 +40,7 @@ void Pop::Unserialize( types::Buffer& buf, Game* game ) { } WRAPIMPL_BEGIN( Pop, CLASS_BASE_POP ) - WRAPIMPL_PROPS { + WRAPIMPL_PROPS { "type", VALUE( gse::type::String, m_def->m_id ) diff --git a/src/game/backend/bindings/Animations.cpp b/src/game/backend/bindings/Animations.cpp index bac3f93a..a6c81152 100644 --- a/src/game/backend/bindings/Animations.cpp +++ b/src/game/backend/bindings/Animations.cpp @@ -45,7 +45,7 @@ BINDING_IMPL( animations ) { N_GETPROP( duration_ms, animation_def, "duration_ms", Int ); N_GETPROP( sound, animation_def, "sound", String ); if ( !g_engine->GetSoundLoader()->LoadCustomSound( sound ) ) { - ERROR( gse::EC.GAME_ERROR, "Failed to load animation sound '" + sound + "'" ); + GSE_ERROR( gse::EC.GAME_ERROR, "Failed to load animation sound '" + sound + "'" ); } auto* def = new animation::FramesRow( id, @@ -68,7 +68,7 @@ BINDING_IMPL( animations ) { return game->AddEvent( new event::DefineAnimation( game->GetSlotNum(), def ) ); } else { - ERROR( gse::EC.GAME_ERROR, "Unsupported animation type: " + type ); + GSE_ERROR( gse::EC.GAME_ERROR, "Unsupported animation type: " + type ); } }) }, @@ -84,7 +84,7 @@ BINDING_IMPL( animations ) { N_UNPERSIST_CALLABLE( on_complete ); }); if ( errmsg ) { - ERROR( gse::EC.GAME_ERROR, *errmsg ); + GSE_ERROR( gse::EC.GAME_ERROR, *errmsg ); delete errmsg; } return VALUE( gse::type::Undefined ); diff --git a/src/game/backend/bindings/Bases.cpp b/src/game/backend/bindings/Bases.cpp index 609b8bad..fd4caf0c 100644 --- a/src/game/backend/bindings/Bases.cpp +++ b/src/game/backend/bindings/Bases.cpp @@ -40,7 +40,7 @@ BINDING_IMPL( bases ) { out.reserve( renders.size() ); for ( const auto& v : renders ) { if ( v.Get()->type != gse::type::Type::T_OBJECT ) { - ERROR( gse::EC.INVALID_CALL, "Pop render elements must be objects" ); + GSE_ERROR( gse::EC.INVALID_CALL, "Pop render elements must be objects" ); } const auto* obj = (gse::type::Object*)v.Get(); const auto& ov = obj->value; @@ -62,7 +62,7 @@ BINDING_IMPL( bases ) { ); } else { - ERROR( gse::EC.INVALID_CALL, "Only sprite pops are supported for now" ); + GSE_ERROR( gse::EC.INVALID_CALL, "Only sprite pops are supported for now" ); } } diff --git a/src/game/backend/bindings/Binding.h b/src/game/backend/bindings/Binding.h index 5af7be5d..3fdbc94d 100644 --- a/src/game/backend/bindings/Binding.h +++ b/src/game/backend/bindings/Binding.h @@ -52,8 +52,6 @@ class Binding { #define GAME m_bindings->GetGame( ctx, call_si ) -#define ERROR( _type, _text ) throw gse::Exception( _type, _text, ctx, call_si ); - #define CALLBACK( _type ) NATIVE_CALL( this ) { \ N_EXPECT_ARGS( 1 ); \ N_GET( cb, 0 ); \ diff --git a/src/game/backend/bindings/Bindings.cpp b/src/game/backend/bindings/Bindings.cpp index 4b804600..fccb2550 100644 --- a/src/game/backend/bindings/Bindings.cpp +++ b/src/game/backend/bindings/Bindings.cpp @@ -1,5 +1,7 @@ #include "Bindings.h" +#include + #include "util/FS.h" #include "game/backend/Game.h" @@ -11,9 +13,11 @@ #include "gse/type/Object.h" #include "gse/type/Callable.h" #include "gse/type/Undefined.h" +#include "gse/callable/Native.h" #include "engine/Engine.h" #include "config/Config.h" #include "game/backend/State.h" +#include "game/backend/System.h" namespace game { namespace backend { @@ -67,6 +71,7 @@ void Bindings::AddToContext( gse::context::Context* ctx ) { it->Add( methods ); } ctx->CreateBuiltin( "game", VALUE( gse::type::Object, methods ) ); + ctx->CreateBuiltin( "system", m_state->GetSystem()->Wrap( true ) ); } void Bindings::RunMain() { @@ -105,14 +110,25 @@ gse::Value Bindings::Call( const callback_slot_t slot, const callback_arguments_ return VALUE( gse::type::Undefined ); } +void Bindings::Configure() { + m_state->GetSystem()->Trigger( + m_gse_context, m_si_internal, "configure", { + { + "state", + m_state->Wrap() + } + } + ); +} + State* Bindings::GetState() const { return m_state; } -Game* Bindings::GetGame( gse::context::Context* ctx, const gse::si_t& call_si ) const { +Game* Bindings::GetGame( GSE_CALLABLE ) const { auto* game = m_state->GetGame(); if ( !game ) { - ERROR( gse::EC.GAME_ERROR, "Game not started yet" ); + GSE_ERROR( gse::EC.GAME_ERROR, "Game not started yet" ); } return game; } diff --git a/src/game/backend/bindings/Bindings.h b/src/game/backend/bindings/Bindings.h index 60abc8fd..57aaa093 100644 --- a/src/game/backend/bindings/Bindings.h +++ b/src/game/backend/bindings/Bindings.h @@ -22,6 +22,7 @@ class GlobalContext; namespace game { namespace backend { +class System; class Game; class State; @@ -58,8 +59,10 @@ class Bindings : public gse::Bindings { typedef std::map< std::string, gse::Value > callback_arguments_t; gse::Value Call( const callback_slot_t slot, const callback_arguments_t& arguments = {} ); + void Configure(); + State* GetState() const; - Game* GetGame( gse::context::Context* ctx, const gse::si_t& call_si ) const; + Game* GetGame( GSE_CALLABLE ) const; void SetCallback( const callback_slot_t slot, const gse::Value& callback, gse::context::Context* context, const gse::si_t& si ); private: diff --git a/src/game/backend/bindings/Factions.cpp b/src/game/backend/bindings/Factions.cpp index 66463f03..13cd03d8 100644 --- a/src/game/backend/bindings/Factions.cpp +++ b/src/game/backend/bindings/Factions.cpp @@ -74,7 +74,7 @@ BINDING_IMPL( factions ) { N_EXPECT_ARGS( 2 ); N_GETVALUE( id, 0, String ); if ( factions.find( id ) != factions.end() ) { - ERROR( gse::EC.GAME_ERROR, "Faction '" + id + "' already exists" ); + GSE_ERROR( gse::EC.GAME_ERROR, "Faction '" + id + "' already exists" ); } N_GETVALUE( faction_def, 1, Object ); N_GETPROP( name, faction_def, "name", String ); @@ -129,7 +129,7 @@ BINDING_IMPL( factions ) { }; } else { - ERROR( gse::EC.GAME_ERROR, "Unsupported bases render type: " + bases_render_type ); + GSE_ERROR( gse::EC.GAME_ERROR, "Unsupported bases render type: " + bases_render_type ); } N_GETPROP( base_names, bases_def, "names", Object ); diff --git a/src/game/backend/bindings/Map.cpp b/src/game/backend/bindings/Map.cpp index 793c32cb..84a4060b 100644 --- a/src/game/backend/bindings/Map.cpp +++ b/src/game/backend/bindings/Map.cpp @@ -39,19 +39,19 @@ BINDING_IMPL( map ) { const auto w = m->GetWidth(); const auto h = m->GetHeight(); if ( x >= w ) { - ERROR( gse::EC.INVALID_CALL, "X coordinate exceeds map width ( " + std::to_string( x ) + " >= " + std::to_string( w ) + " )" ); + GSE_ERROR( gse::EC.INVALID_CALL, "X coordinate exceeds map width ( " + std::to_string( x ) + " >= " + std::to_string( w ) + " )" ); } if ( y >= h ) { - ERROR( gse::EC.INVALID_CALL, "Y coordinate exceeds map height ( " + std::to_string( y ) + " >= " + std::to_string( h ) + " )" ); + GSE_ERROR( gse::EC.INVALID_CALL, "Y coordinate exceeds map height ( " + std::to_string( y ) + " >= " + std::to_string( h ) + " )" ); } if ( x < 0 ) { - ERROR( gse::EC.INVALID_CALL, "X coordinate can't be negative ( " + std::to_string( x ) + " < 0" ); + GSE_ERROR( gse::EC.INVALID_CALL, "X coordinate can't be negative ( " + std::to_string( x ) + " < 0" ); } if ( y < 0 ) { - ERROR( gse::EC.INVALID_CALL, "Y coordinate can't be negative ( " + std::to_string( y ) + " < 0" ); + GSE_ERROR( gse::EC.INVALID_CALL, "Y coordinate can't be negative ( " + std::to_string( y ) + " < 0" ); } if ( x % 2 != y % 2 ) { - ERROR( gse::EC.INVALID_CALL, "X and Y oddity differs ( " + std::to_string( x ) + " % 2 != " + std::to_string( y ) + " % 2 )" ); + GSE_ERROR( gse::EC.INVALID_CALL, "X and Y oddity differs ( " + std::to_string( x ) + " % 2 != " + std::to_string( y ) + " % 2 )" ); } return m->GetTile( x, y )->Wrap(); }), diff --git a/src/game/backend/bindings/Random.cpp b/src/game/backend/bindings/Random.cpp index 194fa5b3..c49b49c3 100644 --- a/src/game/backend/bindings/Random.cpp +++ b/src/game/backend/bindings/Random.cpp @@ -23,10 +23,10 @@ BINDING_IMPL( random ) { N_GETVALUE( min, 0, Int ); N_GETVALUE( max, 1, Int ); if ( max < min ) { - ERROR( gse::EC.INVALID_CALL, "Maximum value is smaller than minimum ( " + std::to_string( max ) + " < " + std::to_string( min ) + " )" ); + GSE_ERROR( gse::EC.INVALID_CALL, "Maximum value is smaller than minimum ( " + std::to_string( max ) + " < " + std::to_string( min ) + " )" ); } if ( !GAME->GetState()->IsMaster() ) { - ERROR( gse::EC.INVALID_CALL, "Only master is allowed to generate random values" ); + GSE_ERROR( gse::EC.INVALID_CALL, "Only master is allowed to generate random values" ); } return VALUE( gse::type::Int, GAME->GetRandom()->GetInt64( min, max ) ); }) @@ -38,10 +38,10 @@ BINDING_IMPL( random ) { N_GETVALUE( min, 0, Float ); N_GETVALUE( max, 1, Float ); if ( max < min ) { - ERROR( gse::EC.INVALID_CALL, "Maximum value is smaller than minimum ( " + std::to_string( max ) + " < " + std::to_string( min ) + " )" ); + GSE_ERROR( gse::EC.INVALID_CALL, "Maximum value is smaller than minimum ( " + std::to_string( max ) + " < " + std::to_string( min ) + " )" ); } if ( !GAME->GetState()->IsMaster() ) { - ERROR( gse::EC.INVALID_CALL, "Only master is allowed to generate random values" ); + GSE_ERROR( gse::EC.INVALID_CALL, "Only master is allowed to generate random values" ); } return VALUE( gse::type::Float, GAME->GetRandom()->GetFloat( min, max ) ); }) diff --git a/src/game/backend/bindings/Resources.cpp b/src/game/backend/bindings/Resources.cpp index 1f1c6d6b..0734e425 100644 --- a/src/game/backend/bindings/Resources.cpp +++ b/src/game/backend/bindings/Resources.cpp @@ -71,7 +71,7 @@ BINDING_IMPL( resources ) { return game->AddEvent( new event::DefineResource( game->GetSlotNum(), resource ) ); } else { - ERROR( gse::EC.GAME_ERROR, "Unsupported resource type: " + type ); + GSE_ERROR( gse::EC.GAME_ERROR, "Unsupported resource type: " + type ); } return VALUE( gse::type::Undefined ); diff --git a/src/game/backend/bindings/Units.cpp b/src/game/backend/bindings/Units.cpp index 326f2eb0..df433ec8 100644 --- a/src/game/backend/bindings/Units.cpp +++ b/src/game/backend/bindings/Units.cpp @@ -24,19 +24,19 @@ namespace game { namespace backend { namespace bindings { -const unit::morale_t GetMorale( const int64_t& morale, gse::context::Context* ctx, const gse::si_t& call_si ) { +const unit::morale_t GetMorale( GSE_CALLABLE, const int64_t& morale ) { if ( morale < unit::MORALE_MIN || morale > unit::MORALE_MAX ) { - ERROR( gse::EC.INVALID_CALL, "Invalid morale value: " + std::to_string( morale ) + " (should be between " + std::to_string( unit::MORALE_MIN ) + " and " + std::to_string( unit::MORALE_MAX ) + ", inclusive)" ); + GSE_ERROR( gse::EC.INVALID_CALL, "Invalid morale value: " + std::to_string( morale ) + " (should be between " + std::to_string( unit::MORALE_MIN ) + " and " + std::to_string( unit::MORALE_MAX ) + ", inclusive)" ); } return (unit::morale_t)morale; } -const unit::health_t GetHealth( const float health, gse::context::Context* ctx, const gse::si_t& call_si ) { +const unit::health_t GetHealth( GSE_CALLABLE, const float health ) { if ( health < unit::Unit::MINIMUM_HEALTH_TO_KEEP || health > unit::StaticDef::HEALTH_MAX ) { - ERROR( gse::EC.INVALID_CALL, "Invalid health value: " + std::to_string( health ) + " (should be between " + std::to_string( unit::Unit::MINIMUM_HEALTH_TO_KEEP ) + " and " + std::to_string( unit::StaticDef::HEALTH_MAX ) + ", inclusive)" ); + GSE_ERROR( gse::EC.INVALID_CALL, "Invalid health value: " + std::to_string( health ) + " (should be between " + std::to_string( unit::Unit::MINIMUM_HEALTH_TO_KEEP ) + " and " + std::to_string( unit::StaticDef::HEALTH_MAX ) + ", inclusive)" ); } if ( health == 0 ) { - ERROR( gse::EC.INVALID_CALL, "Invalid health value: " + std::to_string( health ) + " (you can't spawn a dead unit)" ); + GSE_ERROR( gse::EC.INVALID_CALL, "Invalid health value: " + std::to_string( health ) + " (you can't spawn a dead unit)" ); } return (unit::health_t)health; } @@ -51,12 +51,12 @@ BINDING_IMPL( units ) { N_GETVALUE( arr, 1, Array ); const uint8_t expected_count = unit::MORALE_MAX - unit::MORALE_MIN + 1; if ( arr.size() != expected_count ) { - ERROR( gse::EC.INVALID_CALL, "Morale set must have exactly " + std::to_string( expected_count ) + " values (found " + std::to_string( arr.size() ) + ")"); + GSE_ERROR( gse::EC.INVALID_CALL, "Morale set must have exactly " + std::to_string( expected_count ) + " values (found " + std::to_string( arr.size() ) + ")"); } unit::MoraleSet::morale_values_t values = {}; for ( const auto& v : arr ) { if ( v.Get()->type != gse::type::Type::T_OBJECT ) { - ERROR( gse::EC.INVALID_CALL, "Morale set elements must be objects"); + GSE_ERROR( gse::EC.INVALID_CALL, "Morale set elements must be objects"); } const auto* obj = (gse::type::Object*)v.Get(); N_GETPROP( name, obj->value, "name", String ); @@ -91,7 +91,7 @@ BINDING_IMPL( units ) { movement_type = unit::MT_IMMOVABLE; } else { - ERROR( gse::EC.INVALID_CALL, "Invalid movement type: " + movement_type_str + ". Specify one of: land water air immovable"); + GSE_ERROR( gse::EC.INVALID_CALL, "Invalid movement type: " + movement_type_str + ". Specify one of: land water air immovable"); } N_GETPROP( movement_per_turn, unit_def, "movement_per_turn", Int ); N_GETPROP( render_def, unit_def, "render", Object ); @@ -108,7 +108,7 @@ BINDING_IMPL( units ) { auto* game = GAME; const auto* moraleset = game->GetMoraleSet( morale ); if ( !moraleset ) { - ERROR( gse::EC.INVALID_CALL, "Morale type '" + morale + "' is not defined"); + GSE_ERROR( gse::EC.INVALID_CALL, "Morale type '" + morale + "' is not defined"); } auto* def = new unit::StaticDef( id, @@ -130,11 +130,11 @@ BINDING_IMPL( units ) { return game->AddEvent( new event::DefineUnit( game->GetSlotNum(), def ) ); } else { - ERROR( gse::EC.GAME_ERROR, "Unsupported render type: " + render_type ); + GSE_ERROR( gse::EC.GAME_ERROR, "Unsupported render type: " + render_type ); } } else { - ERROR( gse::EC.GAME_ERROR, "Unsupported unit type: " + unit_type ); + GSE_ERROR( gse::EC.GAME_ERROR, "Unsupported unit type: " + unit_type ); } }) }, @@ -154,8 +154,8 @@ BINDING_IMPL( units ) { owner->GetIndex(), tile->coord.x, tile->coord.y, - GetMorale( morale, ctx, call_si ), - GetHealth( health, ctx, call_si ) + GetMorale( ctx, call_si, morale ), + GetHealth( ctx, call_si, health ) ) ); }) }, diff --git a/src/game/backend/map/tile/Tile.cpp b/src/game/backend/map/tile/Tile.cpp index a91cfa7d..f53e75ad 100644 --- a/src/game/backend/map/tile/Tile.cpp +++ b/src/game/backend/map/tile/Tile.cpp @@ -167,7 +167,7 @@ const std::string Tile::ToString() const { } WRAPIMPL_BEGIN( Tile, CLASS_TILE ) - WRAPIMPL_PROPS { + WRAPIMPL_PROPS { "x", VALUE( gse::type::Int, coord.x ) diff --git a/src/game/backend/rules/Faction.cpp b/src/game/backend/rules/Faction.cpp index 9205676a..a19729c5 100644 --- a/src/game/backend/rules/Faction.cpp +++ b/src/game/backend/rules/Faction.cpp @@ -68,7 +68,7 @@ void Faction::Unserialize( types::Buffer buf ) { } WRAPIMPL_BEGIN( Faction, CLASS_FACTION ) - WRAPIMPL_PROPS { + WRAPIMPL_PROPS { "id", VALUE( gse::type::String, m_id ) diff --git a/src/game/backend/slot/Slot.cpp b/src/game/backend/slot/Slot.cpp index 281af2bd..5616274f 100644 --- a/src/game/backend/slot/Slot.cpp +++ b/src/game/backend/slot/Slot.cpp @@ -164,7 +164,7 @@ void Slot::Unserialize( types::Buffer buf ) { WRAPIMPL_BEGIN( Slot, CLASS_PLAYER ) ASSERT_NOLOG( m_slot_state == SS_PLAYER, "only player slots can be wrapped for now" ); auto* player = m_player_data.player; - WRAPIMPL_PROPS { + WRAPIMPL_PROPS { "id", VALUE( gse::type::Int, m_player_data.cid ) diff --git a/src/game/backend/unit/Def.cpp b/src/game/backend/unit/Def.cpp index 2407b291..dde2a004 100644 --- a/src/game/backend/unit/Def.cpp +++ b/src/game/backend/unit/Def.cpp @@ -51,7 +51,7 @@ Def* Def::Unserialize( types::Buffer& buf ) { } WRAPIMPL_BEGIN( Def, CLASS_UNITDEF ) - WRAPIMPL_PROPS { + WRAPIMPL_PROPS { "name", VALUE( gse::type::String, m_name ) diff --git a/src/game/backend/unit/StaticDef.cpp b/src/game/backend/unit/StaticDef.cpp index 134c4940..db395d69 100644 --- a/src/game/backend/unit/StaticDef.cpp +++ b/src/game/backend/unit/StaticDef.cpp @@ -83,7 +83,7 @@ StaticDef* StaticDef::Unserialize( types::Buffer& buf, const std::string& id, co } WRAPIMPL_BEGIN( StaticDef, CLASS_UNITDEF ) - WRAPIMPL_PROPS { + WRAPIMPL_PROPS WRAPIMPL_GET( "is_immovable", Bool, m_movement_type == MT_IMMOVABLE ) WRAPIMPL_GET( "is_land", Bool, m_movement_type == MT_LAND ) WRAPIMPL_GET( "is_water", Bool, m_movement_type == MT_WATER ) diff --git a/src/gse/Value.h b/src/gse/Value.h index f4001ba6..ef162210 100644 --- a/src/gse/Value.h +++ b/src/gse/Value.h @@ -8,6 +8,10 @@ namespace gse { +#define GSE_ERROR( _type, _text ) throw gse::Exception( _type, _text, ctx, call_si ); + +#define GSE_CALLABLE gse::context::Context* ctx, const gse::si_t& call_si + #define VALUE( _type, ... ) gse::Value( std::make_shared<_type>( __VA_ARGS__ ) ) #ifdef DEBUG #define VALUE_DATA( _type, _var ) ( _var.Get()->type == _type::GetType() ? ((_type*)_var.Get()) : THROW( "invalid GSE value type (expected " + type::Type::GetTypeString( _type::GetType() ) + ", got " + type::Type::GetTypeString( _var.Get()->type ) + ")" ) ) @@ -45,7 +49,47 @@ namespace gse { #define WRAPIMPL_DYNAMIC_GETTERS( _type, _class ) \ WRAPIMPL_DYNAMIC_BEGIN( _type, _class ) \ const gse::type::object_properties_t properties = { -#define WRAPIMPL_PROPS gse::type::object_properties_t properties = +#define WRAPIMPL_PROPS gse::type::object_properties_t properties = { \ + { \ + "on", \ + NATIVE_CALL( this ) { \ + N_EXPECT_ARGS( 2 ); \ + N_GETVALUE( event, 0, String ); \ + N_GET( cb, 1 ); \ + N_CHECKARG( cb.Get(), 1, Callable ); \ + return VALUE( gse::type::Int, On( ctx, call_si, event, cb ) ); \ + } ) \ + }, \ + { \ + "off", \ + NATIVE_CALL( this ) { \ + N_EXPECT_ARGS_MIN_MAX( 1, 2 ); \ + N_GETVALUE( event, 0, String ); \ + if ( arguments.size() == 2 ) { \ + N_GETVALUE( handler_id, 1, Int ); \ + Off( ctx, call_si, event, handler_id ); \ + } \ + else { \ + Off( ctx, call_si, event, 0 ); \ + } \ + return VALUE( gse::type::Undefined ); \ + } ) \ + }, \ + { \ + "trigger", \ + NATIVE_CALL( this ) { \ + N_EXPECT_ARGS_MIN_MAX( 1, 2 ); \ + N_GETVALUE( event, 0, String ); \ + if ( arguments.size() == 2 ) { \ + N_GETVALUE( args, 1, Object ); \ + return Trigger( ctx, call_si, event, args ); \ + } \ + else { \ + return Trigger( ctx, call_si, event, {} ); \ + } \ + } ) \ + }, + #define WRAPIMPL_PROPS_EXTEND( _parent ) \ const auto wrapped_parent = _parent::Wrap(); \ const auto& wrapped_parent_props = ( (gse::type::Object*)wrapped_parent.Get() )->value; \ diff --git a/src/gse/Wrappable.cpp b/src/gse/Wrappable.cpp index 3164a9ac..6bbf37df 100644 --- a/src/gse/Wrappable.cpp +++ b/src/gse/Wrappable.cpp @@ -20,4 +20,55 @@ void Wrappable::Unlink( type::Object* wrapobj ) { m_wrapobjs.erase( wrapobj ); } +const Wrappable::callback_id_t Wrappable::On( GSE_CALLABLE, const std::string& event, const gse::Value& callback ) { + ASSERT_NOLOG( callback.Get()->type == type::Type::T_CALLABLE, "callback not callable" ); + auto it = m_callbacks.find( event ); + if ( it == m_callbacks.end() ) { + it = m_callbacks.insert( + { + event, + {} + } + ).first; + } + return it->second.insert( + { + ++m_next_callback_id, + callback + } + ).first->first; +} + +void Wrappable::Off( GSE_CALLABLE, const std::string& event, const callback_id_t callback_id ) { + const auto& it = m_callbacks.find( event ); + if ( it != m_callbacks.end() ) { + if ( callback_id ) { + // delete one handler + const auto& it2 = it->second.find( callback_id ); + if ( it2 != it->second.end() ) { + it->second.erase( it2 ); + } + else { + GSE_ERROR( gse::EC.INVALID_CALL, "Callback [" + event + "/" + std::to_string( callback_id ) + "] not found" ); + } + } + else { + // delete all handlers + m_callbacks.erase( it ); + } + } +} + +const Value Wrappable::Trigger( GSE_CALLABLE, const std::string& event, const type::object_properties_t& args ) { + const auto& it = m_callbacks.find( event ); + if ( it != m_callbacks.end() ) { + auto e = VALUE( gse::type::Object, args ); + for ( const auto& it2 : it->second ) { + ASSERT_NOLOG( it2.second.Get()->type == type::Type::T_CALLABLE, "callback not callable" ); + ( (type::Callable*)it2.second.Get() )->Run( ctx, call_si, { e } ); + } + } + return VALUE( gse::type::Undefined ); +} + } diff --git a/src/gse/Wrappable.h b/src/gse/Wrappable.h index 5f8ad482..eeaa0653 100644 --- a/src/gse/Wrappable.h +++ b/src/gse/Wrappable.h @@ -1,8 +1,16 @@ #pragma once #include +#include +#include -#include "gse/Value.h" +#include "type/Types.h" +#include "type/Int.h" +#include "type/String.h" +#include "type/Undefined.h" +#include "callable/Native.h" +#include "Exception.h" +#include "Value.h" namespace gse { @@ -13,11 +21,23 @@ class Object; class Wrappable { public: virtual ~Wrappable(); + virtual const gse::Value Wrap( const bool dynamic = false ) = 0; + void Link( type::Object* wrapobj ); void Unlink( type::Object* wrapobj ); + + typedef uint16_t callback_id_t; + const callback_id_t On( GSE_CALLABLE, const std::string& event, const gse::Value& callback ); + void Off( GSE_CALLABLE, const std::string& event, const callback_id_t callback_id ); + const Value Trigger( GSE_CALLABLE, const std::string& event, const type::object_properties_t& args ); + private: std::unordered_set< type::Object* > m_wrapobjs = {}; + + typedef std::unordered_map< std::string, std::map< uint16_t, gse::Value > > callbacks_t; + callbacks_t m_callbacks = {}; + callback_id_t m_next_callback_id = 0; }; } diff --git a/src/gse/callable/Native.h b/src/gse/callable/Native.h index 1c8ec497..a6966a7b 100644 --- a/src/gse/callable/Native.h +++ b/src/gse/callable/Native.h @@ -8,7 +8,7 @@ namespace gse { namespace callable { -#define NATIVE_CALL( ... ) VALUE( gse::callable::Native, [ __VA_ARGS__ ]( gse::context::Context* ctx, const gse::si_t& call_si, const gse::type::function_arguments_t& arguments ) -> gse::Value +#define NATIVE_CALL( ... ) VALUE( gse::callable::Native, [ __VA_ARGS__ ]( GSE_CALLABLE, const gse::type::function_arguments_t& arguments ) -> gse::Value // TODO: refactor these #define N_ARGS \ @@ -22,6 +22,11 @@ namespace callable { throw gse::Exception( gse::EC.INVALID_CALL, "Expected " + std::to_string( _count ) + " arguments, found " + std::to_string( arguments.size() ), ctx, call_si ); \ } \ N_ARGS +#define N_EXPECT_ARGS_MIN( _min ) \ + if ( arguments.size() < _min ) { \ + throw gse::Exception( gse::EC.INVALID_CALL, "Expected at least " + std::to_string( _min ) + " arguments, found " + std::to_string( arguments.size() ), ctx, call_si ); \ + } \ + N_ARGS #define N_EXPECT_ARGS_MIN_MAX( _min, _max ) \ if ( arguments.size() < _min || arguments.size() > _max ) { \ throw gse::Exception( gse::EC.INVALID_CALL, "Expected " + std::to_string( _min ) + " to " + std::to_string( _max ) + " arguments, found " + std::to_string( arguments.size() ), ctx, call_si ); \ diff --git a/src/gse/type/Object.cpp b/src/gse/type/Object.cpp index 343303a4..446e31ae 100644 --- a/src/gse/type/Object.cpp +++ b/src/gse/type/Object.cpp @@ -27,6 +27,18 @@ static const std::unordered_map< Object::object_class_t, std::string > s_object_ Object::CLASS_COLOR, "#color" }, + { + Object::CLASS_SYSTEM, + "#system" + }, + { + Object::CLASS_STATE, + "#state" + }, + { + Object::CLASS_GAME, + "#game" + }, { Object::CLASS_TILE, "#tile" diff --git a/src/gse/type/Object.h b/src/gse/type/Object.h index 0ed4ea80..a88f6446 100644 --- a/src/gse/type/Object.h +++ b/src/gse/type/Object.h @@ -28,6 +28,9 @@ class Object : public Type { CLASS_NONE, CLASS_EXCEPTION, CLASS_COLOR, + CLASS_SYSTEM, + CLASS_STATE, + CLASS_GAME, CLASS_TILE, CLASS_PLAYER, CLASS_FACTION, diff --git a/src/types/Color.cpp b/src/types/Color.cpp index 26360e01..258a97fa 100644 --- a/src/types/Color.cpp +++ b/src/types/Color.cpp @@ -147,7 +147,7 @@ const std::string Color::ToString() const { } WRAPIMPL_BEGIN( Color, CLASS_COLOR ) - WRAPIMPL_PROPS { + WRAPIMPL_PROPS { "r", VALUE( gse::type::Float, value.red ) From eacca894508b6c09f1bd18a82b4626a8c57b42bc Mon Sep 17 00:00:00 2001 From: afwbkbc Date: Tue, 5 Nov 2024 20:07:42 +0200 Subject: [PATCH 02/13] changed factions interface --- GLSMAC_data/default/factions.gls.js | 4 +- GLSMAC_data/default/main.gls.js | 8 +- GLSMAC_data/default/units.gls.js | 2 +- src/game/FrontendRequest.h | 4 +- src/game/backend/CMakeLists.txt | 2 + src/game/backend/{rules => }/Faction.cpp | 4 +- src/game/backend/{rules => }/Faction.h | 4 +- src/game/backend/Factions.cpp | 180 ++++++++++++++++++ src/game/backend/Factions.h | 40 ++++ src/game/backend/Game.cpp | 28 +-- src/game/backend/Game.h | 2 +- src/game/backend/Player.cpp | 12 +- src/game/backend/Player.h | 12 +- src/game/backend/State.cpp | 37 +++- src/game/backend/State.h | 6 + src/game/backend/Types.h | 13 ++ src/game/backend/base/Base.cpp | 5 +- src/game/backend/base/Base.h | 6 +- src/game/backend/bindings/Factions.cpp | 89 +-------- src/game/backend/connection/Server.cpp | 3 +- src/game/backend/event/SpawnBase.cpp | 2 +- src/game/backend/rules/CMakeLists.txt | 1 - src/game/backend/rules/Rules.cpp | 16 -- src/game/backend/rules/Rules.h | 5 - src/game/backend/rules/Types.h | 25 --- src/game/frontend/faction/Faction.cpp | 6 +- src/game/frontend/faction/Faction.h | 12 +- src/game/frontend/faction/FactionManager.cpp | 4 +- src/game/frontend/faction/FactionManager.h | 7 +- src/gse/type/Object.cpp | 4 + src/gse/type/Object.h | 1 + src/main.cpp | 13 +- src/task/mainmenu/menu/lobby/Lobby.cpp | 4 + src/task/mainmenu/menu/lobby/Lobby.h | 1 + .../mainmenu/menu/lobby/PlayersSection.cpp | 11 +- .../mainmenu/menu/lobby/PlayersSectionRow.cpp | 18 +- 36 files changed, 367 insertions(+), 224 deletions(-) rename src/game/backend/{rules => }/Faction.cpp (94%) rename src/game/backend/{rules => }/Faction.h (94%) create mode 100644 src/game/backend/Factions.cpp create mode 100644 src/game/backend/Factions.h delete mode 100644 src/game/backend/rules/Types.h diff --git a/GLSMAC_data/default/factions.gls.js b/GLSMAC_data/default/factions.gls.js index 425d3c04..4dba4f85 100644 --- a/GLSMAC_data/default/factions.gls.js +++ b/GLSMAC_data/default/factions.gls.js @@ -1,6 +1,6 @@ return { - define: () => { + configure: (factions) => { for (faction of [ 'gaians', 'hive', @@ -17,7 +17,7 @@ return { 'caretakers', 'usurpers' ]) { - #game.factions.define(#to_uppercase(faction), #include('./factions/' + faction)); + factions.add(#to_uppercase(faction), #include('./factions/' + faction)); } }, diff --git a/GLSMAC_data/default/main.gls.js b/GLSMAC_data/default/main.gls.js index c6e038b2..f87e53a0 100644 --- a/GLSMAC_data/default/main.gls.js +++ b/GLSMAC_data/default/main.gls.js @@ -6,7 +6,9 @@ const bases = #include('bases'); #system.on('configure', (e) => { - factions.define(); + factions.configure(e.state.factions); + + units.configure(); // will be populated on start let players = []; @@ -39,8 +41,6 @@ const bases = #include('bases'); } }; - units.init(); - let all_bases = []; #game.on.start((e) => { @@ -145,5 +145,5 @@ const bases = #include('bases'); #game.on.unit_despawn((e) => { // }); - + }); diff --git a/GLSMAC_data/default/units.gls.js b/GLSMAC_data/default/units.gls.js index 720e3381..4504a7d0 100644 --- a/GLSMAC_data/default/units.gls.js +++ b/GLSMAC_data/default/units.gls.js @@ -6,7 +6,7 @@ const animations = #include('units/animations'); const result = { - init: () => { + configure: () => { movement.init(); combat.init(animations); turns.init(); diff --git a/src/game/FrontendRequest.h b/src/game/FrontendRequest.h index 427f109e..1cd3dfd7 100644 --- a/src/game/FrontendRequest.h +++ b/src/game/FrontendRequest.h @@ -14,10 +14,8 @@ namespace map::tile { class Tile; class TileState; } -namespace rules { class Faction; } -} class FrontendRequest { public: @@ -50,7 +48,7 @@ class FrontendRequest { const request_type_t type = FR_NONE; - typedef std::vector< const backend::rules::Faction* > faction_defines_t; + typedef std::vector< const backend::Faction* > faction_defines_t; struct slot_define_t { size_t slot_index; diff --git a/src/game/backend/CMakeLists.txt b/src/game/backend/CMakeLists.txt index d6fbe103..7163ea33 100644 --- a/src/game/backend/CMakeLists.txt +++ b/src/game/backend/CMakeLists.txt @@ -16,6 +16,8 @@ SET( SRC ${SRC} ${PWD}/System.cpp ${PWD}/Game.cpp ${PWD}/State.cpp + ${PWD}/Faction.cpp + ${PWD}/Factions.cpp ${PWD}/Account.cpp ${PWD}/Player.cpp ${PWD}/MapObject.cpp diff --git a/src/game/backend/rules/Faction.cpp b/src/game/backend/Faction.cpp similarity index 94% rename from src/game/backend/rules/Faction.cpp rename to src/game/backend/Faction.cpp index a19729c5..f27b10a2 100644 --- a/src/game/backend/rules/Faction.cpp +++ b/src/game/backend/Faction.cpp @@ -9,7 +9,6 @@ namespace game { namespace backend { -namespace rules { Faction::Faction() { // @@ -79,7 +78,7 @@ WRAPIMPL_BEGIN( Faction, CLASS_FACTION ) }, { "is_progenitor", - VALUE( gse::type::Bool, ( m_flags & rules::Faction::FF_PROGENITOR ) == rules::Faction::FF_PROGENITOR ) + VALUE( gse::type::Bool, ( m_flags & Faction::FF_PROGENITOR ) == Faction::FF_PROGENITOR ) }, }; WRAPIMPL_END_PTR( Faction ) @@ -88,4 +87,3 @@ UNWRAPIMPL_PTR( Faction ) } } -} diff --git a/src/game/backend/rules/Faction.h b/src/game/backend/Faction.h similarity index 94% rename from src/game/backend/rules/Faction.h rename to src/game/backend/Faction.h index f937ed38..24bdbf4e 100644 --- a/src/game/backend/rules/Faction.h +++ b/src/game/backend/Faction.h @@ -11,7 +11,6 @@ namespace game { namespace backend { -namespace rules { CLASS2( Faction, types::Serializable, gse::Wrappable ) @@ -38,7 +37,7 @@ CLASS2( Faction, types::Serializable, gse::Wrappable ) std::vector< std::string > water = {}; } m_base_names = {}; - bases_render_info_t m_bases_render = {}; + faction_bases_render_info_t m_bases_render = {}; const types::Buffer Serialize() const override; void Unserialize( types::Buffer buf ) override; @@ -49,4 +48,3 @@ CLASS2( Faction, types::Serializable, gse::Wrappable ) } } -} diff --git a/src/game/backend/Factions.cpp b/src/game/backend/Factions.cpp new file mode 100644 index 00000000..8c866695 --- /dev/null +++ b/src/game/backend/Factions.cpp @@ -0,0 +1,180 @@ +#include "Factions.h" + +#include "Faction.h" + +#include "gse/callable/Native.h" +#include "gse/type/Bool.h" +#include "gse/type/Float.h" +#include "gse/type/Array.h" + +namespace game { +namespace backend { + +Factions::Factions() { + // +} + +Factions::~Factions() { + Clear(); +} + +void Factions::Add( Faction* faction ) { + Remove( faction->m_id ); + m_factions.insert( { faction->m_id, { faction, ++m_next_faction_idx } } ); + m_factions_order.insert({ m_next_faction_idx, faction->m_id }); +} + +void Factions::Remove( const std::string& id ) { + const auto& it = m_factions.find( id ); + if ( it != m_factions.end() ) { + delete it->second.faction; + m_factions_order.erase( it->second.index ); + m_factions.erase( it ); + } +} + +void Factions::Clear() { + for ( const auto& it : m_factions ) { + delete it.second.faction; + } + m_factions.clear(); + m_factions_order.clear(); + m_next_faction_idx = 0; +} + +Faction* Factions::Get( const std::string& id ) const { + const auto& it = m_factions.find( id ); + if ( it != m_factions.end() ) { + return it->second.faction; + } + return nullptr; +} + +const std::vector< Faction* > Factions::GetAll() const { + ASSERT_NOLOG( m_factions.size() == m_factions_order.size(), "factions order size mismatch" ); + std::vector< Faction* > result = {}; + result.reserve( m_factions.size() ); + for ( const auto& it : m_factions_order ) { + ASSERT_NOLOG( m_factions.find( it.second ) != m_factions.end(), "faction not found" ); + result.push_back( m_factions.at( it.second ).faction ); + } + return result; +} + +WRAPIMPL_BEGIN( Factions, CLASS_FACTIONS ) + WRAPIMPL_PROPS + { + "add", + NATIVE_CALL( this ) { + N_EXPECT_ARGS( 2 ); + N_GETVALUE( id, 0, String ); + + N_GETVALUE( faction_def, 1, Object ); + N_GETPROP( name, faction_def, "name", String ); + + auto* faction = new Faction( id, name ); + + N_GETPROP( colors, faction_def, "colors", Object ); + N_GETPROP_UNWRAP( colors_text, colors, "text", types::Color ); + N_GETPROP_UNWRAP( colors_text_shadow, colors, "text_shadow", types::Color ); + N_GETPROP_UNWRAP( colors_border, colors, "border", types::Color ); + faction->m_colors = { + colors_text, + colors_text_shadow, + colors_border + }; + + N_GETPROP_OPT_BOOL( is_naval, faction_def, "is_naval") + if ( is_naval ) { + faction->m_flags |= Faction::FF_NAVAL; + } + N_GETPROP_OPT_BOOL( is_progenitor, faction_def, "is_progenitor") + if ( is_progenitor ) { + faction->m_flags |= Faction::FF_PROGENITOR; + } + + N_GETPROP( bases_def, faction_def, "bases", Object ); + N_GETPROP( bases_render_def, bases_def, "render", Object ); + + N_GETPROP( bases_render_type, bases_render_def, "type", String ); + if ( bases_render_type == "sprite_grid" ) { + N_GETPROP( file, bases_render_def, "file", String ); + N_GETPROP( grid_x, bases_render_def, "grid_x", Int ); + N_GETPROP( grid_y, bases_render_def, "grid_y", Int ); + N_GETPROP( cell_width, bases_render_def, "cell_width", Int ); + N_GETPROP( cell_height, bases_render_def, "cell_height", Int ); + N_GETPROP_OPT( size_t, cell_cx, bases_render_def, "cell_cx", Int, cell_width / 2 ); + N_GETPROP_OPT( size_t, cell_cy, bases_render_def, "cell_cy", Int, cell_height / 2 ); + N_GETPROP( cell_padding, bases_render_def, "cell_padding", Int ); + N_GETPROP_OPT( float, scale_x, bases_render_def, "scale_x", Float, 1.0f ); + N_GETPROP_OPT( float, scale_y, bases_render_def, "scale_y", Float, 1.0f ); + faction->m_bases_render = { + file, + (size_t)grid_x, + (size_t)grid_y, + (size_t)cell_width, + (size_t)cell_height, + cell_cx, + cell_cy, + (size_t)cell_padding, + scale_x, + scale_y + }; + } + else { + delete faction; + GSE_ERROR( gse::EC.GAME_ERROR, "Unsupported bases render type: " + bases_render_type ); + } + + N_GETPROP( base_names, bases_def, "names", Object ); + N_GETPROP( base_names_land, base_names, "land", Array ); + faction->m_base_names.land.reserve( base_names_land.size() ); + for ( size_t i = 0 ; i < base_names_land.size() ; i++ ) { + N_GETELEMENT( v, base_names_land, i, String ); + faction->m_base_names.land.push_back( v ); + } + + N_GETPROP( base_names_water, base_names, "water", Array ); + faction->m_base_names.water.reserve( base_names_water.size() ); + for ( size_t i = 0 ; i < base_names_water.size() ; i++ ) { + N_GETELEMENT( v, base_names_water, i, String ); + faction->m_base_names.water.push_back( v ); + } + + Add( faction ); + return faction->Wrap(); + } ) + }, + }; +WRAPIMPL_END_PTR( Factions ) + +UNWRAPIMPL_PTR( Factions ) + +const types::Buffer Factions::Serialize() const { + types::Buffer buf; + + buf.WriteInt( m_factions_order.size() ); + for ( auto& it : m_factions_order ) { + const auto* faction = m_factions.at( it.second ).faction; + buf.WriteString( it.second ); + buf.WriteString( faction->Serialize().ToString() ); + } + + return buf; +} + +void Factions::Unserialize( types::Buffer buf ) { + Clear(); + + const size_t factions_count = buf.ReadInt(); + for ( size_t i = 0 ; i < factions_count ; i++ ) { + const auto faction_id = buf.ReadString(); + ASSERT_NOLOG( m_factions.find( faction_id ) == m_factions.end(), "duplicate faction id" ); + m_factions.insert({ faction_id, { new Faction(), ++m_next_faction_idx }}).first->second.faction->Unserialize( buf.ReadString() ); + m_factions_order.insert( { m_next_faction_idx, faction_id } ); + } +} + + +} +} diff --git a/src/game/backend/Factions.h b/src/game/backend/Factions.h new file mode 100644 index 00000000..b36c0d89 --- /dev/null +++ b/src/game/backend/Factions.h @@ -0,0 +1,40 @@ +#pragma once + +#include "gse/Wrappable.h" +#include "gse/type/Object.h" + +namespace game { +namespace backend { + +class Faction; + +class Factions : public gse::Wrappable { +public: + Factions(); + ~Factions(); + + void Add( Faction* faction ); + void Remove( const std::string& id ); + void Clear(); + Faction* Get( const std::string& id ) const; + const std::vector< Faction* > GetAll() const; + + WRAPDEFS_PTR( Factions ) + + const types::Buffer Serialize() const; + void Unserialize( types::Buffer buf ); + +private: + struct faction_t { + Faction* faction = {}; + size_t index = 0; + }; + typedef std::unordered_map< std::string, faction_t > factions_t; + factions_t m_factions = {}; + std::map< size_t, std::string > m_factions_order = {}; + size_t m_next_faction_idx = 0; + +}; + +} +} diff --git a/src/game/backend/Game.cpp b/src/game/backend/Game.cpp index eb2ca7a5..a88f2574 100644 --- a/src/game/backend/Game.cpp +++ b/src/game/backend/Game.cpp @@ -8,6 +8,7 @@ #include "util/FS.h" #include "types/Buffer.h" #include "State.h" +#include "Factions.h" #include "map_editor/MapEditor.h" #include "event/FinalizeTurn.h" #include "event/TurnFinalized.h" @@ -329,10 +330,10 @@ void Game::Iterate() { } { - const auto& factions = m_state->m_settings.global.game_rules.m_factions; + const auto factions = m_state->GetFactions()->GetAll(); auto* faction_defines = new FrontendRequest::faction_defines_t(); - for ( const auto& it : factions ) { - faction_defines->push_back( &it.second ); + for ( const auto& faction : factions ) { + faction_defines->push_back( faction ); } auto fr = FrontendRequest( FrontendRequest::FR_FACTION_DEFINE ); fr.data.faction_define.factiondefs = faction_defines; @@ -1538,10 +1539,10 @@ void Game::UnlockTiles( const size_t initiator_slot, const map::tile::positions_ } } -rules::Faction* Game::GetFaction( const std::string& id ) const { - const auto& it = m_state->m_settings.global.game_rules.m_factions.find( id ); // TODO: store factions in Game itself? - ASSERT( it != m_state->m_settings.global.game_rules.m_factions.end(), "faction not found: " + id ); - return &it->second; +Faction* Game::GetFaction( const std::string& id ) const { + auto* faction = m_state->GetFactions()->Get( id ); // TODO: store factions in Game itself? + ASSERT( faction, "faction not found: " + id ); + return faction; } void Game::ValidateEvent( event::Event* event ) { @@ -1772,22 +1773,23 @@ void Game::InitGame( MT_Response& response, MT_CANCELABLE ) { if ( m_state->IsMaster() ) { // assign random factions to players - const auto& factions = m_state->m_settings.global.game_rules.m_factions; - std::vector< std::string > m_available_factions = {}; + auto factions = m_state->GetFactions()->GetAll(); + std::vector< size_t > m_available_factions = {}; const auto& slots = m_state->m_slots->GetSlots(); for ( const auto& slot : slots ) { if ( slot.GetState() == slot::Slot::SS_PLAYER ) { auto* player = slot.GetPlayer(); ASSERT( player, "player not set" ); - if ( !player->GetFaction().has_value() ) { + if ( !player->GetFaction() ) { if ( m_available_factions.empty() ) { // (re)load factions list - for ( const auto& it : factions ) { - m_available_factions.push_back( it.first ); + for ( size_t i = 0 ; i < factions.size() ; i++ ) { + //for ( const auto& faction : factions ) { + m_available_factions.push_back( i ); } ASSERT( !m_available_factions.empty(), "no factions found" ); } - const std::vector< std::string >::iterator it = m_available_factions.begin() + m_random->GetUInt( 0, m_available_factions.size() - 1 ); + const auto it = m_available_factions.begin() + m_random->GetUInt( 0, m_available_factions.size() - 1 ); player->SetFaction( factions.at( *it ) ); m_available_factions.erase( it ); } diff --git a/src/game/backend/Game.h b/src/game/backend/Game.h index 3846d5b5..4c32cc49 100644 --- a/src/game/backend/Game.h +++ b/src/game/backend/Game.h @@ -373,7 +373,7 @@ CLASS2( Game, MTModule, gse::Wrappable ) void RequestTileUnlocks( const size_t initiator_slot, const map::tile::positions_t& tile_positions ); void UnlockTiles( const size_t initiator_slot, const map::tile::positions_t& tile_positions ); - rules::Faction* GetFaction( const std::string& id ) const; + Faction* GetFaction( const std::string& id ) const; private: diff --git a/src/game/backend/Player.cpp b/src/game/backend/Player.cpp index 20fd778a..d30cc9c5 100644 --- a/src/game/backend/Player.cpp +++ b/src/game/backend/Player.cpp @@ -10,7 +10,7 @@ Player::Player( types::Buffer buf ) { Player::Player( const std::string& name, const role_t role, - const std::optional< rules::Faction >& faction, + Faction* faction, const rules::DifficultyLevel& difficulty_level ) : m_name( name ) @@ -42,7 +42,7 @@ const bool Player::IsConnected() const { return m_is_connected; } -void Player::SetFaction( const rules::Faction& faction ) { +void Player::SetFaction( Faction* faction ) { // TODO: validate? m_faction = faction; } @@ -51,7 +51,7 @@ void Player::ClearFaction() { m_faction = {}; } -std::optional< rules::Faction >& Player::GetFaction() { +Faction* Player::GetFaction() { return m_faction; } @@ -93,8 +93,8 @@ const types::Buffer Player::Serialize() const { buf.WriteString( m_name ); buf.WriteInt( m_role ); - buf.WriteBool( m_faction.has_value() ); - if ( m_faction.has_value() ) { + buf.WriteBool( m_faction != nullptr ); + if ( m_faction ) { buf.WriteString( m_faction->Serialize().ToString() ); } buf.WriteString( m_difficulty_level.Serialize().ToString() ); @@ -108,7 +108,7 @@ void Player::Unserialize( types::Buffer buf ) { m_role = (role_t)buf.ReadInt(); m_faction = {}; if ( buf.ReadBool() ) { - m_faction = rules::Faction{}; + m_faction = new Faction(); m_faction->Unserialize( buf.ReadString() ); } m_difficulty_level.Unserialize( buf.ReadString() ); diff --git a/src/game/backend/Player.h b/src/game/backend/Player.h index c3620d00..111f00bb 100644 --- a/src/game/backend/Player.h +++ b/src/game/backend/Player.h @@ -1,11 +1,10 @@ #pragma once #include -#include #include "types/Serializable.h" -#include "game/backend/rules/Faction.h" +#include "Faction.h" #include "game/backend/rules/DifficultyLevel.h" namespace game { @@ -28,7 +27,7 @@ CLASS( Player, types::Serializable ) Player( const std::string& name, const role_t role, - const std::optional< rules::Faction >& faction, + Faction* faction, const rules::DifficultyLevel& difficulty_level ); @@ -39,9 +38,9 @@ CLASS( Player, types::Serializable ) void Disconnect(); const bool IsConnected() const; - void SetFaction( const rules::Faction& faction ); + void SetFaction( Faction* faction ); void ClearFaction(); - std::optional< rules::Faction >& GetFaction(); + Faction* GetFaction(); void SetDifficultyLevel( const rules::DifficultyLevel& difficulty_level ); const rules::DifficultyLevel& GetDifficultyLevel() const; @@ -67,8 +66,7 @@ CLASS( Player, types::Serializable ) slot::Slot* m_slot = nullptr; - // EXPERIMENTAL - std::optional< rules::Faction > m_faction = {}; + Faction* m_faction = {}; rules::DifficultyLevel m_difficulty_level = {}; bool m_is_turn_completed = false; diff --git a/src/game/backend/State.cpp b/src/game/backend/State.cpp index 41b4aa0d..e9b69b20 100644 --- a/src/game/backend/State.cpp +++ b/src/game/backend/State.cpp @@ -1,6 +1,7 @@ #include "State.h" #include "System.h" +#include "Factions.h" #include "Game.h" #include "game/backend/connection/Connection.h" #include "game/backend/bindings/Bindings.h" @@ -13,6 +14,7 @@ namespace backend { State::State() : m_slots( new slot::Slots( this ) ) { NEW( m_system, System ); + NEW( m_factions, Factions ); } State::~State() { @@ -21,7 +23,8 @@ State::~State() { delete m_bindings; } delete m_slots; - delete m_system; + DELETE( m_system ); + DELETE( m_factions ); } void State::SetGame( Game* game ) { @@ -127,16 +130,15 @@ void State::Configure() { Log( "Configuring state" ); // reset - m_settings.global.game_rules.m_factions.clear(); - m_settings.global.game_rules.m_factions_order.clear(); + m_factions->Clear(); // configure m_bindings->Configure(); - //m_bindings->Call( bindings::Bindings::CS_ON_CONFIGURE ); // check - ASSERT( !m_settings.global.game_rules.m_factions.empty(), "no factions were defined" ); - ASSERT( m_settings.global.game_rules.m_factions_order.size() == m_settings.global.game_rules.m_factions.size(), "factions order size mismatch" ); + if ( m_factions->GetAll().empty() ) { + THROW( "no factions were defined" ); + } } void State::Reset() { @@ -170,13 +172,34 @@ System* State::GetSystem() const { return m_system; } +Factions* State::GetFactions() const { + return m_factions; +} + WRAPIMPL_BEGIN( State, CLASS_STATE ) WRAPIMPL_PROPS - + { + "factions", + m_factions->Wrap( true ) + } }; WRAPIMPL_END_PTR( State ) UNWRAPIMPL_PTR( State ) +const types::Buffer State::Serialize() const { + types::Buffer buf; + + buf.WriteString( m_settings.global.Serialize().ToString() ); + buf.WriteString( m_factions->Serialize().ToString() ); + + return buf; +} + +void State::Unserialize( types::Buffer buf ) { + m_settings.global.Unserialize( buf.ReadString() ); + m_factions->Unserialize( buf.ReadString() ); +} + } } diff --git a/src/game/backend/State.h b/src/game/backend/State.h index da0167d9..686fd6b0 100644 --- a/src/game/backend/State.h +++ b/src/game/backend/State.h @@ -15,6 +15,7 @@ namespace game { namespace backend { class System; +class Factions; class Game; class Player; @@ -66,11 +67,16 @@ CLASS2( State, common::Class, gse::Wrappable ) void DetachConnection(); System* GetSystem() const; + Factions* GetFactions() const; WRAPDEFS_PTR( State ) + const types::Buffer Serialize() const; + void Unserialize( types::Buffer buf ); + private: System* m_system = nullptr; + Factions* m_factions = nullptr; Game* m_game = nullptr; std::unordered_set< Player* > m_players = {}; // persistent diff --git a/src/game/backend/Types.h b/src/game/backend/Types.h index c2209ee2..84d46d3b 100644 --- a/src/game/backend/Types.h +++ b/src/game/backend/Types.h @@ -11,5 +11,18 @@ enum tile_query_purpose_t { TQP_OBJECT_SELECT, // unit or base }; +struct faction_bases_render_info_t { + std::string file = ""; + size_t grid_x = 0; + size_t grid_y = 0; + size_t cell_width = 0; + size_t cell_height = 0; + size_t cell_cx = 0; + size_t cell_cy = 0; + size_t cell_padding = 0; + float scale_x = 1.0f; + float scale_y = 1.0f; +}; + } } diff --git a/src/game/backend/base/Base.cpp b/src/game/backend/base/Base.cpp index 5d56453a..ed8c2e24 100644 --- a/src/game/backend/base/Base.cpp +++ b/src/game/backend/base/Base.cpp @@ -11,6 +11,7 @@ #include "game/backend/slot/Slot.h" #include "game/backend/slot/Slots.h" #include "game/backend/map/Map.h" +#include "game/backend/Faction.h" #include "Pop.h" #include "game/backend/bindings/Binding.h" #include "PopDef.h" @@ -32,7 +33,7 @@ Base::Base( Game* game, const size_t id, slot::Slot* owner, - rules::Faction* faction, + Faction* faction, map::tile::Tile* tile, const std::string& name, const pops_t& pops @@ -108,7 +109,7 @@ WRAPIMPL_DYNAMIC_GETTERS( Base, CLASS_BASE ) if ( !def ) { GSE_ERROR( gse::EC.INVALID_DEFINITION, "Unknown pop type: " + poptype ); } - const auto max_variants = (m_faction->m_flags & rules::Faction::FF_PROGENITOR) + const auto max_variants = (m_faction->m_flags & Faction::FF_PROGENITOR) ? 1 // aliens have 1 gender : 2; // humans have 2 ASSERT_NOLOG( max_variants > 0, "no variants found for pop type: " + poptype ); diff --git a/src/game/backend/base/Base.h b/src/game/backend/base/Base.h index 1829db45..5cae1ef8 100644 --- a/src/game/backend/base/Base.h +++ b/src/game/backend/base/Base.h @@ -17,9 +17,7 @@ class Game; namespace slot { class Slot; } -namespace rules { class Faction; -} namespace map::tile { class Tile; } @@ -38,7 +36,7 @@ class Base : public gse::Wrappable, public MapObject { Game* game, const size_t id, slot::Slot* owner, - rules::Faction* faction, // faction may differ from owner's faction in some cases, i.e. after being conquered + Faction* faction, // faction may differ from owner's faction in some cases, i.e. after being conquered map::tile::Tile* tile, const std::string& name, const pops_t& pops @@ -49,7 +47,7 @@ class Base : public gse::Wrappable, public MapObject { const size_t m_id; slot::Slot* m_owner; - rules::Faction* m_faction; + Faction* m_faction; std::string m_name; pops_t m_pops; diff --git a/src/game/backend/bindings/Factions.cpp b/src/game/backend/bindings/Factions.cpp index 13cd03d8..16bcff9c 100644 --- a/src/game/backend/bindings/Factions.cpp +++ b/src/game/backend/bindings/Factions.cpp @@ -16,6 +16,8 @@ #include "loader/txt/FactionTXTLoader.h" #include "loader/texture/TextureLoader.h" #include "types/texture/Texture.h" +#include "game/backend/Faction.h" +#include "game/backend/Factions.h" #include "types/Color.h" @@ -24,8 +26,7 @@ namespace backend { namespace bindings { BINDING_IMPL( factions ) { - auto& factions = m_bindings->GetState()->m_settings.global.game_rules.m_factions; - auto& factions_order = m_bindings->GetState()->m_settings.global.game_rules.m_factions_order; + auto* factions = m_bindings->GetState()->GetFactions(); const gse::type::object_properties_t properties = { { "import_base_names", @@ -68,90 +69,6 @@ BINDING_IMPL( factions ) { return VALUE( gse::type::Object, properties ); } ) }, - { - "define", - NATIVE_CALL( &factions, &factions_order ) { - N_EXPECT_ARGS( 2 ); - N_GETVALUE( id, 0, String ); - if ( factions.find( id ) != factions.end() ) { - GSE_ERROR( gse::EC.GAME_ERROR, "Faction '" + id + "' already exists" ); - } - N_GETVALUE( faction_def, 1, Object ); - N_GETPROP( name, faction_def, "name", String ); - - rules::Faction faction = { id, name }; - - N_GETPROP( colors, faction_def, "colors", Object ); - N_GETPROP_UNWRAP( colors_text, colors, "text", types::Color ); - N_GETPROP_UNWRAP( colors_text_shadow, colors, "text_shadow", types::Color ); - N_GETPROP_UNWRAP( colors_border, colors, "border", types::Color ); - faction.m_colors = { - colors_text, - colors_text_shadow, - colors_border - }; - - N_GETPROP_OPT_BOOL( is_naval, faction_def, "is_naval") - if ( is_naval ) { - faction.m_flags |= rules::Faction::FF_NAVAL; - } - N_GETPROP_OPT_BOOL( is_progenitor, faction_def, "is_progenitor") - if ( is_progenitor ) { - faction.m_flags |= rules::Faction::FF_PROGENITOR; - } - - N_GETPROP( bases_def, faction_def, "bases", Object ); - N_GETPROP( bases_render_def, bases_def, "render", Object ); - - N_GETPROP( bases_render_type, bases_render_def, "type", String ); - if ( bases_render_type == "sprite_grid" ) { - N_GETPROP( file, bases_render_def, "file", String ); - N_GETPROP( grid_x, bases_render_def, "grid_x", Int ); - N_GETPROP( grid_y, bases_render_def, "grid_y", Int ); - N_GETPROP( cell_width, bases_render_def, "cell_width", Int ); - N_GETPROP( cell_height, bases_render_def, "cell_height", Int ); - N_GETPROP_OPT( size_t, cell_cx, bases_render_def, "cell_cx", Int, cell_width / 2 ); - N_GETPROP_OPT( size_t, cell_cy, bases_render_def, "cell_cy", Int, cell_height / 2 ); - N_GETPROP( cell_padding, bases_render_def, "cell_padding", Int ); - N_GETPROP_OPT( float, scale_x, bases_render_def, "scale_x", Float, 1.0f ); - N_GETPROP_OPT( float, scale_y, bases_render_def, "scale_y", Float, 1.0f ); - faction.m_bases_render = { - file, - (size_t)grid_x, - (size_t)grid_y, - (size_t)cell_width, - (size_t)cell_height, - cell_cx, - cell_cy, - (size_t)cell_padding, - scale_x, - scale_y - }; - } - else { - GSE_ERROR( gse::EC.GAME_ERROR, "Unsupported bases render type: " + bases_render_type ); - } - - N_GETPROP( base_names, bases_def, "names", Object ); - N_GETPROP( base_names_land, base_names, "land", Array ); - faction.m_base_names.land.reserve( base_names_land.size() ); - for ( size_t i = 0 ; i < base_names_land.size() ; i++ ) { - N_GETELEMENT( v, base_names_land, i, String ); - faction.m_base_names.land.push_back( v ); - } - - N_GETPROP( base_names_water, base_names, "water", Array ); - faction.m_base_names.water.reserve( base_names_water.size() ); - for ( size_t i = 0 ; i < base_names_water.size() ; i++ ) { - N_GETELEMENT( v, base_names_water, i, String ); - faction.m_base_names.water.push_back( v ); - } - - factions.insert({ id, faction }); - factions_order.push_back( id ); - return VALUE( gse::type::Undefined ); - }) - }, }; return VALUE( gse::type::Object, properties ); } diff --git a/src/game/backend/connection/Server.cpp b/src/game/backend/connection/Server.cpp index 5db5c218..48f5c4af 100644 --- a/src/game/backend/connection/Server.cpp +++ b/src/game/backend/connection/Server.cpp @@ -7,6 +7,7 @@ #include "game/backend/State.h" #include "game/backend/slot/Slots.h" #include "game/backend/Player.h" +#include "game/backend/Factions.h" namespace game { namespace backend { @@ -529,7 +530,7 @@ void Server::Error( const network::cid_t cid, const std::string& reason ) { void Server::SendGlobalSettings( network::cid_t cid ) { Log( "Sending global settings to " + std::to_string( cid ) ); types::Packet p( types::Packet::PT_GLOBAL_SETTINGS ); - p.data.str = m_state->m_settings.global.Serialize().ToString(); + p.data.str = m_state->Serialize().ToString(); m_network->MT_SendPacket( &p, cid ); } diff --git a/src/game/backend/event/SpawnBase.cpp b/src/game/backend/event/SpawnBase.cpp index 73b3ad7a..13878205 100644 --- a/src/game/backend/event/SpawnBase.cpp +++ b/src/game/backend/event/SpawnBase.cpp @@ -46,7 +46,7 @@ const gse::Value SpawnBase::Apply( Game* game ) const { game, base::Base::GetNextId(), &owner, - &owner.GetPlayer()->GetFaction().value(), + owner.GetPlayer()->GetFaction(), tile, m_name, {} // will be added later diff --git a/src/game/backend/rules/CMakeLists.txt b/src/game/backend/rules/CMakeLists.txt index 6c7c3098..554c4e7c 100644 --- a/src/game/backend/rules/CMakeLists.txt +++ b/src/game/backend/rules/CMakeLists.txt @@ -3,7 +3,6 @@ SUBDIR( default ) SET( SRC ${SRC} ${PWD}/Rules.cpp - ${PWD}/Faction.cpp ${PWD}/DifficultyLevel.cpp PARENT_SCOPE ) diff --git a/src/game/backend/rules/Rules.cpp b/src/game/backend/rules/Rules.cpp index 9f5bb997..836ea092 100644 --- a/src/game/backend/rules/Rules.cpp +++ b/src/game/backend/rules/Rules.cpp @@ -14,13 +14,6 @@ void Rules::Initialize() { const types::Buffer Rules::Serialize() const { types::Buffer buf; - buf.WriteInt( m_factions_order.size() ); - for ( auto& id : m_factions_order ) { - const auto& faction = m_factions.at( id ); - buf.WriteString( id ); - buf.WriteString( faction.Serialize().ToString() ); - } - buf.WriteInt( m_difficulty_levels.size() ); for ( auto& difficulty_level : m_difficulty_levels ) { buf.WriteInt( difficulty_level.first ); @@ -32,15 +25,6 @@ const types::Buffer Rules::Serialize() const { void Rules::Unserialize( types::Buffer buf ) { - m_factions.clear(); - m_factions_order.clear(); - const size_t factions_count = buf.ReadInt(); - for ( size_t i = 0 ; i < factions_count ; i++ ) { - const auto faction_id = buf.ReadString(); - m_factions[ faction_id ].Unserialize( buf.ReadString() ); - m_factions_order.push_back( faction_id ); - } - m_difficulty_levels.clear(); const size_t difficulty_levels_count = buf.ReadInt(); for ( size_t i = 0 ; i < difficulty_levels_count ; i++ ) { diff --git a/src/game/backend/rules/Rules.h b/src/game/backend/rules/Rules.h index 0d457aac..778f80cf 100644 --- a/src/game/backend/rules/Rules.h +++ b/src/game/backend/rules/Rules.h @@ -5,7 +5,6 @@ #include "types/Serializable.h" -#include "Faction.h" #include "DifficultyLevel.h" namespace game { @@ -14,10 +13,6 @@ namespace rules { CLASS( Rules, types::Serializable ) - typedef std::unordered_map< std::string, Faction > factions_t; - - factions_t m_factions = {}; - std::vector< std::string > m_factions_order = {}; std::map< size_t, DifficultyLevel > m_difficulty_levels; virtual const DifficultyLevel& GetDefaultDifficultyLevel() const = 0; diff --git a/src/game/backend/rules/Types.h b/src/game/backend/rules/Types.h deleted file mode 100644 index 888dce14..00000000 --- a/src/game/backend/rules/Types.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -#include -#include - -namespace game { -namespace backend { -namespace rules { - -struct bases_render_info_t { - std::string file = ""; - size_t grid_x = 0; - size_t grid_y = 0; - size_t cell_width = 0; - size_t cell_height = 0; - size_t cell_cx = 0; - size_t cell_cy = 0; - size_t cell_padding = 0; - float scale_x = 1.0f; - float scale_y = 1.0f; -}; - -} -} -} diff --git a/src/game/frontend/faction/Faction.cpp b/src/game/frontend/faction/Faction.cpp index 0b0efb2f..6c9d5619 100644 --- a/src/game/frontend/faction/Faction.cpp +++ b/src/game/frontend/faction/Faction.cpp @@ -1,6 +1,6 @@ #include "Faction.h" -#include "game/backend/rules/Faction.h" +#include "game/backend/Faction.h" #include "game/frontend/sprite/InstancedSpriteManager.h" #include "engine/Engine.h" #include "loader/texture/TextureLoader.h" @@ -11,7 +11,7 @@ namespace game { namespace frontend { namespace faction { -Faction::Faction( const backend::rules::Faction* def, sprite::InstancedSpriteManager* ism ) +Faction::Faction( const backend::Faction* def, sprite::InstancedSpriteManager* ism ) : m_ism( ism ) , m_id( def->m_id ) , m_colors( @@ -27,7 +27,7 @@ Faction::Faction( const backend::rules::Faction* def, sprite::InstancedSpriteMan def->m_base_names.water, } ) - , m_is_progenitor( def->m_flags & backend::rules::Faction::FF_PROGENITOR ) + , m_is_progenitor( def->m_flags & backend::Faction::FF_PROGENITOR ) , m_render( { def->m_bases_render, diff --git a/src/game/frontend/faction/Faction.h b/src/game/frontend/faction/Faction.h index 749d9194..0d251cc0 100644 --- a/src/game/frontend/faction/Faction.h +++ b/src/game/frontend/faction/Faction.h @@ -4,8 +4,7 @@ #include #include -#include "game/backend/rules/Types.h" - +#include "game/backend/Types.h" #include "types/Color.h" #include "game/frontend/sprite/Sprite.h" @@ -15,11 +14,12 @@ class Texture; } } -namespace game::backend::rules { +namespace game { + +namespace backend { class Faction; } -namespace game { namespace frontend { namespace sprite { @@ -31,7 +31,7 @@ namespace faction { class Faction { public: - Faction( const backend::rules::Faction* def, sprite::InstancedSpriteManager* ism ); + Faction( const backend::Faction* def, sprite::InstancedSpriteManager* ism ); sprite::Sprite* GetBaseSprite( const bool is_water, const uint8_t size, const uint8_t perimeter_level ); @@ -55,7 +55,7 @@ class Faction { sprite::InstancedSpriteManager* m_ism = nullptr; struct { - backend::rules::bases_render_info_t bases_render; + backend::faction_bases_render_info_t bases_render; } m_render = {}; types::texture::Texture* m_base_grid_texture = nullptr; diff --git a/src/game/frontend/faction/FactionManager.cpp b/src/game/frontend/faction/FactionManager.cpp index f7b561fe..3e1c6a51 100644 --- a/src/game/frontend/faction/FactionManager.cpp +++ b/src/game/frontend/faction/FactionManager.cpp @@ -1,7 +1,7 @@ #include "FactionManager.h" #include "game/frontend/Game.h" -#include "game/backend/rules/Faction.h" +#include "game/backend/Faction.h" #include "Faction.h" namespace game { @@ -13,7 +13,7 @@ FactionManager::FactionManager( Game* game ) } -void FactionManager::DefineFaction( const backend::rules::Faction* def ) { +void FactionManager::DefineFaction( const backend::Faction* def ) { ASSERT( def, "faction is null" ); ASSERT( m_factions.find( def->m_id ) == m_factions.end(), "faction already defined" ); NEWV( faction, Faction, def, m_game->GetISM() ); diff --git a/src/game/frontend/faction/FactionManager.h b/src/game/frontend/faction/FactionManager.h index 861fcb0e..906cb16f 100644 --- a/src/game/frontend/faction/FactionManager.h +++ b/src/game/frontend/faction/FactionManager.h @@ -4,11 +4,12 @@ #include "common/Common.h" -namespace game::backend::rules { +namespace game { + +namespace backend { class Faction; } -namespace game { namespace frontend { class Game; @@ -21,7 +22,7 @@ CLASS( FactionManager, common::Class ) FactionManager( Game* game ); - void DefineFaction( const backend::rules::Faction* def ); + void DefineFaction( const backend::Faction* def ); Faction* GetFactionById( const std::string& id ) const; diff --git a/src/gse/type/Object.cpp b/src/gse/type/Object.cpp index 446e31ae..5df04f80 100644 --- a/src/gse/type/Object.cpp +++ b/src/gse/type/Object.cpp @@ -51,6 +51,10 @@ static const std::unordered_map< Object::object_class_t, std::string > s_object_ Object::CLASS_FACTION, "#faction" }, + { + Object::CLASS_FACTIONS, + "#factions" + }, { Object::CLASS_UNITDEF, "#unitdef" diff --git a/src/gse/type/Object.h b/src/gse/type/Object.h index a88f6446..1d43fe2f 100644 --- a/src/gse/type/Object.h +++ b/src/gse/type/Object.h @@ -34,6 +34,7 @@ class Object : public Type { CLASS_TILE, CLASS_PLAYER, CLASS_FACTION, + CLASS_FACTIONS, CLASS_UNITDEF, CLASS_UNIT, CLASS_BASE, diff --git a/src/main.cpp b/src/main.cpp index b319ef68..af998d43 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -64,6 +64,7 @@ #include "version.h" #include "game/backend/State.h" +#include "game/backend/Factions.h" #include "game/backend/Player.h" #include "game/backend/slot/Slots.h" @@ -281,18 +282,16 @@ int main( const int argc, const char* argv[] ) { state->InitBindings(); state->Configure(); const auto& rules = state->m_settings.global.game_rules; - std::optional< game::backend::rules::Faction > faction = {}; + game::backend::Faction* faction = nullptr; if ( config.HasLaunchFlag( config::Config::LF_QUICKSTART_FACTION ) ) { - const auto& f = state->m_settings.global.game_rules.m_factions; - const auto it = f.find( config.GetQuickstartFaction() ); - if ( it == f.end() ) { + faction = state->GetFactions()->Get( config.GetQuickstartFaction() ); + if ( !faction ) { std::string errmsg = "Faction \"" + config.GetQuickstartFaction() + "\" does not exist. Available factions:"; - for ( const auto& f_it : f ) { - errmsg += " " + f_it.second.m_id; + for ( const auto& f : state->GetFactions()->GetAll() ) { + errmsg += " " + f->m_id; } THROW( errmsg ); } - faction = it->second; } NEWV( player, game::backend::Player, diff --git a/src/task/mainmenu/menu/lobby/Lobby.cpp b/src/task/mainmenu/menu/lobby/Lobby.cpp index 49e0656e..0b426026 100644 --- a/src/task/mainmenu/menu/lobby/Lobby.cpp +++ b/src/task/mainmenu/menu/lobby/Lobby.cpp @@ -247,6 +247,10 @@ const ::game::backend::Player* Lobby::GetPlayer() { return connection->GetPlayer(); } +::game::backend::State* Lobby::GetState() const { + return m_state; +} + void Lobby::Message( const std::string& message ) { Log( "Sending message: " + message ); m_connection->SendMessage( message ); diff --git a/src/task/mainmenu/menu/lobby/Lobby.h b/src/task/mainmenu/menu/lobby/Lobby.h index 737a70d8..664a280e 100644 --- a/src/task/mainmenu/menu/lobby/Lobby.h +++ b/src/task/mainmenu/menu/lobby/Lobby.h @@ -46,6 +46,7 @@ CLASS( Lobby, PopupMenu ) ::game::backend::settings::Settings* GetSettings(); const ::game::backend::Player* GetPlayer(); + ::game::backend::State* GetState() const; void Message( const std::string& message ); diff --git a/src/task/mainmenu/menu/lobby/PlayersSection.cpp b/src/task/mainmenu/menu/lobby/PlayersSection.cpp index c18356fa..1cb6e9f4 100644 --- a/src/task/mainmenu/menu/lobby/PlayersSection.cpp +++ b/src/task/mainmenu/menu/lobby/PlayersSection.cpp @@ -3,6 +3,9 @@ #include "engine/Engine.h" #include "game/backend/slot/Slot.h" +#include "game/backend/State.h" +#include "game/backend/Factions.h" +#include "game/backend/Faction.h" #include "PlayersSectionRow.h" #include "Lobby.h" @@ -65,13 +68,11 @@ void PlayersSection::UpdateSlots( std::vector< game::backend::slot::Slot >& slot "Random" } ); - for ( auto& id : game_rules.m_factions_order ) { - ASSERT( game_rules.m_factions.find( id ) != game_rules.m_factions.end(), "faction not found: " + id ); - const auto& faction = game_rules.m_factions.at( id ); + for ( const auto& faction : GetLobby()->GetState()->GetFactions()->GetAll() ) { m_choices.factions.push_back( { - id, - faction.m_name + faction->m_id, + faction->m_name } ); } diff --git a/src/task/mainmenu/menu/lobby/PlayersSectionRow.cpp b/src/task/mainmenu/menu/lobby/PlayersSectionRow.cpp index 9f3f1cc2..d1dfdec1 100644 --- a/src/task/mainmenu/menu/lobby/PlayersSectionRow.cpp +++ b/src/task/mainmenu/menu/lobby/PlayersSectionRow.cpp @@ -2,6 +2,9 @@ #include "engine/Engine.h" #include "game/backend/slot/Slot.h" +#include "game/backend/State.h" +#include "game/backend/Factions.h" +#include "game/backend/Faction.h" #include "PlayersSection.h" #include "Lobby.h" #include "game/backend/settings/Settings.h" @@ -50,9 +53,9 @@ void PlayersSectionRow::Create() { const auto& faction = player->GetFaction(); - const auto faction_color = !faction.has_value() - ? types::Color( 1.0f, 1.0f, 1.0f ) - : faction->m_colors.text; + const auto faction_color = faction + ? faction->m_colors.text + : types::Color( 1.0f, 1.0f, 1.0f ); // random if ( is_it_ready ) { NEW( m_elements.ready, ui::object::Surface ); @@ -69,14 +72,14 @@ void PlayersSectionRow::Create() { if ( allowed_to_change ) { m_elements.faction->SetChoices( m_parent->GetFactionChoices() ); m_elements.faction->SetValueByKey( - faction.has_value() + faction ? faction->m_id : "RANDOM" ); } else { m_elements.faction->SetValue( - faction.has_value() + faction ? faction->m_name : "Random" ); @@ -91,8 +94,9 @@ void PlayersSectionRow::Create() { player->ClearFaction(); } else { - ASSERT( rules.m_factions.find( faction_id ) != rules.m_factions.end(), "faction not found: " + faction_id ); - player->SetFaction( rules.m_factions.at( faction_id ) ); + auto* faction = m_parent->GetLobby()->GetState()->GetFactions()->Get( faction_id ); + ASSERT( faction, "faction not found: " + faction_id ); + player->SetFaction( faction ); } m_parent->GetLobby()->UpdateSlot( m_slot_num, m_slot ); return true; From 72724b406cbc93518041a0e26f8261cc8de3a5fa Mon Sep 17 00:00:00 2001 From: afwbkbc Date: Wed, 6 Nov 2024 20:58:49 +0200 Subject: [PATCH 03/13] refactored backend units stuff into UnitManager --- GLSMAC_data/default/main.gls.js | 256 +++---- GLSMAC_data/default/units.gls.js | 2 +- src/game/FrontendRequest.h | 4 +- src/game/backend/CMakeLists.txt | 3 +- src/game/backend/Game.cpp | 577 ++-------------- src/game/backend/Game.h | 54 +- src/game/backend/Player.cpp | 10 +- src/game/backend/Player.h | 13 +- src/game/backend/State.cpp | 38 +- src/game/backend/State.h | 10 +- src/game/backend/Types.h | 13 - src/game/backend/base/Base.cpp | 6 +- src/game/backend/base/Base.h | 6 +- src/game/backend/bindings/Bindings.cpp | 14 +- src/game/backend/bindings/Bindings.h | 3 +- src/game/backend/bindings/Factions.cpp | 5 +- src/game/backend/bindings/Units.cpp | 3 +- src/game/backend/connection/Server.cpp | 2 +- src/game/backend/event/AttackUnit.cpp | 19 +- src/game/backend/event/DefineMorales.cpp | 4 +- src/game/backend/event/DefineUnit.cpp | 3 +- src/game/backend/event/DespawnUnit.cpp | 3 +- src/game/backend/event/MoveUnit.cpp | 13 +- src/game/backend/event/SkipUnitTurn.cpp | 5 +- src/game/backend/event/SpawnUnit.cpp | 7 +- src/game/backend/faction/CMakeLists.txt | 6 + src/game/backend/{ => faction}/Faction.cpp | 2 + src/game/backend/{ => faction}/Faction.h | 6 +- .../FactionManager.cpp} | 28 +- .../{Factions.h => faction/FactionManager.h} | 10 +- src/game/backend/faction/Types.h | 24 + src/game/backend/slot/Slot.cpp | 1 + src/game/backend/unit/CMakeLists.txt | 1 + src/game/backend/unit/Unit.cpp | 21 +- src/game/backend/unit/Unit.h | 8 +- src/game/backend/unit/UnitManager.cpp | 638 ++++++++++++++++++ src/game/backend/unit/UnitManager.h | 96 +++ src/game/frontend/faction/Faction.cpp | 6 +- src/game/frontend/faction/Faction.h | 8 +- src/game/frontend/faction/FactionManager.cpp | 4 +- src/game/frontend/faction/FactionManager.h | 4 +- src/gse/type/Object.h | 1 + src/main.cpp | 9 +- .../mainmenu/menu/lobby/PlayersSection.cpp | 6 +- .../mainmenu/menu/lobby/PlayersSectionRow.cpp | 6 +- 45 files changed, 1130 insertions(+), 828 deletions(-) create mode 100644 src/game/backend/faction/CMakeLists.txt rename src/game/backend/{ => faction}/Faction.cpp (98%) rename src/game/backend/{ => faction}/Faction.h (89%) rename src/game/backend/{Factions.cpp => faction/FactionManager.cpp} (88%) rename src/game/backend/{Factions.h => faction/FactionManager.h} (82%) create mode 100644 src/game/backend/faction/Types.h create mode 100644 src/game/backend/unit/UnitManager.cpp create mode 100644 src/game/backend/unit/UnitManager.h diff --git a/GLSMAC_data/default/main.gls.js b/GLSMAC_data/default/main.gls.js index f87e53a0..44beac3f 100644 --- a/GLSMAC_data/default/main.gls.js +++ b/GLSMAC_data/default/main.gls.js @@ -6,144 +6,150 @@ const bases = #include('bases'); #system.on('configure', (e) => { - factions.configure(e.state.factions); - - units.configure(); - - // will be populated on start - let players = []; - let players_sz = 0; - let random_player = () => { - return players[(#game.random.get_int(0, players_sz - 1))]; - }; - - let random_morale = () => { - return #game.random.get_int(0, 6); // TODO: get some constants from api - }; - - let random_health = () => { - return #game.random.get_float(#to_float(0.1), #to_float(1)); - }; - - const pop_types = [ - 'Worker', - 'Talent', - 'Doctor', - 'Librarian', - ]; - - let add_pops = ( base, count ) => { - for (let i = 0; i < count; i++) { - const type = pop_types[(#game.random.get_int(0, #size(pop_types) - 1))]; - base.create_pop({ - type: type, - }); - } - }; - - let all_bases = []; - - #game.on.start((e) => { - - // init game data - resources.define(); - map.define(); - units.define(); - bases.define(); - - // init players - players = #game.players.get_all(); - players_sz = #size(players); - - // spawn units - - let units_spawned = 0; - let bases_spawned = 0; - - let w = #game.map.get_width(); - let h = #game.map.get_height(); - - for (let y = 0; y < h; y++) { - for (let x = 0; x < w; x++) { - if (x % 2 == y % 2) { - let owner = random_player(); - let tile = #game.map.get_tile(x, y); - if (#game.random.get_int(0, 6) == 0) { - let units_count = #game.random.get_int(1, 2); - for (let i = 0; i < units_count; i++) { - if (tile.is_land) { - if (#game.random.get_int(0, 4) != 0) { - #game.units.spawn('MindWorms', owner, tile, random_morale(), random_health()); - units_spawned++; - } else { - if (tile.has_fungus && #game.random.get_int(0, 3) == 0) { - // morale depends on count of fungus tiles around - let morale = 1; - for (neighbour of tile.get_surrounding_tiles()) { - if (morale >= 6) { - break; - } - if (neighbour.has_fungus) { - morale++; + factions.configure(e.lobby.factions); + + e.lobby.on('start', (e) => { + + e.game.on('configure', (e) => { + + units.configure(e.game.units); + + // will be populated on start + let players = []; + let players_sz = 0; + let random_player = () => { + return players[(#game.random.get_int(0, players_sz - 1))]; + }; + + let random_morale = () => { + return #game.random.get_int(0, 6); // TODO: get some constants from api + }; + + let random_health = () => { + return #game.random.get_float(#to_float(0.1), #to_float(1)); + }; + + const pop_types = [ + 'Worker', + 'Talent', + 'Doctor', + 'Librarian', + ]; + + let add_pops = ( base, count ) => { + for (let i = 0; i < count; i++) { + const type = pop_types[(#game.random.get_int(0, #size(pop_types) - 1))]; + base.create_pop({ + type: type, + }); + } + }; + + let all_bases = []; + + #game.on.start((e) => { + + // init game data + resources.define(); + map.define(); + units.define(); + bases.define(); + + // init players + players = #game.players.get_all(); + players_sz = #size(players); + + // spawn units + + let units_spawned = 0; + let bases_spawned = 0; + + let w = #game.map.get_width(); + let h = #game.map.get_height(); + + for (let y = 0; y < h; y++) { + for (let x = 0; x < w; x++) { + if (x % 2 == y % 2) { + let owner = random_player(); + let tile = #game.map.get_tile(x, y); + if (#game.random.get_int(0, 6) == 0) { + let units_count = #game.random.get_int(1, 2); + for (let i = 0; i < units_count; i++) { + if (tile.is_land) { + if (#game.random.get_int(0, 4) != 0) { + #game.units.spawn('MindWorms', owner, tile, random_morale(), random_health()); + units_spawned++; + } else { + if (tile.has_fungus && #game.random.get_int(0, 3) == 0) { + // morale depends on count of fungus tiles around + let morale = 1; + for (neighbour of tile.get_surrounding_tiles()) { + if (morale >= 6) { + break; + } + if (neighbour.has_fungus) { + morale++; + } + } + #game.units.spawn('FungalTower', owner, tile, morale, random_health()); + units_spawned++; + } else { + #game.units.spawn('SporeLauncher', owner, tile, random_morale(), random_health()); + units_spawned++; } } - #game.units.spawn('FungalTower', owner, tile, morale, random_health()); - units_spawned++; } else { - #game.units.spawn('SporeLauncher', owner, tile, random_morale(), random_health()); - units_spawned++; + if (#game.random.get_int(0, 3) == 0) { + #game.units.spawn('SeaLurk', owner, tile, random_morale(), random_health()); + units_spawned++; + } } } - } else { - if (#game.random.get_int(0, 3) == 0) { - #game.units.spawn('SeaLurk', owner, tile, random_morale(), random_health()); - units_spawned++; - } - } - } - } - if (#game.random.get_int(0, 5) == 0) { - let has_adjactent_bases = false; - for (neighbour of tile.get_surrounding_tiles()) { - if (neighbour.get_base() != null) { - has_adjactent_bases = true; - break; } - } - if (!has_adjactent_bases) { - let base = #game.bases.spawn( - owner, - tile, - { - // name: 'base name', + if (#game.random.get_int(0, 5) == 0) { + let has_adjactent_bases = false; + for (neighbour of tile.get_surrounding_tiles()) { + if (neighbour.get_base() != null) { + has_adjactent_bases = true; + break; + } + } + if (!has_adjactent_bases) { + let base = #game.bases.spawn( + owner, + tile, + { + // name: 'base name', + } + ); + add_pops(base, #game.random.get_int(1, 7)); + all_bases []= base; + bases_spawned++; } - ); - add_pops(base, #game.random.get_int(1, 7)); - all_bases []= base; - bases_spawned++; + } } } } - } - } - #game.message('Total units spawned: ' + #to_string(units_spawned)); - #game.message('Total bases spawned: ' + #to_string(bases_spawned)); + #game.message('Total units spawned: ' + #to_string(units_spawned)); + #game.message('Total bases spawned: ' + #to_string(bases_spawned)); - }); + }); - #game.on.turn((e) => { - for ( base of all_bases ) { - add_pops(base, 1); - } - // - }); + #game.on.turn((e) => { + for ( base of all_bases ) { + add_pops(base, 1); + } + // + }); - #game.on.unit_spawn((e) => { - // - }); + #game.on.unit_spawn((e) => { + // + }); - #game.on.unit_despawn((e) => { - // - }); + #game.on.unit_despawn((e) => { + // + }); + }); + }); }); diff --git a/GLSMAC_data/default/units.gls.js b/GLSMAC_data/default/units.gls.js index 4504a7d0..cd99ed73 100644 --- a/GLSMAC_data/default/units.gls.js +++ b/GLSMAC_data/default/units.gls.js @@ -6,7 +6,7 @@ const animations = #include('units/animations'); const result = { - configure: () => { + configure: (units) => { movement.init(); combat.init(animations); turns.init(); diff --git a/src/game/FrontendRequest.h b/src/game/FrontendRequest.h index 1cd3dfd7..5d390533 100644 --- a/src/game/FrontendRequest.h +++ b/src/game/FrontendRequest.h @@ -14,8 +14,10 @@ namespace map::tile { class Tile; class TileState; } +namespace faction { class Faction; } +} class FrontendRequest { public: @@ -48,7 +50,7 @@ class FrontendRequest { const request_type_t type = FR_NONE; - typedef std::vector< const backend::Faction* > faction_defines_t; + typedef std::vector< const backend::faction::Faction* > faction_defines_t; struct slot_define_t { size_t slot_index; diff --git a/src/game/backend/CMakeLists.txt b/src/game/backend/CMakeLists.txt index 7163ea33..5662c4b7 100644 --- a/src/game/backend/CMakeLists.txt +++ b/src/game/backend/CMakeLists.txt @@ -4,6 +4,7 @@ SUBDIR( rules ) SUBDIR( settings ) SUBDIR( slot ) SUBDIR( connection ) +SUBDIR( faction ) SUBDIR( animation ) SUBDIR( unit ) SUBDIR( base ) @@ -16,8 +17,6 @@ SET( SRC ${SRC} ${PWD}/System.cpp ${PWD}/Game.cpp ${PWD}/State.cpp - ${PWD}/Faction.cpp - ${PWD}/Factions.cpp ${PWD}/Account.cpp ${PWD}/Player.cpp ${PWD}/MapObject.cpp diff --git a/src/game/backend/Game.cpp b/src/game/backend/Game.cpp index a88f2574..c57dd59f 100644 --- a/src/game/backend/Game.cpp +++ b/src/game/backend/Game.cpp @@ -8,7 +8,8 @@ #include "util/FS.h" #include "types/Buffer.h" #include "State.h" -#include "Factions.h" +#include "game/backend/faction/Faction.h" +#include "game/backend/faction/FactionManager.h" #include "map_editor/MapEditor.h" #include "event/FinalizeTurn.h" #include "event/TurnFinalized.h" @@ -40,6 +41,7 @@ #include "animation/Def.h" #include "unit/Def.h" #include "unit/Unit.h" +#include "unit/UnitManager.h" #include "unit/MoraleSet.h" #include "base/PopDef.h" #include "base/Base.h" @@ -179,6 +181,7 @@ void Game::Start() { // init map editor NEW( m_map_editor, map_editor::MapEditor, this ); + NEW( m_um, unit::UnitManager, this ); } void Game::Stop() { @@ -195,6 +198,9 @@ void Game::Stop() { DELETE( m_map_editor ); m_map_editor = nullptr; + DELETE( m_um ); + m_um = nullptr; + MTModule::Stop(); } @@ -330,7 +336,7 @@ void Game::Iterate() { } { - const auto factions = m_state->GetFactions()->GetAll(); + const auto factions = m_state->GetFM()->GetAll(); auto* faction_defines = new FrontendRequest::faction_defines_t(); for ( const auto& faction : factions ) { faction_defines->push_back( faction ); @@ -367,10 +373,8 @@ void Game::Iterate() { if ( m_game_state == GS_RUNNING ) { - for ( auto& it : m_unprocessed_units ) { - SpawnUnit( it ); - } - m_unprocessed_units.clear(); + m_um->ProcessUnprocessed(); + for ( auto& it : m_unprocessed_bases ) { SpawnBase( it ); } @@ -418,7 +422,7 @@ void Game::Iterate() { Log( (std::string)e.what() ); Quit( "Event validation error" ); // TODO: fix reason } - PushUnitUpdates(); + m_um->PushUnitUpdates(); PushBaseUpdates(); ProcessTileLockRequests(); } @@ -452,8 +456,11 @@ const size_t Game::GetSlotNum() const { WRAPIMPL_BEGIN( Game, CLASS_GAME ) WRAPIMPL_PROPS - - }; + { + "units", + m_um->Wrap( true ) + }, + }; WRAPIMPL_END_PTR( Game ) UNWRAPIMPL_PTR( Game ) @@ -618,7 +625,7 @@ const MT_Response Game::ProcessRequest( const MT_Request& request, MT_CANCELABLE } ); if ( result.Get()->type != gse::type::Type::T_OBJECT ) { - THROW( "unexpected return type: expected Object, got " + result.GetTypeString() ); + break; // TMP //THROW( "unexpected return type: expected Object, got " + result.GetTypeString() ); } const auto& values = ( (gse::type::Object*)result.Get() )->value; for ( const auto& v : values ) { @@ -801,32 +808,6 @@ void Game::OnGSEError( gse::Exception& err ) { AddFrontendRequest( fr ); } -unit::MoraleSet* Game::GetMoraleSet( const std::string& name ) const { - const auto& it = m_unit_moralesets.find( name ); - if ( it != m_unit_moralesets.end() ) { - return it->second; - } - return nullptr; -} - -unit::Unit* Game::GetUnit( const size_t id ) const { - const auto& it = m_units.find( id ); - if ( it != m_units.end() ) { - return it->second; - } - return nullptr; -} - -unit::Def* Game::GetUnitDef( const std::string& name ) const { - const auto& it = m_unit_defs.find( name ); - if ( it != m_unit_defs.end() ) { - return it->second; - } - else { - return nullptr; - } -} - base::PopDef* Game::GetPopDef( const std::string& id ) const { const auto& it = m_base_popdefs.find( id ); if ( it != m_base_popdefs.end() ) { @@ -859,17 +840,6 @@ const gse::Value Game::AddEvent( event::Event* event ) { return VALUE( gse::type::Undefined ); } -void Game::RefreshUnit( const unit::Unit* unit ) { - if ( unit->m_health <= 0.0f ) { - if ( GetState()->IsMaster() ) { - AddEvent( new event::DespawnUnit( GetSlotNum(), unit->m_id ) ); - } - } - else { - QueueUnitUpdate( unit, UUO_REFRESH ); - } -} - void Game::RefreshBase( const base::Base* base ) { QueueBaseUpdate( base, BUO_REFRESH ); } @@ -899,16 +869,9 @@ const std::string* Game::ShowAnimationOnTile( const std::string& animation_id, m if ( !tile->IsLocked() ) { return new std::string( "Tile must be locked before showing animation" ); } - const auto running_animation_id = m_next_running_animation_id++; - m_running_animations_callbacks.insert( - { - running_animation_id, - on_complete - } - ); auto fr = FrontendRequest( FrontendRequest::FR_ANIMATION_SHOW ); NEW( fr.data.animation_show.animation_id, std::string, animation_id ); - fr.data.animation_show.running_animation_id = running_animation_id; + fr.data.animation_show.running_animation_id = AddAnimationCallback( on_complete ); const auto c = GetTileRenderCoords( tile ); fr.data.animation_show.render_coords = { c.x, @@ -939,107 +902,6 @@ void Game::DefineResource( Resource* resource ) { m_resource_idx.push_back( resource->m_id ); } -void Game::DefineMoraleSet( unit::MoraleSet* moraleset ) { - Log( "Defining unit moraleset ('" + moraleset->m_id + "')" ); - - ASSERT( m_unit_moralesets.find( moraleset->m_id ) == m_unit_moralesets.end(), "Unit moraleset '" + moraleset->m_id + "' already exists" ); - - m_unit_moralesets.insert( - { - moraleset->m_id, - moraleset - } - ); -} - -void Game::DefineUnit( unit::Def* def ) { - Log( "Defining unit ('" + def->m_id + "')" ); - - ASSERT( m_unit_defs.find( def->m_id ) == m_unit_defs.end(), "Unit definition '" + def->m_id + "' already exists" ); - - m_unit_defs.insert( - { - def->m_id, - def - } - ); - - auto fr = FrontendRequest( FrontendRequest::FR_UNIT_DEFINE ); - NEW( fr.data.unit_define.serialized_unitdef, std::string, unit::Def::Serialize( def ).ToString() ); - AddFrontendRequest( fr ); -} - -void Game::SpawnUnit( unit::Unit* unit ) { - if ( m_game_state != GS_RUNNING ) { - m_unprocessed_units.push_back( unit ); - return; - } - - auto* tile = unit->GetTile(); - - Log( "Spawning unit #" + std::to_string( unit->m_id ) + " (" + unit->m_def->m_id + ") at " + tile->ToString() ); - - ASSERT( m_units.find( unit->m_id ) == m_units.end(), "duplicate unit id" ); - m_units.insert_or_assign( unit->m_id, unit ); - - QueueUnitUpdate( unit, UUO_SPAWN ); - - if ( m_state->IsMaster() ) { - m_state->m_bindings->Call( - bindings::Bindings::CS_ON_UNIT_SPAWN, { - { - "unit", - unit->Wrap() - }, - } - ); - } -} - -void Game::SkipUnitTurn( const size_t unit_id ) { - const auto& it = m_units.find( unit_id ); - ASSERT( it != m_units.end(), "unit id not found" ); - auto* unit = it->second; - - Log( "Skipping unit turn #" + std::to_string( unit->m_id ) + " (" + unit->m_def->m_id + ") at " + unit->GetTile()->ToString() ); - - unit->m_movement = 0.0f; - - RefreshUnit( unit ); -} - -void Game::DespawnUnit( const size_t unit_id ) { - - const auto& it = m_units.find( unit_id ); - ASSERT( it != m_units.end(), "unit id not found" ); - auto* unit = it->second; - - Log( "Despawning unit #" + std::to_string( unit->m_id ) + " (" + unit->m_def->m_id + ") at " + unit->GetTile()->ToString() ); - - QueueUnitUpdate( unit, UUO_DESPAWN ); - - auto* tile = unit->GetTile(); - ASSERT( tile, "unit tile not set" ); - const auto& tile_it = tile->units.find( unit->m_id ); - ASSERT( tile_it != tile->units.end(), "unit id not found in tile" ); - tile->units.erase( tile_it ); - - m_units.erase( it ); - - if ( m_state->IsMaster() ) { - m_state->m_bindings->Call( - bindings::Bindings::CS_ON_UNIT_DESPAWN, { - { - "unit", - unit->Wrap() - } - } - ); - } - - delete unit; -} - void Game::DefinePop( base::PopDef* pop_def ) { Log( "Defining base pop ('" + pop_def->m_id + "')" ); @@ -1116,192 +978,6 @@ void Game::SpawnBase( base::Base* base ) { RefreshBase( base ); } -const std::string* Game::MoveUnitValidate( unit::Unit* unit, map::tile::Tile* dst_tile ) { - const auto result = m_state->m_bindings->Call( - bindings::Bindings::CS_ON_UNIT_MOVE_VALIDATE, { - { - "unit", - unit->Wrap() - }, - { - "src_tile", - unit->GetTile()->Wrap() - }, - { - "dst_tile", - dst_tile->Wrap() - }, - } - ); - switch ( result.Get()->type ) { - case gse::type::Type::T_NULL: - case gse::type::Type::T_UNDEFINED: - return nullptr; // no errors - case gse::type::Type::T_STRING: - return new std::string( ( (gse::type::String*)result.Get() )->value ); // error - default: - THROW( "unexpected validation result type: " + gse::type::Type::GetTypeString( result.Get()->type ) ); - } -} - -const gse::Value Game::MoveUnitResolve( unit::Unit* unit, map::tile::Tile* dst_tile ) { - return m_state->m_bindings->Call( - bindings::Bindings::CS_ON_UNIT_MOVE_RESOLVE, { - { - "unit", - unit->Wrap() - }, - { - "src_tile", - unit->GetTile()->Wrap() - }, - { - "dst_tile", - dst_tile->Wrap() - }, - } - ); -} - -void Game::MoveUnitApply( unit::Unit* unit, map::tile::Tile* dst_tile, const gse::Value resolutions ) { - ASSERT( dst_tile, "dst tile not set" ); - - Log( "Moving unit #" + std::to_string( unit->m_id ) + " to " + dst_tile->coord.ToString() ); - - auto* src_tile = unit->GetTile(); - ASSERT( src_tile, "src tile not set" ); - ASSERT( src_tile->units.find( unit->m_id ) != src_tile->units.end(), "src tile does not contain this unit" ); - ASSERT( dst_tile->units.find( unit->m_id ) == dst_tile->units.end(), "dst tile already contains this unit" ); - - const auto result = m_state->m_bindings->Call( - bindings::Bindings::CS_ON_UNIT_MOVE_APPLY, { - { - "unit", - unit->Wrap( true ) - }, - { - "src_tile", - src_tile->Wrap() - }, - { - "dst_tile", - dst_tile->Wrap() - }, - { - "resolutions", - resolutions - } - } - ); -} - -const std::string* Game::MoveUnitToTile( unit::Unit* unit, map::tile::Tile* dst_tile, const cb_oncomplete& on_complete ) { - const auto* src_tile = unit->GetTile(); - if ( src_tile == dst_tile ) { - return new std::string( "Unit can't move because it's already on target tile" ); - } - if ( !src_tile->IsLocked() ) { - return new std::string( "Source tile must be locked before moving unit" ); - } - if ( !dst_tile->IsLocked() ) { - return new std::string( "Destination tile must be locked before moving unit" ); - } - const auto running_animation_id = m_next_running_animation_id++; - m_running_animations_callbacks.insert( - { - running_animation_id, - [ this, on_complete, unit, dst_tile ]() { - unit->SetTile( dst_tile ); - on_complete(); - } - } - ); - auto fr = FrontendRequest( FrontendRequest::FR_UNIT_MOVE ); - fr.data.unit_move.unit_id = unit->m_id; - fr.data.unit_move.dst_tile_coords = { - dst_tile->coord.x, - dst_tile->coord.y - }; - fr.data.unit_move.running_animation_id = running_animation_id; - AddFrontendRequest( fr ); - return nullptr; // no error -} - -const std::string* Game::AttackUnitValidate( unit::Unit* attacker, unit::Unit* defender ) { - const auto result = m_state->m_bindings->Call( - bindings::Bindings::CS_ON_UNIT_ATTACK_VALIDATE, { - { - "attacker", - attacker->Wrap() - }, - { - "defender", - defender->Wrap() - }, - } - ); - switch ( result.Get()->type ) { - case gse::type::Type::T_NULL: - case gse::type::Type::T_UNDEFINED: - return nullptr; // no errors - case gse::type::Type::T_STRING: - return new std::string( ( (gse::type::String*)result.Get() )->value ); // error - default: - THROW( "unexpected validation result type: " + gse::type::Type::GetTypeString( result.Get()->type ) ); - } - return nullptr; -} - -const gse::Value Game::AttackUnitResolve( unit::Unit* attacker, unit::Unit* defender ) { - return m_state->m_bindings->Call( - bindings::Bindings::CS_ON_UNIT_ATTACK_RESOLVE, { - { - "attacker", - attacker->Wrap() - }, - { - "defender", - defender->Wrap() - }, - } - ); -} - -void Game::AttackUnitApply( unit::Unit* attacker, unit::Unit* defender, const gse::Value resolutions ) { - m_state->m_bindings->Call( - bindings::Bindings::CS_ON_UNIT_ATTACK_APPLY, { - { - "attacker", - attacker->Wrap( true ) - }, - { - "defender", - defender->Wrap( true ) - }, - { - "resolutions", - resolutions - } - } - ); - if ( attacker->m_health <= 0.0f ) { - if ( GetState()->IsMaster() ) { - AddEvent( new event::DespawnUnit( GetSlotNum(), attacker->m_id ) ); - } - } - else { - RefreshUnit( attacker ); - } - if ( defender->m_health <= 0.0f ) { - if ( GetState()->IsMaster() ) { - AddEvent( new event::DespawnUnit( GetSlotNum(), defender->m_id ) ); - } - } - else { - RefreshUnit( defender ); - } -} - const size_t Game::GetTurnId() const { return m_current_turn.GetId(); } @@ -1383,7 +1059,7 @@ void Game::AdvanceTurn( const size_t turn_id ) { AddFrontendRequest( fr ); } - for ( auto& it : m_units ) { + for ( auto& it : m_um->GetUnits() ) { auto* unit = it.second; m_state->m_bindings->Call( bindings::Bindings::CS_ON_UNIT_TURN, { @@ -1394,7 +1070,7 @@ void Game::AdvanceTurn( const size_t turn_id ) { } ); unit->m_moved_this_turn = false; - RefreshUnit( unit ); + m_um->RefreshUnit( unit ); } for ( auto& it : m_bases ) { @@ -1539,12 +1215,16 @@ void Game::UnlockTiles( const size_t initiator_slot, const map::tile::positions_ } } -Faction* Game::GetFaction( const std::string& id ) const { - auto* faction = m_state->GetFactions()->Get( id ); // TODO: store factions in Game itself? +faction::Faction* Game::GetFaction( const std::string& id ) const { + auto* faction = m_state->GetFM()->Get( id ); // TODO: store factions in Game itself? ASSERT( faction, "faction not found: " + id ); return faction; } +unit::UnitManager* Game::GetUM() const { + return m_um; +} + void Game::ValidateEvent( event::Event* event ) { if ( !event->m_is_validated ) { const auto* errmsg = event->Validate( this ); @@ -1614,72 +1294,6 @@ void Game::UnserializeResources( types::Buffer& buf ) { } } -void Game::SerializeUnits( types::Buffer& buf ) const { - - Log( "Serializing " + std::to_string( m_unit_moralesets.size() ) + " unit moralesets" ); - buf.WriteInt( m_unit_moralesets.size() ); - for ( const auto& it : m_unit_moralesets ) { - buf.WriteString( it.first ); - buf.WriteString( unit::MoraleSet::Serialize( it.second ).ToString() ); - } - - Log( "Serializing " + std::to_string( m_unit_defs.size() ) + " unit defs" ); - buf.WriteInt( m_unit_defs.size() ); - for ( const auto& it : m_unit_defs ) { - buf.WriteString( it.first ); - buf.WriteString( unit::Def::Serialize( it.second ).ToString() ); - } - - Log( "Serializing " + std::to_string( m_units.size() ) + " units" ); - buf.WriteInt( m_units.size() ); - for ( const auto& it : m_units ) { - buf.WriteInt( it.first ); - buf.WriteString( unit::Unit::Serialize( it.second ).ToString() ); - } - buf.WriteInt( unit::Unit::GetNextId() ); - - Log( "Saved next unit id: " + std::to_string( unit::Unit::GetNextId() ) ); -} - -void Game::UnserializeUnits( types::Buffer& buf ) { - ASSERT( m_unit_moralesets.empty(), "unit moralesets not empty" ); - ASSERT( m_unit_defs.empty(), "unit defs not empty" ); - ASSERT( m_units.empty(), "units not empty" ); - ASSERT( m_unprocessed_units.empty(), "unprocessed units not empty" ); - - size_t sz = buf.ReadInt(); - Log( "Unserializing " + std::to_string( sz ) + " unit moralesets" ); - m_unit_moralesets.reserve( sz ); - for ( size_t i = 0 ; i < sz ; i++ ) { - const auto name = buf.ReadString(); - auto b = types::Buffer( buf.ReadString() ); - DefineMoraleSet( unit::MoraleSet::Unserialize( b ) ); - } - - sz = buf.ReadInt(); - Log( "Unserializing " + std::to_string( sz ) + " unit defs" ); - m_unit_defs.reserve( sz ); - for ( size_t i = 0 ; i < sz ; i++ ) { - const auto name = buf.ReadString(); - auto b = types::Buffer( buf.ReadString() ); - DefineUnit( unit::Def::Unserialize( b ) ); - } - - sz = buf.ReadInt(); - Log( "Unserializing " + std::to_string( sz ) + " units" ); - if ( m_game_state != GS_RUNNING ) { - m_unprocessed_units.reserve( sz ); - } - for ( size_t i = 0 ; i < sz ; i++ ) { - const auto unit_id = buf.ReadInt(); - auto b = types::Buffer( buf.ReadString() ); - SpawnUnit( unit::Unit::Unserialize( b, this ) ); - } - - unit::Unit::SetNextId( buf.ReadInt() ); - Log( "Restored next unit id: " + std::to_string( unit::Unit::GetNextId() ) ); -} - void Game::SerializeBases( types::Buffer& buf ) const { Log( "Serializing " + std::to_string( m_base_popdefs.size() ) + " base pop defs" ); @@ -1762,6 +1376,13 @@ void Game::InitGame( MT_Response& response, MT_CANCELABLE ) { Log( "Initializing game" ); + m_state->m_bindings->Trigger( this, "configure", { + { + "game", + Wrap() + }, + }); + ASSERT( m_pending_frontend_requests, "pending events not set" ); m_pending_frontend_requests->clear(); @@ -1773,7 +1394,7 @@ void Game::InitGame( MT_Response& response, MT_CANCELABLE ) { if ( m_state->IsMaster() ) { // assign random factions to players - auto factions = m_state->GetFactions()->GetAll(); + auto factions = m_state->GetFM()->GetAll(); std::vector< size_t > m_available_factions = {}; const auto& slots = m_state->m_slots->GetSlots(); for ( const auto& slot : slots ) { @@ -1854,7 +1475,7 @@ void Game::InitGame( MT_Response& response, MT_CANCELABLE ) { // units { types::Buffer b; - SerializeUnits( b ); + m_um->Serialize( b ); buf.WriteString( b.ToString() ); } @@ -2047,7 +1668,7 @@ void Game::InitGame( MT_Response& response, MT_CANCELABLE ) { // units { auto ub = types::Buffer( buf.ReadString() ); - UnserializeUnits( ub ); + m_um->Unserialize( ub ); } // bases @@ -2110,10 +1731,7 @@ void Game::ResetGame() { m_slot_num = 0; m_slot = nullptr; - for ( auto& it : m_unprocessed_units ) { - delete it; - } - m_unprocessed_units.clear(); + m_um->Clear(); for ( auto& it : m_unprocessed_events ) { delete it; @@ -2121,7 +1739,7 @@ void Game::ResetGame() { m_unprocessed_events.clear(); m_running_animations_callbacks.clear(); - m_next_running_animation_id = 1; + m_next_running_animation_id = 0; for ( auto& it : m_resources ) { delete it.second; @@ -2130,21 +1748,6 @@ void Game::ResetGame() { m_resource_idx.clear(); m_resource_idx_map.clear(); - for ( auto& it : m_unit_moralesets ) { - delete it.second; - } - m_unit_moralesets.clear(); - - for ( auto& it : m_units ) { - delete it.second; - } - m_units.clear(); - - for ( auto& it : m_unit_defs ) { - delete it.second; - } - m_unit_defs.clear(); - for ( auto& it : m_base_popdefs ) { delete it.second; } @@ -2168,7 +1771,6 @@ void Game::ResetGame() { ASSERT( m_pending_frontend_requests, "pending events not set" ); m_pending_frontend_requests->clear(); - m_unit_updates.clear(); m_base_updates.clear(); m_tile_lock_requests.clear(); @@ -2199,7 +1801,7 @@ void Game::CheckTurnComplete() { bool is_turn_complete = true; - for ( const auto& unit : m_units ) { + for ( const auto& unit : m_um->GetUnits() ) { if ( unit.second->m_owner == m_slot && unit.second->HasMovesLeft() ) { is_turn_complete = false; break; @@ -2217,32 +1819,6 @@ void Game::CheckTurnComplete() { } } -void Game::QueueUnitUpdate( const unit::Unit* unit, const unit_update_op_t op ) { - auto it = m_unit_updates.find( unit->m_id ); - if ( it == m_unit_updates.end() ) { - it = m_unit_updates.insert( - { - unit->m_id, - { - {}, - unit, - } - } - ).first; - } - auto& update = it->second; - if ( op == UUO_DESPAWN ) { - if ( op & UUO_SPAWN ) { - // if unit is despawned immediately after spawning - frontend doesn't need to know - m_unit_updates.erase( it ); - return; - } - update.ops = UUO_NONE; // clear other actions if unit was despawned - } - // add to operations list - update.ops = (unit_update_op_t)( (uint8_t)update.ops | (uint8_t)op ); -} - void Game::QueueBaseUpdate( const base::Base* base, const base_update_op_t op ) { auto it = m_base_updates.find( base->m_id ); if ( it == m_base_updates.end() ) { @@ -2269,64 +1845,6 @@ void Game::QueueBaseUpdate( const base::Base* base, const base_update_op_t op ) update.ops = (base_update_op_t)( (uint8_t)update.ops | (uint8_t)op ); } -void Game::PushUnitUpdates() { - if ( m_game_state == GS_RUNNING && !m_unit_updates.empty() ) { - for ( const auto& it : m_unit_updates ) { - const auto unit_id = it.first; - const auto& uu = it.second; - const auto& unit = uu.unit; - if ( uu.ops & UUO_SPAWN ) { - auto fr = FrontendRequest( FrontendRequest::FR_UNIT_SPAWN ); - fr.data.unit_spawn.unit_id = unit->m_id; - NEW( fr.data.unit_spawn.unitdef_id, std::string, unit->m_def->m_id ); - fr.data.unit_spawn.slot_index = unit->m_owner->GetIndex(); - const auto* tile = unit->GetTile(); - fr.data.unit_spawn.tile_coords = { - tile->coord.x, - tile->coord.y - }; - const auto c = unit->GetRenderCoords(); - fr.data.unit_spawn.render_coords = { - c.x, - c.y, - c.z - }; - - fr.data.unit_spawn.movement = unit->m_movement; - fr.data.unit_spawn.morale = unit->m_morale; - NEW( fr.data.unit_spawn.morale_string, std::string, unit->GetMoraleString() ); - fr.data.unit_spawn.health = unit->m_health; - AddFrontendRequest( fr ); - } - if ( uu.ops & UUO_REFRESH ) { - auto fr = FrontendRequest( FrontendRequest::FR_UNIT_UPDATE ); - fr.data.unit_update.unit_id = unit->m_id; - fr.data.unit_update.movement = unit->m_movement; - fr.data.unit_update.health = unit->m_health; - const auto* tile = unit->GetTile(); - fr.data.unit_update.tile_coords = { - tile->coord.x, - tile->coord.y - }; - const auto c = unit->GetRenderCoords(); - fr.data.unit_update.render_coords = { - c.x, - c.y, - c.z - }; - AddFrontendRequest( fr ); - } - if ( uu.ops & UUO_DESPAWN ) { - auto fr = FrontendRequest( FrontendRequest::FR_UNIT_DESPAWN ); - fr.data.unit_despawn.unit_id = unit_id; - AddFrontendRequest( fr ); - } - } - m_unit_updates.clear(); - CheckTurnComplete(); - } -} - void Game::PushBaseUpdates() { if ( m_game_state == GS_RUNNING && !m_base_updates.empty() ) { for ( const auto& it : m_base_updates ) { @@ -2368,7 +1886,7 @@ void Game::PushBaseUpdates() { } AddFrontendRequest( fr ); } - if ( bu.ops & UUO_DESPAWN ) { + if ( bu.ops & BUO_DESPAWN ) { THROW( "TODO: BASE DESPAWN" ); } } @@ -2441,5 +1959,20 @@ void Game::ProcessTileLockRequests() { } } +const bool Game::IsRunning() const { + return m_game_state == GS_RUNNING; +} + +const size_t Game::AddAnimationCallback( const game::backend::Game::cb_oncomplete& on_complete ) { + const auto running_animation_id = ++m_next_running_animation_id; + m_running_animations_callbacks.insert( + { + running_animation_id, + on_complete + } + ); + return running_animation_id; +} + } } diff --git a/src/game/backend/Game.h b/src/game/backend/Game.h index 4c32cc49..75fd0181 100644 --- a/src/game/backend/Game.h +++ b/src/game/backend/Game.h @@ -44,10 +44,15 @@ namespace animation { class Def; } +namespace faction { +class Faction; +} + namespace unit { class MoraleSet; class Def; class Unit; +class UnitManager; } namespace base { @@ -327,31 +332,15 @@ CLASS2( Game, MTModule, gse::Wrappable ) void Quit( const std::string& reason ); void OnError( std::runtime_error& err ); void OnGSEError( gse::Exception& e ); - unit::MoraleSet* GetMoraleSet( const std::string& name ) const; - unit::Unit* GetUnit( const size_t id ) const; - unit::Def* GetUnitDef( const std::string& name ) const; base::PopDef* GetPopDef( const std::string& id ) const; base::Base* GetBase( const size_t id ) const; const gse::Value AddEvent( event::Event* event ); - void RefreshUnit( const unit::Unit* unit ); void RefreshBase( const base::Base* base ); void DefineAnimation( animation::Def* def ); const std::string* ShowAnimationOnTile( const std::string& animation_id, map::tile::Tile* tile, const cb_oncomplete& on_complete ); void DefineResource( Resource* resource ); - void DefineMoraleSet( unit::MoraleSet* moraleset ); - void DefineUnit( unit::Def* def ); - void SpawnUnit( unit::Unit* unit ); - void SkipUnitTurn( const size_t unit_id ); - void DespawnUnit( const size_t unit_id ); void DefinePop( base::PopDef* pop_def ); void SpawnBase( base::Base* base ); - const std::string* MoveUnitValidate( unit::Unit* unit, map::tile::Tile* dst_tile ); - const gse::Value MoveUnitResolve( unit::Unit* unit, map::tile::Tile* dst_tile ); - void MoveUnitApply( unit::Unit* unit, map::tile::Tile* dst_tile, const gse::Value resolutions ); - const std::string* MoveUnitToTile( unit::Unit* unit, map::tile::Tile* dst_tile, const cb_oncomplete& on_complete ); - const std::string* AttackUnitValidate( unit::Unit* attacker, unit::Unit* defender ); - const gse::Value AttackUnitResolve( unit::Unit* attacker, unit::Unit* defender ); - void AttackUnitApply( unit::Unit* attacker, unit::Unit* defender, const gse::Value resolutions ); const size_t GetTurnId() const; const bool IsTurnActive() const; const bool IsTurnCompleted( const size_t slot_num ) const; @@ -373,7 +362,9 @@ CLASS2( Game, MTModule, gse::Wrappable ) void RequestTileUnlocks( const size_t initiator_slot, const map::tile::positions_t& tile_positions ); void UnlockTiles( const size_t initiator_slot, const map::tile::positions_t& tile_positions ); - Faction* GetFaction( const std::string& id ) const; + faction::Faction* GetFaction( const std::string& id ) const; + + unit::UnitManager* GetUM() const; private: @@ -388,11 +379,7 @@ CLASS2( Game, MTModule, gse::Wrappable ) void SerializeResources( types::Buffer& buf ) const; void UnserializeResources( types::Buffer& buf ); - std::unordered_map< std::string, unit::MoraleSet* > m_unit_moralesets = {}; - std::unordered_map< std::string, unit::Def* > m_unit_defs = {}; - std::map< size_t, unit::Unit* > m_units = {}; - void SerializeUnits( types::Buffer& buf ) const; - void UnserializeUnits( types::Buffer& buf ); + unit::UnitManager* m_um = nullptr; std::unordered_map< std::string, base::PopDef* > m_base_popdefs = {}; std::map< size_t, base::Base* > m_bases = {}; @@ -423,7 +410,6 @@ CLASS2( Game, MTModule, gse::Wrappable ) std::unordered_set< size_t > m_verified_turn_checksum_slots = {}; std::vector< FrontendRequest >* m_pending_frontend_requests = nullptr; - void AddFrontendRequest( const FrontendRequest& request ); void InitGame( MT_Response& response, MT_CANCELABLE ); void ResetGame(); @@ -439,7 +425,6 @@ CLASS2( Game, MTModule, gse::Wrappable ) std::vector< backend::event::Event* > m_unprocessed_events = {}; // TODO: refactor these? - std::vector< unit::Unit* > m_unprocessed_units = {}; std::vector< base::Base* > m_unprocessed_bases = {}; turn::Turn m_current_turn = {}; @@ -450,19 +435,6 @@ CLASS2( Game, MTModule, gse::Wrappable ) size_t m_next_running_animation_id = 1; std::unordered_map< size_t, cb_oncomplete > m_running_animations_callbacks = {}; - enum unit_update_op_t : uint8_t { - UUO_NONE = 0, - UUO_SPAWN = 1 << 0, - UUO_REFRESH = 1 << 1, - UUO_DESPAWN = 1 << 2, - }; - struct unit_update_t { - unit_update_op_t ops = UUO_NONE; - const unit::Unit* unit = nullptr; - }; - std::unordered_map< size_t, unit_update_t > m_unit_updates = {}; - void QueueUnitUpdate( const unit::Unit* unit, const unit_update_op_t op ); - enum base_update_op_t : uint8_t { BUO_NONE = 0, BUO_SPAWN = 1 << 0, @@ -494,9 +466,15 @@ CLASS2( Game, MTModule, gse::Wrappable ) private: friend class bindings::Bindings; - void PushUnitUpdates(); void PushBaseUpdates(); +private: + friend class unit::UnitManager; + void AddFrontendRequest( const FrontendRequest& request ); + + const bool IsRunning() const; + const size_t AddAnimationCallback( const cb_oncomplete& on_complete ); + }; } diff --git a/src/game/backend/Player.cpp b/src/game/backend/Player.cpp index d30cc9c5..ae4dc2e2 100644 --- a/src/game/backend/Player.cpp +++ b/src/game/backend/Player.cpp @@ -1,5 +1,7 @@ #include "Player.h" +#include "game/backend/faction/Faction.h" + namespace game { namespace backend { @@ -10,7 +12,7 @@ Player::Player( types::Buffer buf ) { Player::Player( const std::string& name, const role_t role, - Faction* faction, + faction::Faction* faction, const rules::DifficultyLevel& difficulty_level ) : m_name( name ) @@ -42,7 +44,7 @@ const bool Player::IsConnected() const { return m_is_connected; } -void Player::SetFaction( Faction* faction ) { +void Player::SetFaction( faction::Faction* faction ) { // TODO: validate? m_faction = faction; } @@ -51,7 +53,7 @@ void Player::ClearFaction() { m_faction = {}; } -Faction* Player::GetFaction() { +faction::Faction* Player::GetFaction() { return m_faction; } @@ -108,7 +110,7 @@ void Player::Unserialize( types::Buffer buf ) { m_role = (role_t)buf.ReadInt(); m_faction = {}; if ( buf.ReadBool() ) { - m_faction = new Faction(); + m_faction = new faction::Faction(); m_faction->Unserialize( buf.ReadString() ); } m_difficulty_level.Unserialize( buf.ReadString() ); diff --git a/src/game/backend/Player.h b/src/game/backend/Player.h index 111f00bb..5b8db43b 100644 --- a/src/game/backend/Player.h +++ b/src/game/backend/Player.h @@ -4,12 +4,15 @@ #include "types/Serializable.h" -#include "Faction.h" #include "game/backend/rules/DifficultyLevel.h" namespace game { namespace backend { +namespace faction { +class Faction; +} + namespace slot { class Slot; } @@ -27,7 +30,7 @@ CLASS( Player, types::Serializable ) Player( const std::string& name, const role_t role, - Faction* faction, + faction::Faction* faction, const rules::DifficultyLevel& difficulty_level ); @@ -38,9 +41,9 @@ CLASS( Player, types::Serializable ) void Disconnect(); const bool IsConnected() const; - void SetFaction( Faction* faction ); + void SetFaction( faction::Faction* faction ); void ClearFaction(); - Faction* GetFaction(); + faction::Faction* GetFaction(); void SetDifficultyLevel( const rules::DifficultyLevel& difficulty_level ); const rules::DifficultyLevel& GetDifficultyLevel() const; @@ -66,7 +69,7 @@ CLASS( Player, types::Serializable ) slot::Slot* m_slot = nullptr; - Faction* m_faction = {}; + faction::Faction* m_faction = {}; rules::DifficultyLevel m_difficulty_level = {}; bool m_is_turn_completed = false; diff --git a/src/game/backend/State.cpp b/src/game/backend/State.cpp index e9b69b20..8794dbc6 100644 --- a/src/game/backend/State.cpp +++ b/src/game/backend/State.cpp @@ -1,7 +1,7 @@ #include "State.h" #include "System.h" -#include "Factions.h" +#include "game/backend/faction/FactionManager.h" #include "Game.h" #include "game/backend/connection/Connection.h" #include "game/backend/bindings/Bindings.h" @@ -14,7 +14,7 @@ namespace backend { State::State() : m_slots( new slot::Slots( this ) ) { NEW( m_system, System ); - NEW( m_factions, Factions ); + NEW( m_fm, faction::FactionManager ); } State::~State() { @@ -24,7 +24,7 @@ State::~State() { } delete m_slots; DELETE( m_system ); - DELETE( m_factions ); + DELETE( m_fm ); } void State::SetGame( Game* game ) { @@ -33,6 +33,12 @@ void State::SetGame( Game* game ) { m_on_gse_error = [ this ]( gse::Exception& e ) -> void { m_game->OnGSEError( e ); }; + m_bindings->Trigger( this, "start", { + { + "game", + m_game->Wrap() + } + }); } void State::UnsetGame() { @@ -129,14 +135,16 @@ void State::Configure() { Log( "Configuring state" ); - // reset - m_factions->Clear(); + m_fm->Clear(); - // configure - m_bindings->Configure(); + m_bindings->Trigger( m_system, "configure", { + { + "lobby", + Wrap() + } + }); - // check - if ( m_factions->GetAll().empty() ) { + if ( m_fm->GetAll().empty() ) { THROW( "no factions were defined" ); } } @@ -172,16 +180,16 @@ System* State::GetSystem() const { return m_system; } -Factions* State::GetFactions() const { - return m_factions; +faction::FactionManager* State::GetFM() const { + return m_fm; } WRAPIMPL_BEGIN( State, CLASS_STATE ) WRAPIMPL_PROPS { "factions", - m_factions->Wrap( true ) - } + m_fm->Wrap( true ) + }, }; WRAPIMPL_END_PTR( State ) @@ -191,14 +199,14 @@ const types::Buffer State::Serialize() const { types::Buffer buf; buf.WriteString( m_settings.global.Serialize().ToString() ); - buf.WriteString( m_factions->Serialize().ToString() ); + buf.WriteString( m_fm->Serialize().ToString() ); return buf; } void State::Unserialize( types::Buffer buf ) { m_settings.global.Unserialize( buf.ReadString() ); - m_factions->Unserialize( buf.ReadString() ); + m_fm->Unserialize( buf.ReadString() ); } } diff --git a/src/game/backend/State.h b/src/game/backend/State.h index 686fd6b0..5a218ed3 100644 --- a/src/game/backend/State.h +++ b/src/game/backend/State.h @@ -15,7 +15,11 @@ namespace game { namespace backend { class System; -class Factions; + +namespace faction { +class FactionManager; +} + class Game; class Player; @@ -67,7 +71,7 @@ CLASS2( State, common::Class, gse::Wrappable ) void DetachConnection(); System* GetSystem() const; - Factions* GetFactions() const; + faction::FactionManager* GetFM() const; WRAPDEFS_PTR( State ) @@ -76,7 +80,7 @@ CLASS2( State, common::Class, gse::Wrappable ) private: System* m_system = nullptr; - Factions* m_factions = nullptr; + faction::FactionManager* m_fm = nullptr; Game* m_game = nullptr; std::unordered_set< Player* > m_players = {}; // persistent diff --git a/src/game/backend/Types.h b/src/game/backend/Types.h index 84d46d3b..c2209ee2 100644 --- a/src/game/backend/Types.h +++ b/src/game/backend/Types.h @@ -11,18 +11,5 @@ enum tile_query_purpose_t { TQP_OBJECT_SELECT, // unit or base }; -struct faction_bases_render_info_t { - std::string file = ""; - size_t grid_x = 0; - size_t grid_y = 0; - size_t cell_width = 0; - size_t cell_height = 0; - size_t cell_cx = 0; - size_t cell_cy = 0; - size_t cell_padding = 0; - float scale_x = 1.0f; - float scale_y = 1.0f; -}; - } } diff --git a/src/game/backend/base/Base.cpp b/src/game/backend/base/Base.cpp index ed8c2e24..db9ac5d3 100644 --- a/src/game/backend/base/Base.cpp +++ b/src/game/backend/base/Base.cpp @@ -11,7 +11,7 @@ #include "game/backend/slot/Slot.h" #include "game/backend/slot/Slots.h" #include "game/backend/map/Map.h" -#include "game/backend/Faction.h" +#include "game/backend/faction/Faction.h" #include "Pop.h" #include "game/backend/bindings/Binding.h" #include "PopDef.h" @@ -33,7 +33,7 @@ Base::Base( Game* game, const size_t id, slot::Slot* owner, - Faction* faction, + faction::Faction* faction, map::tile::Tile* tile, const std::string& name, const pops_t& pops @@ -109,7 +109,7 @@ WRAPIMPL_DYNAMIC_GETTERS( Base, CLASS_BASE ) if ( !def ) { GSE_ERROR( gse::EC.INVALID_DEFINITION, "Unknown pop type: " + poptype ); } - const auto max_variants = (m_faction->m_flags & Faction::FF_PROGENITOR) + const auto max_variants = (m_faction->m_flags & faction::Faction::FF_PROGENITOR) ? 1 // aliens have 1 gender : 2; // humans have 2 ASSERT_NOLOG( max_variants > 0, "no variants found for pop type: " + poptype ); diff --git a/src/game/backend/base/Base.h b/src/game/backend/base/Base.h index 5cae1ef8..d30ba5f8 100644 --- a/src/game/backend/base/Base.h +++ b/src/game/backend/base/Base.h @@ -17,7 +17,9 @@ class Game; namespace slot { class Slot; } +namespace faction { class Faction; +} namespace map::tile { class Tile; } @@ -36,7 +38,7 @@ class Base : public gse::Wrappable, public MapObject { Game* game, const size_t id, slot::Slot* owner, - Faction* faction, // faction may differ from owner's faction in some cases, i.e. after being conquered + faction::Faction* faction, // faction may differ from owner's faction in some cases, i.e. after being conquered map::tile::Tile* tile, const std::string& name, const pops_t& pops @@ -47,7 +49,7 @@ class Base : public gse::Wrappable, public MapObject { const size_t m_id; slot::Slot* m_owner; - Faction* m_faction; + faction::Faction* m_faction; std::string m_name; pops_t m_pops; diff --git a/src/game/backend/bindings/Bindings.cpp b/src/game/backend/bindings/Bindings.cpp index fccb2550..2d0cb456 100644 --- a/src/game/backend/bindings/Bindings.cpp +++ b/src/game/backend/bindings/Bindings.cpp @@ -5,6 +5,7 @@ #include "util/FS.h" #include "game/backend/Game.h" +#include "game/backend/unit/UnitManager.h" #include "Binding.h" #include "gse/GSE.h" #include "gse/context/GlobalContext.h" @@ -90,7 +91,7 @@ gse::Value Bindings::Call( const callback_slot_t slot, const callback_arguments_ ); auto* game = m_state->GetGame(); if ( game ) { - game->PushUnitUpdates(); + game->GetUM()->PushUnitUpdates(); } if ( result.Get()->type == gse::type::Type::T_NOTHING ) { // return undefined by default @@ -110,15 +111,8 @@ gse::Value Bindings::Call( const callback_slot_t slot, const callback_arguments_ return VALUE( gse::type::Undefined ); } -void Bindings::Configure() { - m_state->GetSystem()->Trigger( - m_gse_context, m_si_internal, "configure", { - { - "state", - m_state->Wrap() - } - } - ); +void Bindings::Trigger( gse::Wrappable* object, const std::string& event, const gse::type::object_properties_t& args ) { + object->Trigger( m_gse_context, m_si_internal, event, args ); } State* Bindings::GetState() const { diff --git a/src/game/backend/bindings/Bindings.h b/src/game/backend/bindings/Bindings.h index 57aaa093..97c801f7 100644 --- a/src/game/backend/bindings/Bindings.h +++ b/src/game/backend/bindings/Bindings.h @@ -12,6 +12,7 @@ namespace gse { class GSE; +class Wrappable; namespace context { class GlobalContext; @@ -59,7 +60,7 @@ class Bindings : public gse::Bindings { typedef std::map< std::string, gse::Value > callback_arguments_t; gse::Value Call( const callback_slot_t slot, const callback_arguments_t& arguments = {} ); - void Configure(); + void Trigger( gse::Wrappable* object, const std::string& event, const gse::type::object_properties_t& args ); State* GetState() const; Game* GetGame( GSE_CALLABLE ) const; diff --git a/src/game/backend/bindings/Factions.cpp b/src/game/backend/bindings/Factions.cpp index 16bcff9c..3efbf419 100644 --- a/src/game/backend/bindings/Factions.cpp +++ b/src/game/backend/bindings/Factions.cpp @@ -16,8 +16,8 @@ #include "loader/txt/FactionTXTLoader.h" #include "loader/texture/TextureLoader.h" #include "types/texture/Texture.h" -#include "game/backend/Faction.h" -#include "game/backend/Factions.h" +#include "game/backend/faction/Faction.h" +#include "game/backend/faction/FactionManager.h" #include "types/Color.h" @@ -26,7 +26,6 @@ namespace backend { namespace bindings { BINDING_IMPL( factions ) { - auto* factions = m_bindings->GetState()->GetFactions(); const gse::type::object_properties_t properties = { { "import_base_names", diff --git a/src/game/backend/bindings/Units.cpp b/src/game/backend/bindings/Units.cpp index df433ec8..06dc50b5 100644 --- a/src/game/backend/bindings/Units.cpp +++ b/src/game/backend/bindings/Units.cpp @@ -10,6 +10,7 @@ #include "gse/type/Undefined.h" #include "game/backend/Game.h" #include "game/backend/slot/Slot.h" +#include "game/backend/unit/UnitManager.h" #include "game/backend/unit/MoraleSet.h" #include "game/backend/unit/StaticDef.h" #include "game/backend/unit/SpriteRender.h" @@ -106,7 +107,7 @@ BINDING_IMPL( units ) { N_GETPROP( sprite_cy, render_def, "cy", Int ); N_GETPROP_OPT_INT( sprite_morale_based_xshift, render_def, "morale_based_xshift" ); auto* game = GAME; - const auto* moraleset = game->GetMoraleSet( morale ); + const auto* moraleset = game->GetUM()->GetMoraleSet( morale ); if ( !moraleset ) { GSE_ERROR( gse::EC.INVALID_CALL, "Morale type '" + morale + "' is not defined"); } diff --git a/src/game/backend/connection/Server.cpp b/src/game/backend/connection/Server.cpp index 48f5c4af..6c7d96d5 100644 --- a/src/game/backend/connection/Server.cpp +++ b/src/game/backend/connection/Server.cpp @@ -7,7 +7,7 @@ #include "game/backend/State.h" #include "game/backend/slot/Slots.h" #include "game/backend/Player.h" -#include "game/backend/Factions.h" +#include "game/backend/faction/FactionManager.h" namespace game { namespace backend { diff --git a/src/game/backend/event/AttackUnit.cpp b/src/game/backend/event/AttackUnit.cpp index 3bdfc38d..dd86d0a1 100644 --- a/src/game/backend/event/AttackUnit.cpp +++ b/src/game/backend/event/AttackUnit.cpp @@ -1,6 +1,7 @@ #include "AttackUnit.h" #include "game/backend/Game.h" +#include "game/backend/unit/UnitManager.h" #include "game/backend/unit/StaticDef.h" #include "game/backend/unit/Unit.h" #include "game/backend/slot/Slot.h" @@ -19,7 +20,7 @@ AttackUnit::AttackUnit( const size_t initiator_slot, const size_t attacker_unit_ } const std::string* AttackUnit::Validate( Game* game ) const { - auto* attacker = game->GetUnit( m_attacker_unit_id ); + auto* attacker = game->GetUM()->GetUnit( m_attacker_unit_id ); if ( !attacker ) { return Error( "Attacker unit not found" ); } @@ -29,32 +30,32 @@ const std::string* AttackUnit::Validate( Game* game ) const { if ( attacker->GetTile()->IsLocked() ) { return Error( "Attacker tile is locked" ); } - auto* defender = game->GetUnit( m_defender_unit_id ); + auto* defender = game->GetUM()->GetUnit( m_defender_unit_id ); if ( !defender ) { return Error( "Defender unit not found" ); } if ( defender->GetTile()->IsLocked() ) { return Error( "Attacker tile is locked" ); } - return game->AttackUnitValidate( attacker, defender ); + return game->GetUM()->AttackUnitValidate( attacker, defender ); } void AttackUnit::Resolve( Game* game ) { - auto* attacker = game->GetUnit( m_attacker_unit_id ); + auto* attacker = game->GetUM()->GetUnit( m_attacker_unit_id ); ASSERT_NOLOG( attacker, "attacker unit not found" ); - auto* defender = game->GetUnit( m_defender_unit_id ); + auto* defender = game->GetUM()->GetUnit( m_defender_unit_id ); ASSERT_NOLOG( defender, "defender unit not found" ); - m_resolutions = game->AttackUnitResolve( attacker, defender ); + m_resolutions = game->GetUM()->AttackUnitResolve( attacker, defender ); } const gse::Value AttackUnit::Apply( Game* game ) const { - auto* attacker = game->GetUnit( m_attacker_unit_id ); + auto* attacker = game->GetUM()->GetUnit( m_attacker_unit_id ); ASSERT_NOLOG( attacker, "attacker unit not found" ); - auto* defender = game->GetUnit( m_defender_unit_id ); + auto* defender = game->GetUM()->GetUnit( m_defender_unit_id ); ASSERT_NOLOG( defender, "defender unit not found" ); - game->AttackUnitApply( attacker, defender, m_resolutions ); + game->GetUM()->AttackUnitApply( attacker, defender, m_resolutions ); return VALUE( gse::type::Undefined ); } diff --git a/src/game/backend/event/DefineMorales.cpp b/src/game/backend/event/DefineMorales.cpp index 283b0558..a3a2127b 100644 --- a/src/game/backend/event/DefineMorales.cpp +++ b/src/game/backend/event/DefineMorales.cpp @@ -1,7 +1,7 @@ #include "DefineMorales.h" #include "game/backend/Game.h" - +#include "game/backend/unit/UnitManager.h" #include "game/backend/unit/MoraleSet.h" #include "gse/type/Undefined.h" @@ -23,7 +23,7 @@ const std::string* DefineMorales::Validate( Game* game ) const { } const gse::Value DefineMorales::Apply( Game* game ) const { - game->DefineMoraleSet( m_moraleset ); + game->GetUM()->DefineMoraleSet( m_moraleset ); return VALUE( gse::type::Undefined ); } diff --git a/src/game/backend/event/DefineUnit.cpp b/src/game/backend/event/DefineUnit.cpp index 8c3d89af..13bda011 100644 --- a/src/game/backend/event/DefineUnit.cpp +++ b/src/game/backend/event/DefineUnit.cpp @@ -1,6 +1,7 @@ #include "DefineUnit.h" #include "game/backend/Game.h" +#include "game/backend/unit/UnitManager.h" #include "game/backend/unit/Def.h" namespace game { @@ -21,7 +22,7 @@ const std::string* DefineUnit::Validate( Game* game ) const { } const gse::Value DefineUnit::Apply( Game* game ) const { - game->DefineUnit( m_def ); + game->GetUM()->DefineUnit( m_def ); return m_def->Wrap(); } diff --git a/src/game/backend/event/DespawnUnit.cpp b/src/game/backend/event/DespawnUnit.cpp index 355fbd43..05ae410c 100644 --- a/src/game/backend/event/DespawnUnit.cpp +++ b/src/game/backend/event/DespawnUnit.cpp @@ -1,6 +1,7 @@ #include "DespawnUnit.h" #include "game/backend/Game.h" +#include "game/backend/unit/UnitManager.h" #include "gse/type/Undefined.h" @@ -22,7 +23,7 @@ const std::string* DespawnUnit::Validate( Game* game ) const { } const gse::Value DespawnUnit::Apply( Game* game ) const { - game->DespawnUnit( m_unit_id ); + game->GetUM()->DespawnUnit( m_unit_id ); return VALUE( gse::type::Undefined ); } diff --git a/src/game/backend/event/MoveUnit.cpp b/src/game/backend/event/MoveUnit.cpp index 38385cf6..71d1939d 100644 --- a/src/game/backend/event/MoveUnit.cpp +++ b/src/game/backend/event/MoveUnit.cpp @@ -1,6 +1,7 @@ #include "MoveUnit.h" #include "game/backend/Game.h" +#include "game/backend/unit/UnitManager.h" #include "game/backend/unit/StaticDef.h" #include "game/backend/unit/Unit.h" #include "game/backend/slot/Slot.h" @@ -19,7 +20,7 @@ MoveUnit::MoveUnit( const size_t initiator_slot, const size_t unit_id, const bac } const std::string* MoveUnit::Validate( Game* game ) const { - auto* unit = game->GetUnit( m_unit_id ); + auto* unit = game->GetUM()->GetUnit( m_unit_id ); if ( !unit ) { return Error( "Unit not found" ); } @@ -39,24 +40,24 @@ const std::string* MoveUnit::Validate( Game* game ) const { return Error( "Destination tile is locked" ); } - return game->MoveUnitValidate( unit, dst_tile ); + return game->GetUM()->MoveUnitValidate( unit, dst_tile ); } void MoveUnit::Resolve( Game* game ) { - auto* unit = game->GetUnit( m_unit_id ); + auto* unit = game->GetUM()->GetUnit( m_unit_id ); ASSERT_NOLOG( unit, "unit not found" ); auto* src_tile = unit->GetTile(); ASSERT_NOLOG( src_tile, "src tile not set" ); auto* dst_tile = src_tile->GetNeighbour( m_direction ); ASSERT_NOLOG( dst_tile, "dst tile not set" ); - m_resolutions = game->MoveUnitResolve( unit, dst_tile ); + m_resolutions = game->GetUM()->MoveUnitResolve( unit, dst_tile ); } const gse::Value MoveUnit::Apply( Game* game ) const { - auto* unit = game->GetUnit( m_unit_id ); + auto* unit = game->GetUM()->GetUnit( m_unit_id ); ASSERT_NOLOG( unit, "unit not found" ); - game->MoveUnitApply( unit, unit->GetTile()->GetNeighbour( m_direction ), m_resolutions ); + game->GetUM()->MoveUnitApply( unit, unit->GetTile()->GetNeighbour( m_direction ), m_resolutions ); return VALUE( gse::type::Undefined ); } diff --git a/src/game/backend/event/SkipUnitTurn.cpp b/src/game/backend/event/SkipUnitTurn.cpp index 16dd6167..2a1e2e79 100644 --- a/src/game/backend/event/SkipUnitTurn.cpp +++ b/src/game/backend/event/SkipUnitTurn.cpp @@ -1,6 +1,7 @@ #include "SkipUnitTurn.h" #include "game/backend/Game.h" +#include "game/backend/unit/UnitManager.h" #include "game/backend/unit/Unit.h" #include "game/backend/slot/Slot.h" #include "gse/type/Undefined.h" @@ -16,7 +17,7 @@ SkipUnitTurn::SkipUnitTurn( const size_t initiator_slot, const size_t unit_id ) } const std::string* SkipUnitTurn::Validate( Game* game ) const { - const auto* unit = game->GetUnit( m_unit_id ); + const auto* unit = game->GetUM()->GetUnit( m_unit_id ); if ( !unit ) { return Error( "Unit not found" ); } @@ -37,7 +38,7 @@ const std::string* SkipUnitTurn::Validate( Game* game ) const { } const gse::Value SkipUnitTurn::Apply( Game* game ) const { - game->SkipUnitTurn( m_unit_id ); + game->GetUM()->SkipUnitTurn( m_unit_id ); return VALUE( gse::type::Undefined ); } diff --git a/src/game/backend/event/SpawnUnit.cpp b/src/game/backend/event/SpawnUnit.cpp index e4216cae..7d3ea795 100644 --- a/src/game/backend/event/SpawnUnit.cpp +++ b/src/game/backend/event/SpawnUnit.cpp @@ -4,6 +4,7 @@ #include "game/backend/State.h" #include "game/backend/slot/Slots.h" #include "game/backend/map/Map.h" +#include "game/backend/unit/UnitManager.h" #include "game/backend/unit/StaticDef.h" #include "game/backend/unit/Unit.h" @@ -38,14 +39,14 @@ const std::string* SpawnUnit::Validate( Game* game ) const { } const gse::Value SpawnUnit::Apply( Game* game ) const { - auto* def = game->GetUnitDef( m_unit_def ); + auto* def = game->GetUM()->GetUnitDef( m_unit_def ); ASSERT_NOLOG( def, "unit def '" + m_unit_def + "' not found" ); ASSERT_NOLOG( def->m_type == unit::DT_STATIC, "only static defs are supported" ); const auto* staticdef = (unit::StaticDef*)def; auto& owner = game->GetState()->m_slots->GetSlot( m_owner_slot ); auto* tile = game->GetMap()->GetTile( m_pos_x, m_pos_y ); auto* unit = new unit::Unit( - game, + game->GetUM(), unit::Unit::GetNextId(), def, &owner, @@ -55,7 +56,7 @@ const gse::Value SpawnUnit::Apply( Game* game ) const { m_health, false ); - game->SpawnUnit( unit ); + game->GetUM()->SpawnUnit( unit ); return unit->Wrap(); } diff --git a/src/game/backend/faction/CMakeLists.txt b/src/game/backend/faction/CMakeLists.txt new file mode 100644 index 00000000..68a5cad7 --- /dev/null +++ b/src/game/backend/faction/CMakeLists.txt @@ -0,0 +1,6 @@ +SET( SRC ${SRC} + + ${PWD}/FactionManager.cpp + ${PWD}/Faction.cpp + + PARENT_SCOPE ) diff --git a/src/game/backend/Faction.cpp b/src/game/backend/faction/Faction.cpp similarity index 98% rename from src/game/backend/Faction.cpp rename to src/game/backend/faction/Faction.cpp index f27b10a2..c503a081 100644 --- a/src/game/backend/Faction.cpp +++ b/src/game/backend/faction/Faction.cpp @@ -9,6 +9,7 @@ namespace game { namespace backend { +namespace faction { Faction::Faction() { // @@ -87,3 +88,4 @@ UNWRAPIMPL_PTR( Faction ) } } +} diff --git a/src/game/backend/Faction.h b/src/game/backend/faction/Faction.h similarity index 89% rename from src/game/backend/Faction.h rename to src/game/backend/faction/Faction.h index 24bdbf4e..35e2a04f 100644 --- a/src/game/backend/Faction.h +++ b/src/game/backend/faction/Faction.h @@ -6,11 +6,12 @@ #include "types/Serializable.h" #include "gse/Wrappable.h" -#include "Types.h" +#include "game/backend/faction/Types.h" #include "types/Color.h" namespace game { namespace backend { +namespace faction { CLASS2( Faction, types::Serializable, gse::Wrappable ) @@ -37,7 +38,7 @@ CLASS2( Faction, types::Serializable, gse::Wrappable ) std::vector< std::string > water = {}; } m_base_names = {}; - faction_bases_render_info_t m_bases_render = {}; + faction::bases_render_info_t m_bases_render = {}; const types::Buffer Serialize() const override; void Unserialize( types::Buffer buf ) override; @@ -48,3 +49,4 @@ CLASS2( Faction, types::Serializable, gse::Wrappable ) } } +} diff --git a/src/game/backend/Factions.cpp b/src/game/backend/faction/FactionManager.cpp similarity index 88% rename from src/game/backend/Factions.cpp rename to src/game/backend/faction/FactionManager.cpp index 8c866695..75e3e40d 100644 --- a/src/game/backend/Factions.cpp +++ b/src/game/backend/faction/FactionManager.cpp @@ -1,4 +1,4 @@ -#include "Factions.h" +#include "FactionManager.h" #include "Faction.h" @@ -9,22 +9,23 @@ namespace game { namespace backend { +namespace faction { -Factions::Factions() { +FactionManager::FactionManager() { // } -Factions::~Factions() { +FactionManager::~FactionManager() { Clear(); } -void Factions::Add( Faction* faction ) { +void FactionManager::Add( Faction* faction ) { Remove( faction->m_id ); m_factions.insert( { faction->m_id, { faction, ++m_next_faction_idx } } ); m_factions_order.insert({ m_next_faction_idx, faction->m_id }); } -void Factions::Remove( const std::string& id ) { +void FactionManager::Remove( const std::string& id ) { const auto& it = m_factions.find( id ); if ( it != m_factions.end() ) { delete it->second.faction; @@ -33,7 +34,7 @@ void Factions::Remove( const std::string& id ) { } } -void Factions::Clear() { +void FactionManager::Clear() { for ( const auto& it : m_factions ) { delete it.second.faction; } @@ -42,7 +43,7 @@ void Factions::Clear() { m_next_faction_idx = 0; } -Faction* Factions::Get( const std::string& id ) const { +Faction* FactionManager::Get( const std::string& id ) const { const auto& it = m_factions.find( id ); if ( it != m_factions.end() ) { return it->second.faction; @@ -50,7 +51,7 @@ Faction* Factions::Get( const std::string& id ) const { return nullptr; } -const std::vector< Faction* > Factions::GetAll() const { +const std::vector< Faction* > FactionManager::GetAll() const { ASSERT_NOLOG( m_factions.size() == m_factions_order.size(), "factions order size mismatch" ); std::vector< Faction* > result = {}; result.reserve( m_factions.size() ); @@ -61,7 +62,7 @@ const std::vector< Faction* > Factions::GetAll() const { return result; } -WRAPIMPL_BEGIN( Factions, CLASS_FACTIONS ) +WRAPIMPL_BEGIN( FactionManager, CLASS_FACTIONS ) WRAPIMPL_PROPS { "add", @@ -146,11 +147,11 @@ WRAPIMPL_BEGIN( Factions, CLASS_FACTIONS ) } ) }, }; -WRAPIMPL_END_PTR( Factions ) +WRAPIMPL_END_PTR( FactionManager ) -UNWRAPIMPL_PTR( Factions ) +UNWRAPIMPL_PTR( FactionManager ) -const types::Buffer Factions::Serialize() const { +const types::Buffer FactionManager::Serialize() const { types::Buffer buf; buf.WriteInt( m_factions_order.size() ); @@ -163,7 +164,7 @@ const types::Buffer Factions::Serialize() const { return buf; } -void Factions::Unserialize( types::Buffer buf ) { +void FactionManager::Unserialize( types::Buffer buf ) { Clear(); const size_t factions_count = buf.ReadInt(); @@ -178,3 +179,4 @@ void Factions::Unserialize( types::Buffer buf ) { } } +} diff --git a/src/game/backend/Factions.h b/src/game/backend/faction/FactionManager.h similarity index 82% rename from src/game/backend/Factions.h rename to src/game/backend/faction/FactionManager.h index b36c0d89..943e8b32 100644 --- a/src/game/backend/Factions.h +++ b/src/game/backend/faction/FactionManager.h @@ -5,13 +5,14 @@ namespace game { namespace backend { +namespace faction { class Faction; -class Factions : public gse::Wrappable { +class FactionManager : public gse::Wrappable { public: - Factions(); - ~Factions(); + FactionManager(); + ~FactionManager(); void Add( Faction* faction ); void Remove( const std::string& id ); @@ -19,7 +20,7 @@ class Factions : public gse::Wrappable { Faction* Get( const std::string& id ) const; const std::vector< Faction* > GetAll() const; - WRAPDEFS_PTR( Factions ) + WRAPDEFS_PTR( FactionManager ) const types::Buffer Serialize() const; void Unserialize( types::Buffer buf ); @@ -38,3 +39,4 @@ class Factions : public gse::Wrappable { } } +} diff --git a/src/game/backend/faction/Types.h b/src/game/backend/faction/Types.h new file mode 100644 index 00000000..4ac0933c --- /dev/null +++ b/src/game/backend/faction/Types.h @@ -0,0 +1,24 @@ +#pragma once + +#include + +namespace game { +namespace backend { +namespace faction { + +struct bases_render_info_t { + std::string file = ""; + size_t grid_x = 0; + size_t grid_y = 0; + size_t cell_width = 0; + size_t cell_height = 0; + size_t cell_cx = 0; + size_t cell_cy = 0; + size_t cell_padding = 0; + float scale_x = 1.0f; + float scale_y = 1.0f; +}; + +} +} +} diff --git a/src/game/backend/slot/Slot.cpp b/src/game/backend/slot/Slot.cpp index 5616274f..f0e4e5c3 100644 --- a/src/game/backend/slot/Slot.cpp +++ b/src/game/backend/slot/Slot.cpp @@ -2,6 +2,7 @@ #include "game/backend/State.h" #include "game/backend/Player.h" +#include "game/backend/faction/Faction.h" #include "gse/type/String.h" #include "gse/type/Int.h" diff --git a/src/game/backend/unit/CMakeLists.txt b/src/game/backend/unit/CMakeLists.txt index 32dde5ac..fdef1269 100644 --- a/src/game/backend/unit/CMakeLists.txt +++ b/src/game/backend/unit/CMakeLists.txt @@ -1,5 +1,6 @@ SET( SRC ${SRC} + ${PWD}/UnitManager.cpp ${PWD}/Def.cpp ${PWD}/StaticDef.cpp ${PWD}/Render.cpp diff --git a/src/game/backend/unit/Unit.cpp b/src/game/backend/unit/Unit.cpp index e18d74a4..2028358f 100644 --- a/src/game/backend/unit/Unit.cpp +++ b/src/game/backend/unit/Unit.cpp @@ -14,6 +14,7 @@ #include "game/backend/map/Map.h" #include "MoraleSet.h" #include "StaticDef.h" +#include "UnitManager.h" namespace game { namespace backend { @@ -28,7 +29,7 @@ const void Unit::SetNextId( const size_t id ) { } Unit::Unit( - Game* game, + UnitManager* um, const size_t id, Def* def, slot::Slot* owner, @@ -38,8 +39,8 @@ Unit::Unit( const health_t health, const bool moved_this_turn ) - : MapObject( game->GetMap(), tile ) - , m_game( game ) + : MapObject( um->GetMap(), tile ) + , m_um( um ) , m_id( id ) , m_def( def ) , m_owner( owner ) @@ -76,7 +77,7 @@ void Unit::SetTile( map::tile::Tile* tile ) { } ); m_tile = tile; - m_game->RefreshUnit( this ); + m_um->RefreshUnit( this ); } const types::Buffer Unit::Serialize( const Unit* unit ) { @@ -93,19 +94,19 @@ const types::Buffer Unit::Serialize( const Unit* unit ) { return buf; } -Unit* Unit::Unserialize( types::Buffer& buf, Game* game ) { +Unit* Unit::Unserialize( types::Buffer& buf, UnitManager* units ) { const auto id = buf.ReadInt(); auto defbuf = types::Buffer( buf.ReadString() ); auto* def = Def::Unserialize( defbuf ); - auto* slot = game ? &game->GetState()->m_slots->GetSlot( buf.ReadInt() ) : nullptr; + auto* slot = units ? units->GetSlot( buf.ReadInt() ) : nullptr; const auto pos_x = buf.ReadInt(); const auto pos_y = buf.ReadInt(); - auto* tile = game ? game->GetMap()->GetTile( pos_x, pos_y ) : nullptr; + auto* tile = units ? units->GetMap()->GetTile( pos_x, pos_y ) : nullptr; const auto movement = (movement_t)buf.ReadFloat(); const auto morale = (morale_t)buf.ReadInt(); const auto health = (health_t)buf.ReadFloat(); const auto moved_this_turn = buf.ReadBool(); - return new Unit( game, id, def, slot, tile, movement, morale, health, moved_this_turn ); + return new Unit( units, id, def, slot, tile, movement, morale, health, moved_this_turn ); } WRAPIMPL_DYNAMIC_GETTERS( Unit, CLASS_UNIT ) @@ -138,7 +139,7 @@ WRAPIMPL_DYNAMIC_GETTERS( Unit, CLASS_UNIT ) N_EXPECT_ARGS( 2 ); N_GETVALUE_UNWRAP( tile, 0, map::tile::Tile ); N_PERSIST_CALLABLE( on_complete, 1 ); - const auto* errmsg = m_game->MoveUnitToTile( this, tile, [ on_complete, ctx, call_si ]() { + const auto* errmsg = m_um->MoveUnitToTile( this, tile, [ on_complete, ctx, call_si ]() { on_complete->Run( ctx, call_si, {} ); N_UNPERSIST_CALLABLE( on_complete ); }); @@ -156,7 +157,7 @@ WRAPIMPL_DYNAMIC_SETTERS( Unit ) WRAPIMPL_DYNAMIC_ON_SET( Unit ) // this is potentially risky because if it gets zero health it will be despawned without script's awareness, how to handle it? // maybe despawn unit from within script? but then it would be script's responsibility to ensure there are no zero-health units walking around - m_game->RefreshUnit( this ); + m_um->RefreshUnit( this ); WRAPIMPL_DYNAMIC_END() UNWRAPIMPL_PTR( Unit ) diff --git a/src/game/backend/unit/Unit.h b/src/game/backend/unit/Unit.h index 41ce93a8..1b8acfe8 100644 --- a/src/game/backend/unit/Unit.h +++ b/src/game/backend/unit/Unit.h @@ -12,7 +12,6 @@ namespace game { namespace backend { -class Game; namespace slot { class Slot; } @@ -23,6 +22,7 @@ class Tile; namespace unit { class Def; +class UnitManager; class Unit : public gse::Wrappable, public MapObject { public: @@ -31,7 +31,7 @@ class Unit : public gse::Wrappable, public MapObject { static const void SetNextId( const size_t id ); Unit( - Game* game, + UnitManager* um, const size_t id, Def* def, slot::Slot* owner, @@ -61,12 +61,12 @@ class Unit : public gse::Wrappable, public MapObject { void SetTile( map::tile::Tile* tile ); static const types::Buffer Serialize( const Unit* unit ); - static Unit* Unserialize( types::Buffer& buf, Game* game ); + static Unit* Unserialize( types::Buffer& buf, UnitManager* units ); WRAPDEFS_DYNAMIC( Unit ); private: - Game* const m_game; + UnitManager* const m_um; }; diff --git a/src/game/backend/unit/UnitManager.cpp b/src/game/backend/unit/UnitManager.cpp new file mode 100644 index 00000000..2bd2efec --- /dev/null +++ b/src/game/backend/unit/UnitManager.cpp @@ -0,0 +1,638 @@ +#include "UnitManager.h" + +#include "MoraleSet.h" +#include "Def.h" +#include "Unit.h" + +#include "game/backend/Game.h" +#include "game/backend/State.h" +#include "game/backend/bindings/Bindings.h" +#include "game/backend/slot/Slots.h" +#include "game/backend/event/DespawnUnit.h" + +#include "gse/callable/Native.h" +#include "gse/type/Bool.h" +#include "gse/type/Float.h" +#include "gse/type/Array.h" + +namespace game { +namespace backend { +namespace unit { + +UnitManager::UnitManager( Game* game ) + : m_game( game ) { + // +} + +UnitManager::~UnitManager() { + Clear(); +} + +void UnitManager::Clear() { + for ( auto& it : m_unprocessed_units ) { + delete it; + } + m_unprocessed_units.clear(); + + for ( auto& it : m_unit_moralesets ) { + delete it.second; + } + m_unit_moralesets.clear(); + + for ( auto& it : m_units ) { + delete it.second; + } + m_units.clear(); + + for ( auto& it : m_unit_defs ) { + delete it.second; + } + m_unit_defs.clear(); + + m_unit_updates.clear(); +} + +void UnitManager::DefineMoraleSet( unit::MoraleSet* moraleset ) { + Log( "Defining unit moraleset ('" + moraleset->m_id + "')" ); + + ASSERT( m_unit_moralesets.find( moraleset->m_id ) == m_unit_moralesets.end(), "Unit moraleset '" + moraleset->m_id + "' already exists" ); + + m_unit_moralesets.insert( + { + moraleset->m_id, + moraleset + } + ); +} + +void UnitManager::DefineUnit( unit::Def* def ) { + Log( "Defining unit ('" + def->m_id + "')" ); + + ASSERT( m_unit_defs.find( def->m_id ) == m_unit_defs.end(), "Unit definition '" + def->m_id + "' already exists" ); + + m_unit_defs.insert( + { + def->m_id, + def + } + ); + + auto fr = FrontendRequest( FrontendRequest::FR_UNIT_DEFINE ); + NEW( fr.data.unit_define.serialized_unitdef, std::string, unit::Def::Serialize( def ).ToString() ); + m_game->AddFrontendRequest( fr ); +} + +void UnitManager::SpawnUnit( Unit* unit ) { + if ( !m_game->IsRunning() ) { + m_unprocessed_units.push_back( unit ); + return; + } + + auto* tile = unit->GetTile(); + + Log( "Spawning unit #" + std::to_string( unit->m_id ) + " (" + unit->m_def->m_id + ") at " + tile->ToString() ); + + ASSERT( m_units.find( unit->m_id ) == m_units.end(), "duplicate unit id" ); + m_units.insert_or_assign( unit->m_id, unit ); + + QueueUnitUpdate( unit, UUO_SPAWN ); + + auto* state = m_game->GetState(); + if ( state->IsMaster() ) { + state->m_bindings->Call( + bindings::Bindings::CS_ON_UNIT_SPAWN, { + { + "unit", + unit->Wrap() + }, + } + ); + } +} + +void UnitManager::DespawnUnit( const size_t unit_id ) { + + const auto& it = m_units.find( unit_id ); + ASSERT( it != m_units.end(), "unit id not found" ); + auto* unit = it->second; + + Log( "Despawning unit #" + std::to_string( unit->m_id ) + " (" + unit->m_def->m_id + ") at " + unit->GetTile()->ToString() ); + + QueueUnitUpdate( unit, UUO_DESPAWN ); + + auto* tile = unit->GetTile(); + ASSERT( tile, "unit tile not set" ); + const auto& tile_it = tile->units.find( unit->m_id ); + ASSERT( tile_it != tile->units.end(), "unit id not found in tile" ); + tile->units.erase( tile_it ); + + m_units.erase( it ); + + auto* state = m_game->GetState(); + if ( state->IsMaster() ) { + state->m_bindings->Call( + bindings::Bindings::CS_ON_UNIT_DESPAWN, { + { + "unit", + unit->Wrap() + } + } + ); + } + + delete unit; +} + +void UnitManager::SkipUnitTurn( const size_t unit_id ) { + const auto& it = m_units.find( unit_id ); + ASSERT( it != m_units.end(), "unit id not found" ); + auto* unit = it->second; + + Log( "Skipping unit turn #" + std::to_string( unit->m_id ) + " (" + unit->m_def->m_id + ") at " + unit->GetTile()->ToString() ); + + unit->m_movement = 0.0f; + + RefreshUnit( unit ); +} + +unit::MoraleSet* UnitManager::GetMoraleSet( const std::string& name ) const { + const auto& it = m_unit_moralesets.find( name ); + if ( it != m_unit_moralesets.end() ) { + return it->second; + } + return nullptr; +} + +unit::Unit* UnitManager::GetUnit( const size_t id ) const { + const auto& it = m_units.find( id ); + if ( it != m_units.end() ) { + return it->second; + } + return nullptr; +} + +unit::Def* UnitManager::GetUnitDef( const std::string& name ) const { + const auto& it = m_unit_defs.find( name ); + if ( it != m_unit_defs.end() ) { + return it->second; + } + else { + return nullptr; + } +} + +const std::map< size_t, Unit* >& UnitManager::GetUnits() const { + return m_units; +} + +void UnitManager::ProcessUnprocessed() { + ASSERT( m_game->IsRunning(), "game not running" ); + for ( auto& it : m_unprocessed_units ) { + SpawnUnit( it ); + } + m_unprocessed_units.clear(); +} + +void UnitManager::PushUnitUpdates() { + if ( m_game->IsRunning() && !m_unit_updates.empty() ) { + for ( const auto& it : m_unit_updates ) { + const auto unit_id = it.first; + const auto& uu = it.second; + const auto& unit = uu.unit; + if ( uu.ops & UUO_SPAWN ) { + auto fr = FrontendRequest( FrontendRequest::FR_UNIT_SPAWN ); + fr.data.unit_spawn.unit_id = unit->m_id; + NEW( fr.data.unit_spawn.unitdef_id, std::string, unit->m_def->m_id ); + fr.data.unit_spawn.slot_index = unit->m_owner->GetIndex(); + const auto* tile = unit->GetTile(); + fr.data.unit_spawn.tile_coords = { + tile->coord.x, + tile->coord.y + }; + const auto c = unit->GetRenderCoords(); + fr.data.unit_spawn.render_coords = { + c.x, + c.y, + c.z + }; + + fr.data.unit_spawn.movement = unit->m_movement; + fr.data.unit_spawn.morale = unit->m_morale; + NEW( fr.data.unit_spawn.morale_string, std::string, unit->GetMoraleString() ); + fr.data.unit_spawn.health = unit->m_health; + m_game->AddFrontendRequest( fr ); + } + if ( uu.ops & UUO_REFRESH ) { + auto fr = FrontendRequest( FrontendRequest::FR_UNIT_UPDATE ); + fr.data.unit_update.unit_id = unit->m_id; + fr.data.unit_update.movement = unit->m_movement; + fr.data.unit_update.health = unit->m_health; + const auto* tile = unit->GetTile(); + fr.data.unit_update.tile_coords = { + tile->coord.x, + tile->coord.y + }; + const auto c = unit->GetRenderCoords(); + fr.data.unit_update.render_coords = { + c.x, + c.y, + c.z + }; + m_game->AddFrontendRequest( fr ); + } + if ( uu.ops & UUO_DESPAWN ) { + auto fr = FrontendRequest( FrontendRequest::FR_UNIT_DESPAWN ); + fr.data.unit_despawn.unit_id = unit_id; + m_game->AddFrontendRequest( fr ); + } + } + m_unit_updates.clear(); + m_game->CheckTurnComplete(); + } +} + +WRAPIMPL_BEGIN( UnitManager, CLASS_UNITS ) + WRAPIMPL_PROPS +/* { + "add", + NATIVE_CALL( this ) { + N_EXPECT_ARGS( 2 ); + N_GETVALUE( id, 0, String ); + + N_GETVALUE( faction_def, 1, Object ); + N_GETPROP( name, faction_def, "name", String ); + + auto* faction = new Faction( id, name ); + + N_GETPROP( colors, faction_def, "colors", Object ); + N_GETPROP_UNWRAP( colors_text, colors, "text", types::Color ); + N_GETPROP_UNWRAP( colors_text_shadow, colors, "text_shadow", types::Color ); + N_GETPROP_UNWRAP( colors_border, colors, "border", types::Color ); + faction->m_colors = { + colors_text, + colors_text_shadow, + colors_border + }; + + N_GETPROP_OPT_BOOL( is_naval, faction_def, "is_naval") + if ( is_naval ) { + faction->m_flags |= Faction::FF_NAVAL; + } + N_GETPROP_OPT_BOOL( is_progenitor, faction_def, "is_progenitor") + if ( is_progenitor ) { + faction->m_flags |= Faction::FF_PROGENITOR; + } + + N_GETPROP( bases_def, faction_def, "bases", Object ); + N_GETPROP( bases_render_def, bases_def, "render", Object ); + + N_GETPROP( bases_render_type, bases_render_def, "type", String ); + if ( bases_render_type == "sprite_grid" ) { + N_GETPROP( file, bases_render_def, "file", String ); + N_GETPROP( grid_x, bases_render_def, "grid_x", Int ); + N_GETPROP( grid_y, bases_render_def, "grid_y", Int ); + N_GETPROP( cell_width, bases_render_def, "cell_width", Int ); + N_GETPROP( cell_height, bases_render_def, "cell_height", Int ); + N_GETPROP_OPT( size_t, cell_cx, bases_render_def, "cell_cx", Int, cell_width / 2 ); + N_GETPROP_OPT( size_t, cell_cy, bases_render_def, "cell_cy", Int, cell_height / 2 ); + N_GETPROP( cell_padding, bases_render_def, "cell_padding", Int ); + N_GETPROP_OPT( float, scale_x, bases_render_def, "scale_x", Float, 1.0f ); + N_GETPROP_OPT( float, scale_y, bases_render_def, "scale_y", Float, 1.0f ); + faction->m_bases_render = { + file, + (size_t)grid_x, + (size_t)grid_y, + (size_t)cell_width, + (size_t)cell_height, + cell_cx, + cell_cy, + (size_t)cell_padding, + scale_x, + scale_y + }; + } + else { + delete faction; + GSE_ERROR( gse::EC.GAME_ERROR, "Unsupported bases render type: " + bases_render_type ); + } + + N_GETPROP( base_names, bases_def, "names", Object ); + N_GETPROP( base_names_land, base_names, "land", Array ); + faction->m_base_names.land.reserve( base_names_land.size() ); + for ( size_t i = 0 ; i < base_names_land.size() ; i++ ) { + N_GETELEMENT( v, base_names_land, i, String ); + faction->m_base_names.land.push_back( v ); + } + + N_GETPROP( base_names_water, base_names, "water", Array ); + faction->m_base_names.water.reserve( base_names_water.size() ); + for ( size_t i = 0 ; i < base_names_water.size() ; i++ ) { + N_GETELEMENT( v, base_names_water, i, String ); + faction->m_base_names.water.push_back( v ); + } + + Add( faction ); + return faction->Wrap(); + } ) + },*/ + }; +WRAPIMPL_END_PTR( UnitManager ) + +UNWRAPIMPL_PTR( UnitManager ) + +void UnitManager::Serialize( types::Buffer& buf ) const { + + Log( "Serializing " + std::to_string( m_unit_moralesets.size() ) + " unit moralesets" ); + buf.WriteInt( m_unit_moralesets.size() ); + for ( const auto& it : m_unit_moralesets ) { + buf.WriteString( it.first ); + buf.WriteString( MoraleSet::Serialize( it.second ).ToString() ); + } + + Log( "Serializing " + std::to_string( m_unit_defs.size() ) + " unit defs" ); + buf.WriteInt( m_unit_defs.size() ); + for ( const auto& it : m_unit_defs ) { + buf.WriteString( it.first ); + buf.WriteString( Def::Serialize( it.second ).ToString() ); + } + + Log( "Serializing " + std::to_string( m_units.size() ) + " units" ); + buf.WriteInt( m_units.size() ); + for ( const auto& it : m_units ) { + buf.WriteInt( it.first ); + buf.WriteString( Unit::Serialize( it.second ).ToString() ); + } + buf.WriteInt( Unit::GetNextId() ); + + Log( "Saved next unit id: " + std::to_string( Unit::GetNextId() ) ); +} + +void UnitManager::Unserialize( types::Buffer& buf ) { + ASSERT( m_unit_moralesets.empty(), "unit moralesets not empty" ); + ASSERT( m_unit_defs.empty(), "unit defs not empty" ); + ASSERT( m_units.empty(), "units not empty" ); + ASSERT( m_unprocessed_units.empty(), "unprocessed units not empty" ); + + size_t sz = buf.ReadInt(); + Log( "Unserializing " + std::to_string( sz ) + " unit moralesets" ); + m_unit_moralesets.reserve( sz ); + for ( size_t i = 0 ; i < sz ; i++ ) { + const auto name = buf.ReadString(); + auto b = types::Buffer( buf.ReadString() ); + DefineMoraleSet( unit::MoraleSet::Unserialize( b ) ); + } + + sz = buf.ReadInt(); + Log( "Unserializing " + std::to_string( sz ) + " unit defs" ); + m_unit_defs.reserve( sz ); + for ( size_t i = 0 ; i < sz ; i++ ) { + const auto name = buf.ReadString(); + auto b = types::Buffer( buf.ReadString() ); + DefineUnit( unit::Def::Unserialize( b ) ); + } + + sz = buf.ReadInt(); + Log( "Unserializing " + std::to_string( sz ) + " units" ); + if ( !m_game->IsRunning() ) { + m_unprocessed_units.reserve( sz ); + } + for ( size_t i = 0 ; i < sz ; i++ ) { + const auto unit_id = buf.ReadInt(); + auto b = types::Buffer( buf.ReadString() ); + SpawnUnit( Unit::Unserialize( b, this ) ); + } + + Unit::SetNextId( buf.ReadInt() ); + Log( "Restored next unit id: " + std::to_string( Unit::GetNextId() ) ); +} + +void UnitManager::QueueUnitUpdate( const Unit* unit, const unit_update_op_t op ) { + auto it = m_unit_updates.find( unit->m_id ); + if ( it == m_unit_updates.end() ) { + it = m_unit_updates.insert( + { + unit->m_id, + { + {}, + unit, + } + } + ).first; + } + auto& update = it->second; + if ( op == UUO_DESPAWN ) { + if ( op & UUO_SPAWN ) { + // if unit is despawned immediately after spawning - frontend doesn't need to know + m_unit_updates.erase( it ); + return; + } + update.ops = UUO_NONE; // clear other actions if unit was despawned + } + // add to operations list + update.ops = (unit_update_op_t)( (uint8_t)update.ops | (uint8_t)op ); +} + +const std::string* UnitManager::MoveUnitValidate( Unit* unit, map::tile::Tile* dst_tile ) { + const auto result = m_game->GetState()->m_bindings->Call( + bindings::Bindings::CS_ON_UNIT_MOVE_VALIDATE, { + { + "unit", + unit->Wrap() + }, + { + "src_tile", + unit->GetTile()->Wrap() + }, + { + "dst_tile", + dst_tile->Wrap() + }, + } + ); + switch ( result.Get()->type ) { + case gse::type::Type::T_NULL: + case gse::type::Type::T_UNDEFINED: + return nullptr; // no errors + case gse::type::Type::T_STRING: + return new std::string( ( (gse::type::String*)result.Get() )->value ); // error + default: + THROW( "unexpected validation result type: " + gse::type::Type::GetTypeString( result.Get()->type ) ); + } +} + +const gse::Value UnitManager::MoveUnitResolve( Unit* unit, map::tile::Tile* dst_tile ) { + return m_game->GetState()->m_bindings->Call( + bindings::Bindings::CS_ON_UNIT_MOVE_RESOLVE, { + { + "unit", + unit->Wrap() + }, + { + "src_tile", + unit->GetTile()->Wrap() + }, + { + "dst_tile", + dst_tile->Wrap() + }, + } + ); +} + +void UnitManager::MoveUnitApply( Unit* unit, map::tile::Tile* dst_tile, const gse::Value resolutions ) { + ASSERT( dst_tile, "dst tile not set" ); + + Log( "Moving unit #" + std::to_string( unit->m_id ) + " to " + dst_tile->coord.ToString() ); + + auto* src_tile = unit->GetTile(); + ASSERT( src_tile, "src tile not set" ); + ASSERT( src_tile->units.find( unit->m_id ) != src_tile->units.end(), "src tile does not contain this unit" ); + ASSERT( dst_tile->units.find( unit->m_id ) == dst_tile->units.end(), "dst tile already contains this unit" ); + + const auto result = m_game->GetState()->m_bindings->Call( + bindings::Bindings::CS_ON_UNIT_MOVE_APPLY, { + { + "unit", + unit->Wrap( true ) + }, + { + "src_tile", + src_tile->Wrap() + }, + { + "dst_tile", + dst_tile->Wrap() + }, + { + "resolutions", + resolutions + } + } + ); +} + +const std::string* UnitManager::MoveUnitToTile( Unit* unit, map::tile::Tile* dst_tile, const cb_oncomplete& on_complete ) { + const auto* src_tile = unit->GetTile(); + if ( src_tile == dst_tile ) { + return new std::string( "Unit can't move because it's already on target tile" ); + } + if ( !src_tile->IsLocked() ) { + return new std::string( "Source tile must be locked before moving unit" ); + } + if ( !dst_tile->IsLocked() ) { + return new std::string( "Destination tile must be locked before moving unit" ); + } + auto fr = FrontendRequest( FrontendRequest::FR_UNIT_MOVE ); + fr.data.unit_move.unit_id = unit->m_id; + fr.data.unit_move.dst_tile_coords = { + dst_tile->coord.x, + dst_tile->coord.y + }; + fr.data.unit_move.running_animation_id = m_game->AddAnimationCallback( + [ on_complete, unit, dst_tile ]() { + unit->SetTile( dst_tile ); + on_complete(); + } + ); + m_game->AddFrontendRequest( fr ); + return nullptr; // no error +} + +const std::string* UnitManager::AttackUnitValidate( Unit* attacker, Unit* defender ) { + const auto result = m_game->GetState()->m_bindings->Call( + bindings::Bindings::CS_ON_UNIT_ATTACK_VALIDATE, { + { + "attacker", + attacker->Wrap() + }, + { + "defender", + defender->Wrap() + }, + } + ); + switch ( result.Get()->type ) { + case gse::type::Type::T_NULL: + case gse::type::Type::T_UNDEFINED: + return nullptr; // no errors + case gse::type::Type::T_STRING: + return new std::string( ( (gse::type::String*)result.Get() )->value ); // error + default: + THROW( "unexpected validation result type: " + gse::type::Type::GetTypeString( result.Get()->type ) ); + } + return nullptr; +} + +const gse::Value UnitManager::AttackUnitResolve( Unit* attacker, Unit* defender ) { + return m_game->GetState()->m_bindings->Call( + bindings::Bindings::CS_ON_UNIT_ATTACK_RESOLVE, { + { + "attacker", + attacker->Wrap() + }, + { + "defender", + defender->Wrap() + }, + } + ); +} + +void UnitManager::AttackUnitApply( Unit* attacker, Unit* defender, const gse::Value resolutions ) { + auto* state = m_game->GetState(); + state->m_bindings->Call( + bindings::Bindings::CS_ON_UNIT_ATTACK_APPLY, { + { + "attacker", + attacker->Wrap( true ) + }, + { + "defender", + defender->Wrap( true ) + }, + { + "resolutions", + resolutions + } + } + ); + if ( attacker->m_health <= 0.0f ) { + if ( state->IsMaster() ) { + m_game->AddEvent( new event::DespawnUnit( m_game->GetSlotNum(), attacker->m_id ) ); + } + } + else { + RefreshUnit( attacker ); + } + if ( defender->m_health <= 0.0f ) { + if ( state->IsMaster() ) { + m_game->AddEvent( new event::DespawnUnit( m_game->GetSlotNum(), defender->m_id ) ); + } + } + else { + RefreshUnit( defender ); + } +} + +void UnitManager::RefreshUnit( const unit::Unit* unit ) { + if ( unit->m_health <= 0.0f ) { + if ( m_game->GetState()->IsMaster() ) { + m_game->AddEvent( new event::DespawnUnit( m_game->GetSlotNum(), unit->m_id ) ); + } + } + else { + QueueUnitUpdate( unit, UUO_REFRESH ); + } +} + +map::Map* UnitManager::GetMap() const { + return m_game->GetMap(); +} + +slot::Slot* UnitManager::GetSlot( const size_t slot_num ) const { + return &m_game->GetState()->m_slots->GetSlot( slot_num ); +} + +} +} +} diff --git a/src/game/backend/unit/UnitManager.h b/src/game/backend/unit/UnitManager.h new file mode 100644 index 00000000..bc137e27 --- /dev/null +++ b/src/game/backend/unit/UnitManager.h @@ -0,0 +1,96 @@ +#pragma once + +#include "common/Common.h" + +#include "gse/Wrappable.h" +#include "gse/type/Object.h" + +namespace game { +namespace backend { + +class Game; + +namespace map { +class Map; +namespace tile { +class Tile; +} +} + +namespace slot { +class Slot; +} + +namespace unit { + +class MoraleSet; +class Def; +class Unit; + +CLASS2( UnitManager, common::Class, gse::Wrappable ) +public: + UnitManager( Game* game ); + ~UnitManager(); + + void Clear(); + void DefineMoraleSet( unit::MoraleSet* moraleset ); + void DefineUnit( unit::Def* def ); + void SpawnUnit( Unit* unit ); + void DespawnUnit( const size_t unit_id ); + void SkipUnitTurn( const size_t unit_id ); + + unit::MoraleSet* GetMoraleSet( const std::string& name ) const; + unit::Unit* GetUnit( const size_t id ) const; + unit::Def* GetUnitDef( const std::string& name ) const; + const std::map< size_t, Unit* >& GetUnits() const; + + void ProcessUnprocessed(); + void PushUnitUpdates(); + + WRAPDEFS_PTR( UnitManager ) + + void Serialize( types::Buffer& buf ) const; + void Unserialize( types::Buffer& buf ); + +public: + // TODO: limit access + typedef std::function< void() > cb_oncomplete; + const std::string* MoveUnitValidate( Unit* unit, map::tile::Tile* dst_tile ); + const gse::Value MoveUnitResolve( Unit* unit, map::tile::Tile* dst_tile ); + void MoveUnitApply( Unit* unit, map::tile::Tile* dst_tile, const gse::Value resolutions ); + const std::string* MoveUnitToTile( Unit* unit, map::tile::Tile* dst_tile, const cb_oncomplete& on_complete ); + const std::string* AttackUnitValidate( Unit* attacker, Unit* defender ); + const gse::Value AttackUnitResolve( Unit* attacker, Unit* defender ); + void AttackUnitApply( Unit* attacker, Unit* defender, const gse::Value resolutions ); + void RefreshUnit( const Unit* unit ); + +private: + Game* m_game = nullptr; + + std::unordered_map< std::string, MoraleSet* > m_unit_moralesets = {}; + std::unordered_map< std::string, Def* > m_unit_defs = {}; + std::map< size_t, Unit* > m_units = {}; + std::vector< Unit* > m_unprocessed_units = {}; + + enum unit_update_op_t : uint8_t { + UUO_NONE = 0, + UUO_SPAWN = 1 << 0, + UUO_REFRESH = 1 << 1, + UUO_DESPAWN = 1 << 2, + }; + struct unit_update_t { + unit_update_op_t ops = UUO_NONE; + const Unit* unit = nullptr; + }; + std::unordered_map< size_t, unit_update_t > m_unit_updates = {}; + void QueueUnitUpdate( const Unit* unit, const unit_update_op_t op ); + +private: + friend class Unit; + map::Map* GetMap() const; + slot::Slot* GetSlot( const size_t slot_num ) const; +}; + +} +} +} diff --git a/src/game/frontend/faction/Faction.cpp b/src/game/frontend/faction/Faction.cpp index 6c9d5619..6bafb6ba 100644 --- a/src/game/frontend/faction/Faction.cpp +++ b/src/game/frontend/faction/Faction.cpp @@ -1,6 +1,6 @@ #include "Faction.h" -#include "game/backend/Faction.h" +#include "game/backend/faction/Faction.h" #include "game/frontend/sprite/InstancedSpriteManager.h" #include "engine/Engine.h" #include "loader/texture/TextureLoader.h" @@ -11,7 +11,7 @@ namespace game { namespace frontend { namespace faction { -Faction::Faction( const backend::Faction* def, sprite::InstancedSpriteManager* ism ) +Faction::Faction( const backend::faction::Faction* def, sprite::InstancedSpriteManager* ism ) : m_ism( ism ) , m_id( def->m_id ) , m_colors( @@ -27,7 +27,7 @@ Faction::Faction( const backend::Faction* def, sprite::InstancedSpriteManager* i def->m_base_names.water, } ) - , m_is_progenitor( def->m_flags & backend::Faction::FF_PROGENITOR ) + , m_is_progenitor( def->m_flags & backend::faction::Faction::FF_PROGENITOR ) , m_render( { def->m_bases_render, diff --git a/src/game/frontend/faction/Faction.h b/src/game/frontend/faction/Faction.h index 0d251cc0..daa3a2f2 100644 --- a/src/game/frontend/faction/Faction.h +++ b/src/game/frontend/faction/Faction.h @@ -4,7 +4,7 @@ #include #include -#include "game/backend/Types.h" +#include "game/backend/faction/Types.h" #include "types/Color.h" #include "game/frontend/sprite/Sprite.h" @@ -16,7 +16,7 @@ class Texture; namespace game { -namespace backend { +namespace backend::faction { class Faction; } @@ -31,7 +31,7 @@ namespace faction { class Faction { public: - Faction( const backend::Faction* def, sprite::InstancedSpriteManager* ism ); + Faction( const backend::faction::Faction* def, sprite::InstancedSpriteManager* ism ); sprite::Sprite* GetBaseSprite( const bool is_water, const uint8_t size, const uint8_t perimeter_level ); @@ -55,7 +55,7 @@ class Faction { sprite::InstancedSpriteManager* m_ism = nullptr; struct { - backend::faction_bases_render_info_t bases_render; + backend::faction::bases_render_info_t bases_render; } m_render = {}; types::texture::Texture* m_base_grid_texture = nullptr; diff --git a/src/game/frontend/faction/FactionManager.cpp b/src/game/frontend/faction/FactionManager.cpp index 3e1c6a51..20f0cd65 100644 --- a/src/game/frontend/faction/FactionManager.cpp +++ b/src/game/frontend/faction/FactionManager.cpp @@ -1,7 +1,7 @@ #include "FactionManager.h" #include "game/frontend/Game.h" -#include "game/backend/Faction.h" +#include "game/backend/faction/Faction.h" #include "Faction.h" namespace game { @@ -13,7 +13,7 @@ FactionManager::FactionManager( Game* game ) } -void FactionManager::DefineFaction( const backend::Faction* def ) { +void FactionManager::DefineFaction( const backend::faction::Faction* def ) { ASSERT( def, "faction is null" ); ASSERT( m_factions.find( def->m_id ) == m_factions.end(), "faction already defined" ); NEWV( faction, Faction, def, m_game->GetISM() ); diff --git a/src/game/frontend/faction/FactionManager.h b/src/game/frontend/faction/FactionManager.h index 906cb16f..16186673 100644 --- a/src/game/frontend/faction/FactionManager.h +++ b/src/game/frontend/faction/FactionManager.h @@ -6,7 +6,7 @@ namespace game { -namespace backend { +namespace backend::faction { class Faction; } @@ -22,7 +22,7 @@ CLASS( FactionManager, common::Class ) FactionManager( Game* game ); - void DefineFaction( const backend::Faction* def ); + void DefineFaction( const backend::faction::Faction* def ); Faction* GetFactionById( const std::string& id ) const; diff --git a/src/gse/type/Object.h b/src/gse/type/Object.h index 1d43fe2f..4ca362af 100644 --- a/src/gse/type/Object.h +++ b/src/gse/type/Object.h @@ -35,6 +35,7 @@ class Object : public Type { CLASS_PLAYER, CLASS_FACTION, CLASS_FACTIONS, + CLASS_UNITS, CLASS_UNITDEF, CLASS_UNIT, CLASS_BASE, diff --git a/src/main.cpp b/src/main.cpp index af998d43..29d39f1c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -64,7 +64,8 @@ #include "version.h" #include "game/backend/State.h" -#include "game/backend/Factions.h" +#include "game/backend/faction/Faction.h" +#include "game/backend/faction/FactionManager.h" #include "game/backend/Player.h" #include "game/backend/slot/Slots.h" @@ -282,12 +283,12 @@ int main( const int argc, const char* argv[] ) { state->InitBindings(); state->Configure(); const auto& rules = state->m_settings.global.game_rules; - game::backend::Faction* faction = nullptr; + game::backend::faction::Faction* faction = nullptr; if ( config.HasLaunchFlag( config::Config::LF_QUICKSTART_FACTION ) ) { - faction = state->GetFactions()->Get( config.GetQuickstartFaction() ); + faction = state->GetFM()->Get( config.GetQuickstartFaction() ); if ( !faction ) { std::string errmsg = "Faction \"" + config.GetQuickstartFaction() + "\" does not exist. Available factions:"; - for ( const auto& f : state->GetFactions()->GetAll() ) { + for ( const auto& f : state->GetFM()->GetAll() ) { errmsg += " " + f->m_id; } THROW( errmsg ); diff --git a/src/task/mainmenu/menu/lobby/PlayersSection.cpp b/src/task/mainmenu/menu/lobby/PlayersSection.cpp index 1cb6e9f4..b86ac1e9 100644 --- a/src/task/mainmenu/menu/lobby/PlayersSection.cpp +++ b/src/task/mainmenu/menu/lobby/PlayersSection.cpp @@ -4,8 +4,8 @@ #include "game/backend/slot/Slot.h" #include "game/backend/State.h" -#include "game/backend/Factions.h" -#include "game/backend/Faction.h" +#include "game/backend/faction/FactionManager.h" +#include "game/backend/faction/Faction.h" #include "PlayersSectionRow.h" #include "Lobby.h" @@ -68,7 +68,7 @@ void PlayersSection::UpdateSlots( std::vector< game::backend::slot::Slot >& slot "Random" } ); - for ( const auto& faction : GetLobby()->GetState()->GetFactions()->GetAll() ) { + for ( const auto& faction : GetLobby()->GetState()->GetFM()->GetAll() ) { m_choices.factions.push_back( { faction->m_id, diff --git a/src/task/mainmenu/menu/lobby/PlayersSectionRow.cpp b/src/task/mainmenu/menu/lobby/PlayersSectionRow.cpp index d1dfdec1..dad130d5 100644 --- a/src/task/mainmenu/menu/lobby/PlayersSectionRow.cpp +++ b/src/task/mainmenu/menu/lobby/PlayersSectionRow.cpp @@ -3,8 +3,8 @@ #include "engine/Engine.h" #include "game/backend/slot/Slot.h" #include "game/backend/State.h" -#include "game/backend/Factions.h" -#include "game/backend/Faction.h" +#include "game/backend/faction/FactionManager.h" +#include "game/backend/faction/Faction.h" #include "PlayersSection.h" #include "Lobby.h" #include "game/backend/settings/Settings.h" @@ -94,7 +94,7 @@ void PlayersSectionRow::Create() { player->ClearFaction(); } else { - auto* faction = m_parent->GetLobby()->GetState()->GetFactions()->Get( faction_id ); + auto* faction = m_parent->GetLobby()->GetState()->GetFM()->Get( faction_id ); ASSERT( faction, "faction not found: " + faction_id ); player->SetFaction( faction ); } From 1ec3d397f1c4b112a1307d9302308c8040acb008 Mon Sep 17 00:00:00 2001 From: afwbkbc Date: Thu, 7 Nov 2024 19:52:18 +0200 Subject: [PATCH 04/13] refactored unit manager interface a bit --- GLSMAC_data/default/factions.gls.js | 4 +- GLSMAC_data/default/main.gls.js | 6 +- GLSMAC_data/default/units.gls.js | 8 +- GLSMAC_data/default/units/combat.gls.js | 10 +- GLSMAC_data/default/units/movement.gls.js | 10 +- GLSMAC_data/default/units/turns.gls.js | 8 +- src/game/backend/Game.cpp | 16 +- src/game/backend/State.cpp | 4 +- src/game/backend/bindings/Bindings.cpp | 20 ++- src/game/backend/bindings/Bindings.h | 2 +- src/game/backend/unit/UnitManager.cpp | 205 ++++++++++++---------- src/gse/Exception.cpp | 1 + src/gse/Exception.h | 1 + src/gse/Wrappable.cpp | 8 +- 14 files changed, 171 insertions(+), 132 deletions(-) diff --git a/GLSMAC_data/default/factions.gls.js b/GLSMAC_data/default/factions.gls.js index 4dba4f85..1270d83c 100644 --- a/GLSMAC_data/default/factions.gls.js +++ b/GLSMAC_data/default/factions.gls.js @@ -1,6 +1,6 @@ return { - configure: (factions) => { + configure: (fm) => { for (faction of [ 'gaians', 'hive', @@ -17,7 +17,7 @@ return { 'caretakers', 'usurpers' ]) { - factions.add(#to_uppercase(faction), #include('./factions/' + faction)); + fm.add(#to_uppercase(faction), #include('./factions/' + faction)); } }, diff --git a/GLSMAC_data/default/main.gls.js b/GLSMAC_data/default/main.gls.js index 44beac3f..7f0fd7da 100644 --- a/GLSMAC_data/default/main.gls.js +++ b/GLSMAC_data/default/main.gls.js @@ -6,13 +6,13 @@ const bases = #include('bases'); #system.on('configure', (e) => { - factions.configure(e.lobby.factions); + factions.configure(e.gm.fm); - e.lobby.on('start', (e) => { + e.gm.on('start', (e) => { e.game.on('configure', (e) => { - units.configure(e.game.units); + units.configure(e.game.um); // will be populated on start let players = []; diff --git a/GLSMAC_data/default/units.gls.js b/GLSMAC_data/default/units.gls.js index cd99ed73..e87946a9 100644 --- a/GLSMAC_data/default/units.gls.js +++ b/GLSMAC_data/default/units.gls.js @@ -6,10 +6,10 @@ const animations = #include('units/animations'); const result = { - configure: (units) => { - movement.init(); - combat.init(animations); - turns.init(); + configure: (um) => { + movement.configure(um); + combat.configure(um, animations); + turns.configure(um); }, define: () => { diff --git a/GLSMAC_data/default/units/combat.gls.js b/GLSMAC_data/default/units/combat.gls.js index 618c8bfb..a9e0433f 100644 --- a/GLSMAC_data/default/units/combat.gls.js +++ b/GLSMAC_data/default/units/combat.gls.js @@ -29,9 +29,9 @@ const get_unit_defence_power = (unit) => { const result = { - init: (animations) => { + configure: (um, animations) => { - #game.on.unit_attack_validate((e) => { + um.on('unit_attack_validate', (e) => { if (e.attacker.is_immovable) { return 'Unit is immovable'; @@ -60,7 +60,7 @@ const result = { } }); - #game.on.unit_attack_resolve((e) => { + um.on('unit_attack_resolve', (e) => { let attack_power = get_unit_attack_power(e.attacker); let defence_power = get_unit_defence_power(e.defender); @@ -86,12 +86,12 @@ const result = { return damage_sequence; }); - #game.on.unit_attack_apply((e) => { + um.on('unit_attack_apply', (e) => { let attacker_tile = e.attacker.get_tile(); let defender_tile = e.defender.get_tile(); - #game.tiles.lock([attacker_tile, defender_tile], (unlock) => { + um.lock_tiles([attacker_tile, defender_tile], (unlock) => { let damages_sz = #size(e.resolutions); diff --git a/GLSMAC_data/default/units/movement.gls.js b/GLSMAC_data/default/units/movement.gls.js index 264ca18a..a742d71c 100644 --- a/GLSMAC_data/default/units/movement.gls.js +++ b/GLSMAC_data/default/units/movement.gls.js @@ -35,9 +35,9 @@ const get_movement_aftercost = (unit, src_tile, dst_tile) => { const result = { - init: () => { + configure: (um) => { - #game.on.unit_move_validate((e) => { + um.on('unit_move_validate', (e) => { if (e.unit.is_immovable) { return 'Unit is immovable'; } @@ -71,7 +71,7 @@ const result = { }); - #game.on.unit_move_resolve((e) => { + um.on('unit_move_resolve', (e) => { if (e.unit.movement == 0.0) { // no moves left return false; @@ -85,10 +85,10 @@ const result = { }; }); - #game.on.unit_move_apply((e) => { + um.on('unit_move_apply', (e) => { let src_tile = e.unit.get_tile(); - #game.tiles.lock([src_tile, e.dst_tile], (unlock) => { + um.lock_tiles([src_tile, e.dst_tile], (unlock) => { let movement_cost = get_movement_cost(e.unit, src_tile, e.dst_tile) + get_movement_aftercost(e.unit, e.unit.get_tile(), e.dst_tile); diff --git a/GLSMAC_data/default/units/turns.gls.js b/GLSMAC_data/default/units/turns.gls.js index 8d70353b..4302d373 100644 --- a/GLSMAC_data/default/units/turns.gls.js +++ b/GLSMAC_data/default/units/turns.gls.js @@ -1,19 +1,17 @@ const result = { - init: () => { - #game.on.unit_turn((e) => { - const def = e.unit.get_def(); + configure: (um) => { + um.on('unit_turn', (e) => { + const def = e.unit.get_def(); if (!e.unit.moved_this_turn) { if (e.unit.health < def.health_max) { e.unit.health = #min(e.unit.health + def.health_per_turn, def.health_max); } } - if (!def.is_immovable) { e.unit.movement = def.movement_per_turn; } - }); }, diff --git a/src/game/backend/Game.cpp b/src/game/backend/Game.cpp index c57dd59f..74073698 100644 --- a/src/game/backend/Game.cpp +++ b/src/game/backend/Game.cpp @@ -457,7 +457,7 @@ const size_t Game::GetSlotNum() const { WRAPIMPL_BEGIN( Game, CLASS_GAME ) WRAPIMPL_PROPS { - "units", + "um", m_um->Wrap( true ) }, }; @@ -1061,14 +1061,12 @@ void Game::AdvanceTurn( const size_t turn_id ) { for ( auto& it : m_um->GetUnits() ) { auto* unit = it.second; - m_state->m_bindings->Call( - bindings::Bindings::CS_ON_UNIT_TURN, { - { - "unit", - unit->Wrap( true ) - }, - } - ); + m_state->m_bindings->Trigger( m_um, "unit_turn", { + { + "unit", + unit->Wrap( true ) + }, + }); unit->m_moved_this_turn = false; m_um->RefreshUnit( unit ); } diff --git a/src/game/backend/State.cpp b/src/game/backend/State.cpp index 8794dbc6..e26b87bd 100644 --- a/src/game/backend/State.cpp +++ b/src/game/backend/State.cpp @@ -139,7 +139,7 @@ void State::Configure() { m_bindings->Trigger( m_system, "configure", { { - "lobby", + "gm", Wrap() } }); @@ -187,7 +187,7 @@ faction::FactionManager* State::GetFM() const { WRAPIMPL_BEGIN( State, CLASS_STATE ) WRAPIMPL_PROPS { - "factions", + "fm", m_fm->Wrap( true ) }, }; diff --git a/src/game/backend/bindings/Bindings.cpp b/src/game/backend/bindings/Bindings.cpp index 2d0cb456..26e64e4d 100644 --- a/src/game/backend/bindings/Bindings.cpp +++ b/src/game/backend/bindings/Bindings.cpp @@ -111,8 +111,24 @@ gse::Value Bindings::Call( const callback_slot_t slot, const callback_arguments_ return VALUE( gse::type::Undefined ); } -void Bindings::Trigger( gse::Wrappable* object, const std::string& event, const gse::type::object_properties_t& args ) { - object->Trigger( m_gse_context, m_si_internal, event, args ); +const gse::Value Bindings::Trigger( gse::Wrappable* object, const std::string& event, const gse::type::object_properties_t& args ) { + auto result = VALUE( gse::type::Undefined ); + try { + result = object->Trigger( m_gse_context, m_si_internal, event, args ); + if ( result.Get()->type == gse::type::Type::T_NOTHING ) { + // return undefined by default + return VALUE( gse::type::Undefined ); + } + } + catch ( gse::Exception& e ) { + if ( m_state->m_on_gse_error ) { + m_state->m_on_gse_error( e ); + } + else { + throw std::runtime_error( e.ToStringAndCleanup() ); + } + } + return result; } State* Bindings::GetState() const { diff --git a/src/game/backend/bindings/Bindings.h b/src/game/backend/bindings/Bindings.h index 97c801f7..a898b264 100644 --- a/src/game/backend/bindings/Bindings.h +++ b/src/game/backend/bindings/Bindings.h @@ -60,7 +60,7 @@ class Bindings : public gse::Bindings { typedef std::map< std::string, gse::Value > callback_arguments_t; gse::Value Call( const callback_slot_t slot, const callback_arguments_t& arguments = {} ); - void Trigger( gse::Wrappable* object, const std::string& event, const gse::type::object_properties_t& args ); + const gse::Value Trigger( gse::Wrappable* object, const std::string& event, const gse::type::object_properties_t& args ); State* GetState() const; Game* GetGame( GSE_CALLABLE ) const; diff --git a/src/game/backend/unit/UnitManager.cpp b/src/game/backend/unit/UnitManager.cpp index 2bd2efec..9cb2169d 100644 --- a/src/game/backend/unit/UnitManager.cpp +++ b/src/game/backend/unit/UnitManager.cpp @@ -10,6 +10,7 @@ #include "game/backend/slot/Slots.h" #include "game/backend/event/DespawnUnit.h" +#include "gse/context/Context.h" #include "gse/callable/Native.h" #include "gse/type/Bool.h" #include "gse/type/Float.h" @@ -253,6 +254,35 @@ void UnitManager::PushUnitUpdates() { WRAPIMPL_BEGIN( UnitManager, CLASS_UNITS ) WRAPIMPL_PROPS + { + "lock_tiles", + NATIVE_CALL( this ) { + N_EXPECT_ARGS( 2 ); + N_GETVALUE( tiles, 0, Array ); + N_PERSIST_CALLABLE( on_complete, 1 ); + map::tile::positions_t tile_positions = {}; + tile_positions.reserve( tiles.size() ); + for ( const auto& tileobj : tiles ) { + N_UNWRAP( tile, tileobj, map::tile::Tile ); + tile_positions.push_back( tile->coord ); + } + m_game->SendTileLockRequest( tile_positions, [ this, on_complete, tile_positions, ctx, call_si ]() { + on_complete->Run( ctx, call_si, { + VALUE( gse::callable::Native, [ this, tile_positions ]( + gse::context::Context* ctx, + const gse::si_t& call_si, + const gse::type::function_arguments_t& arguments + ) -> gse::Value { + m_game->SendTileUnlockRequest( tile_positions ); + return VALUE( gse::type::Undefined ); + }), + }); + N_UNPERSIST_CALLABLE( on_complete ); + }); + return VALUE( gse::type::Undefined ); + }) + + }, /* { "add", NATIVE_CALL( this ) { @@ -433,22 +463,20 @@ void UnitManager::QueueUnitUpdate( const Unit* unit, const unit_update_op_t op ) } const std::string* UnitManager::MoveUnitValidate( Unit* unit, map::tile::Tile* dst_tile ) { - const auto result = m_game->GetState()->m_bindings->Call( - bindings::Bindings::CS_ON_UNIT_MOVE_VALIDATE, { - { - "unit", - unit->Wrap() - }, - { - "src_tile", - unit->GetTile()->Wrap() - }, - { - "dst_tile", - dst_tile->Wrap() - }, - } - ); + const auto result = m_game->GetState()->m_bindings->Trigger( this, "unit_move_validate", { + { + "unit", + unit->Wrap() + }, + { + "src_tile", + unit->GetTile()->Wrap() + }, + { + "dst_tile", + dst_tile->Wrap() + }, + }); switch ( result.Get()->type ) { case gse::type::Type::T_NULL: case gse::type::Type::T_UNDEFINED: @@ -461,54 +489,54 @@ const std::string* UnitManager::MoveUnitValidate( Unit* unit, map::tile::Tile* d } const gse::Value UnitManager::MoveUnitResolve( Unit* unit, map::tile::Tile* dst_tile ) { - return m_game->GetState()->m_bindings->Call( - bindings::Bindings::CS_ON_UNIT_MOVE_RESOLVE, { - { - "unit", - unit->Wrap() - }, - { - "src_tile", - unit->GetTile()->Wrap() - }, - { - "dst_tile", - dst_tile->Wrap() - }, - } - ); + return m_game->GetState()->m_bindings->Trigger(this, "unit_move_resolve", { + { + "unit", + unit->Wrap() + }, + { + "src_tile", + unit->GetTile()->Wrap() + }, + { + "dst_tile", + dst_tile->Wrap() + }, + }); } void UnitManager::MoveUnitApply( Unit* unit, map::tile::Tile* dst_tile, const gse::Value resolutions ) { + auto* src_tile = unit->GetTile(); ASSERT( dst_tile, "dst tile not set" ); + if ( src_tile == dst_tile ) { + return; + } + Log( "Moving unit #" + std::to_string( unit->m_id ) + " to " + dst_tile->coord.ToString() ); - auto* src_tile = unit->GetTile(); ASSERT( src_tile, "src tile not set" ); ASSERT( src_tile->units.find( unit->m_id ) != src_tile->units.end(), "src tile does not contain this unit" ); ASSERT( dst_tile->units.find( unit->m_id ) == dst_tile->units.end(), "dst tile already contains this unit" ); - const auto result = m_game->GetState()->m_bindings->Call( - bindings::Bindings::CS_ON_UNIT_MOVE_APPLY, { - { - "unit", - unit->Wrap( true ) - }, - { - "src_tile", - src_tile->Wrap() - }, - { - "dst_tile", - dst_tile->Wrap() - }, - { - "resolutions", - resolutions - } + m_game->GetState()->m_bindings->Trigger( this, "unit_move_apply", { + { + "unit", + unit->Wrap( true ) + }, + { + "src_tile", + src_tile->Wrap() + }, + { + "dst_tile", + dst_tile->Wrap() + }, + { + "resolutions", + resolutions } - ); + }); } const std::string* UnitManager::MoveUnitToTile( Unit* unit, map::tile::Tile* dst_tile, const cb_oncomplete& on_complete ) { @@ -539,18 +567,16 @@ const std::string* UnitManager::MoveUnitToTile( Unit* unit, map::tile::Tile* dst } const std::string* UnitManager::AttackUnitValidate( Unit* attacker, Unit* defender ) { - const auto result = m_game->GetState()->m_bindings->Call( - bindings::Bindings::CS_ON_UNIT_ATTACK_VALIDATE, { - { - "attacker", - attacker->Wrap() - }, - { - "defender", - defender->Wrap() - }, - } - ); + const auto result = m_game->GetState()->m_bindings->Trigger( this, "unit_attack_validate", { + { + "attacker", + attacker->Wrap() + }, + { + "defender", + defender->Wrap() + }, + }); switch ( result.Get()->type ) { case gse::type::Type::T_NULL: case gse::type::Type::T_UNDEFINED: @@ -560,42 +586,37 @@ const std::string* UnitManager::AttackUnitValidate( Unit* attacker, Unit* defend default: THROW( "unexpected validation result type: " + gse::type::Type::GetTypeString( result.Get()->type ) ); } - return nullptr; } const gse::Value UnitManager::AttackUnitResolve( Unit* attacker, Unit* defender ) { - return m_game->GetState()->m_bindings->Call( - bindings::Bindings::CS_ON_UNIT_ATTACK_RESOLVE, { - { - "attacker", - attacker->Wrap() - }, - { - "defender", - defender->Wrap() - }, - } - ); + return m_game->GetState()->m_bindings->Trigger( this, "unit_attack_resolve", { + { + "attacker", + attacker->Wrap() + }, + { + "defender", + defender->Wrap() + }, + }); } void UnitManager::AttackUnitApply( Unit* attacker, Unit* defender, const gse::Value resolutions ) { auto* state = m_game->GetState(); - state->m_bindings->Call( - bindings::Bindings::CS_ON_UNIT_ATTACK_APPLY, { - { - "attacker", - attacker->Wrap( true ) - }, - { - "defender", - defender->Wrap( true ) - }, - { - "resolutions", - resolutions - } + state->m_bindings->Trigger( this, "unit_attack_apply",{ + { + "attacker", + attacker->Wrap( true ) + }, + { + "defender", + defender->Wrap( true ) + }, + { + "resolutions", + resolutions } - ); + }); if ( attacker->m_health <= 0.0f ) { if ( state->IsMaster() ) { m_game->AddEvent( new event::DespawnUnit( m_game->GetSlotNum(), attacker->m_id ) ); diff --git a/src/gse/Exception.cpp b/src/gse/Exception.cpp index 349ef915..92c377ad 100644 --- a/src/gse/Exception.cpp +++ b/src/gse/Exception.cpp @@ -19,6 +19,7 @@ const exception_ec_t EC = { "GSEConversionError", "GSEGameError", "GSEInvalidEvent", + "GSEInvalidHandler", }; const Exception::backtrace_t Exception::GetBacktraceAndCleanup( const context::Context* const current_ctx ) { diff --git a/src/gse/Exception.h b/src/gse/Exception.h index fb4f8281..38759ec5 100644 --- a/src/gse/Exception.h +++ b/src/gse/Exception.h @@ -28,6 +28,7 @@ struct exception_ec_t { const std::string MAP_ERROR; const std::string INVALID_EVENT; const std::string INVALID_DEFINITION; + const std::string INVALID_HANDLER; }; extern const exception_ec_t EC; diff --git a/src/gse/Wrappable.cpp b/src/gse/Wrappable.cpp index 6bbf37df..277c8467 100644 --- a/src/gse/Wrappable.cpp +++ b/src/gse/Wrappable.cpp @@ -61,14 +61,18 @@ void Wrappable::Off( GSE_CALLABLE, const std::string& event, const callback_id_t const Value Wrappable::Trigger( GSE_CALLABLE, const std::string& event, const type::object_properties_t& args ) { const auto& it = m_callbacks.find( event ); + Value result = VALUE( gse::type::Undefined ); if ( it != m_callbacks.end() ) { auto e = VALUE( gse::type::Object, args ); for ( const auto& it2 : it->second ) { ASSERT_NOLOG( it2.second.Get()->type == type::Type::T_CALLABLE, "callback not callable" ); - ( (type::Callable*)it2.second.Get() )->Run( ctx, call_si, { e } ); + if ( result.Get()->type != type::Type::T_UNDEFINED ) { + // TODO: resolve result conflicts somehow + } + result = ( (type::Callable*)it2.second.Get() )->Run( ctx, call_si, { e } ); } } - return VALUE( gse::type::Undefined ); + return result; } } From 09476c8600ab290945a55472cb3960b0fefb5ac6 Mon Sep 17 00:00:00 2001 From: afwbkbc Date: Sun, 10 Nov 2024 18:22:41 +0200 Subject: [PATCH 05/13] tile manager, animation manager bindings --- GLSMAC_data/default/main.gls.js | 2 +- GLSMAC_data/default/units.gls.js | 8 +- GLSMAC_data/default/units/combat.gls.js | 18 +- GLSMAC_data/default/units/movement.gls.js | 11 +- GLSMAC_data/default/units/turns.gls.js | 4 +- src/engine/Engine.cpp | 4 + src/game/backend/CMakeLists.txt | 1 - src/game/backend/Game.cpp | 271 +++--------------- src/game/backend/Game.h | 49 ++-- .../backend/animation/AnimationManager.cpp | 141 +++++++++ src/game/backend/animation/AnimationManager.h | 50 ++++ src/game/backend/animation/CMakeLists.txt | 1 + src/game/backend/bindings/Animations.cpp | 3 +- src/game/backend/bindings/Tiles.cpp | 5 +- src/game/backend/event/DefineAnimation.cpp | 3 +- src/game/backend/event/LockTiles.cpp | 3 +- src/game/backend/event/RequestTileLocks.cpp | 3 +- src/game/backend/event/RequestTileUnlocks.cpp | 3 +- src/game/backend/event/UnlockTiles.cpp | 3 +- src/game/backend/map/tile/CMakeLists.txt | 2 + src/game/backend/{ => map/tile}/TileLock.cpp | 6 +- src/game/backend/{ => map/tile}/TileLock.h | 5 + src/game/backend/map/tile/TileManager.cpp | 219 ++++++++++++++ src/game/backend/map/tile/TileManager.h | 62 ++++ src/game/backend/unit/UnitManager.cpp | 34 +-- src/gse/type/Object.h | 4 +- 26 files changed, 583 insertions(+), 332 deletions(-) create mode 100644 src/game/backend/animation/AnimationManager.cpp create mode 100644 src/game/backend/animation/AnimationManager.h rename src/game/backend/{ => map/tile}/TileLock.cpp (90%) rename src/game/backend/{ => map/tile}/TileLock.h (92%) create mode 100644 src/game/backend/map/tile/TileManager.cpp create mode 100644 src/game/backend/map/tile/TileManager.h diff --git a/GLSMAC_data/default/main.gls.js b/GLSMAC_data/default/main.gls.js index 7f0fd7da..415244e4 100644 --- a/GLSMAC_data/default/main.gls.js +++ b/GLSMAC_data/default/main.gls.js @@ -12,7 +12,7 @@ const bases = #include('bases'); e.game.on('configure', (e) => { - units.configure(e.game.um); + units.configure(e.game); // will be populated on start let players = []; diff --git a/GLSMAC_data/default/units.gls.js b/GLSMAC_data/default/units.gls.js index e87946a9..d78c315f 100644 --- a/GLSMAC_data/default/units.gls.js +++ b/GLSMAC_data/default/units.gls.js @@ -6,10 +6,10 @@ const animations = #include('units/animations'); const result = { - configure: (um) => { - movement.configure(um); - combat.configure(um, animations); - turns.configure(um); + configure: (game) => { + movement.configure(game); + combat.configure(game, animations); + turns.configure(game); }, define: () => { diff --git a/GLSMAC_data/default/units/combat.gls.js b/GLSMAC_data/default/units/combat.gls.js index a9e0433f..588f74f1 100644 --- a/GLSMAC_data/default/units/combat.gls.js +++ b/GLSMAC_data/default/units/combat.gls.js @@ -29,9 +29,9 @@ const get_unit_defence_power = (unit) => { const result = { - configure: (um, animations) => { + configure: (game, animations) => { - um.on('unit_attack_validate', (e) => { + game.um.on('unit_attack_validate', (e) => { if (e.attacker.is_immovable) { return 'Unit is immovable'; @@ -60,7 +60,7 @@ const result = { } }); - um.on('unit_attack_resolve', (e) => { + game.um.on('unit_attack_resolve', (e) => { let attack_power = get_unit_attack_power(e.attacker); let defence_power = get_unit_defence_power(e.defender); @@ -86,12 +86,12 @@ const result = { return damage_sequence; }); - um.on('unit_attack_apply', (e) => { + game.um.on('unit_attack_apply', (e) => { let attacker_tile = e.attacker.get_tile(); let defender_tile = e.defender.get_tile(); - um.lock_tiles([attacker_tile, defender_tile], (unlock) => { + game.tm.lock_tiles([attacker_tile, defender_tile], (unlock) => { let damages_sz = #size(e.resolutions); @@ -102,19 +102,19 @@ const result = { if (damage_index < damages_sz) { const damages = e.resolutions[damage_index]; if (damages[0]) { - #game.animations.show_on_tile(animations.ATTACK_PSI, defender_tile, () => { + game.am.show_animation(animations.ATTACK_PSI, defender_tile, () => { e.defender.health = e.defender.health - damages[1]; if (e.defender.health == 0.0) { - #game.animations.show_on_tile(animations.DEATH_PSI, defender_tile, next); + game.am.show_animation(animations.DEATH_PSI, defender_tile, next); } else { next(); } }); } else { - #game.animations.show_on_tile(animations.ATTACK_PSI, attacker_tile, () => { + game.am.show_animation(animations.ATTACK_PSI, attacker_tile, () => { e.attacker.health = e.attacker.health - damages[1]; if (e.attacker.health == 0.0) { - #game.animations.show_on_tile(animations.DEATH_PSI, attacker_tile, next); + game.am.show_animation(animations.DEATH_PSI, attacker_tile, next); } else { next(); } diff --git a/GLSMAC_data/default/units/movement.gls.js b/GLSMAC_data/default/units/movement.gls.js index a742d71c..eabd8393 100644 --- a/GLSMAC_data/default/units/movement.gls.js +++ b/GLSMAC_data/default/units/movement.gls.js @@ -35,9 +35,9 @@ const get_movement_aftercost = (unit, src_tile, dst_tile) => { const result = { - configure: (um) => { + configure: (game) => { - um.on('unit_move_validate', (e) => { + game.um.on('unit_move_validate', (e) => { if (e.unit.is_immovable) { return 'Unit is immovable'; } @@ -71,7 +71,7 @@ const result = { }); - um.on('unit_move_resolve', (e) => { + game.um.on('unit_move_resolve', (e) => { if (e.unit.movement == 0.0) { // no moves left return false; @@ -85,10 +85,10 @@ const result = { }; }); - um.on('unit_move_apply', (e) => { + game.um.on('unit_move_apply', (e) => { let src_tile = e.unit.get_tile(); - um.lock_tiles([src_tile, e.dst_tile], (unlock) => { + game.tm.lock_tiles([src_tile, e.dst_tile], (unlock) => { let movement_cost = get_movement_cost(e.unit, src_tile, e.dst_tile) + get_movement_aftercost(e.unit, e.unit.get_tile(), e.dst_tile); @@ -100,6 +100,7 @@ const result = { e.unit.movement = 0.0; } e.unit.moved_this_turn = true; + unlock(); }; diff --git a/GLSMAC_data/default/units/turns.gls.js b/GLSMAC_data/default/units/turns.gls.js index 4302d373..458fe992 100644 --- a/GLSMAC_data/default/units/turns.gls.js +++ b/GLSMAC_data/default/units/turns.gls.js @@ -1,8 +1,8 @@ const result = { - configure: (um) => { + configure: (game) => { - um.on('unit_turn', (e) => { + game.um.on('unit_turn', (e) => { const def = e.unit.get_def(); if (!e.unit.moved_this_turn) { if (e.unit.health < def.health_max) { diff --git a/src/engine/Engine.cpp b/src/engine/Engine.cpp index 7490e9f8..940d8b18 100644 --- a/src/engine/Engine.cpp +++ b/src/engine/Engine.cpp @@ -19,6 +19,10 @@ #include "ui/UI.h" #include "game/backend/Game.h" +#ifdef DEBUG +#include "util/Timer.h" +#endif + // TODO: move to config const size_t g_max_fps = 500; diff --git a/src/game/backend/CMakeLists.txt b/src/game/backend/CMakeLists.txt index 5662c4b7..f7b54c5d 100644 --- a/src/game/backend/CMakeLists.txt +++ b/src/game/backend/CMakeLists.txt @@ -20,7 +20,6 @@ SET( SRC ${SRC} ${PWD}/Account.cpp ${PWD}/Player.cpp ${PWD}/MapObject.cpp - ${PWD}/TileLock.cpp ${PWD}/Resource.cpp PARENT_SCOPE ) diff --git a/src/game/backend/Game.cpp b/src/game/backend/Game.cpp index 74073698..112f6c7d 100644 --- a/src/game/backend/Game.cpp +++ b/src/game/backend/Game.cpp @@ -14,11 +14,6 @@ #include "event/FinalizeTurn.h" #include "event/TurnFinalized.h" #include "event/AdvanceTurn.h" -#include "event/DespawnUnit.h" -#include "event/RequestTileLocks.h" -#include "event/LockTiles.h" -#include "event/RequestTileUnlocks.h" -#include "event/UnlockTiles.h" #include "util/random/Random.h" #include "config/Config.h" #include "slot/Slots.h" @@ -33,6 +28,7 @@ #include "gse/type/Undefined.h" #include "gse/type/Array.h" #include "ui/UI.h" +#include "map/tile/TileManager.h" #include "map/tile/Tiles.h" #include "map/MapState.h" #include "bindings/Bindings.h" @@ -40,11 +36,12 @@ #include "Resource.h" #include "animation/Def.h" #include "unit/Def.h" -#include "unit/Unit.h" #include "unit/UnitManager.h" +#include "unit/Unit.h" #include "unit/MoraleSet.h" #include "base/PopDef.h" #include "base/Base.h" +#include "animation/AnimationManager.h" namespace game { namespace backend { @@ -181,7 +178,9 @@ void Game::Start() { // init map editor NEW( m_map_editor, map_editor::MapEditor, this ); + NEW( m_tm, map::tile::TileManager, this ); NEW( m_um, unit::UnitManager, this ); + NEW( m_am, animation::AnimationManager, this ); } void Game::Stop() { @@ -198,9 +197,15 @@ void Game::Stop() { DELETE( m_map_editor ); m_map_editor = nullptr; + DELETE( m_tm ); + m_tm = nullptr; + DELETE( m_um ); m_um = nullptr; + DELETE( m_am ); + m_am = nullptr; + MTModule::Stop(); } @@ -424,7 +429,7 @@ void Game::Iterate() { } m_um->PushUnitUpdates(); PushBaseUpdates(); - ProcessTileLockRequests(); + m_tm->ProcessTileLockRequests(); } util::random::Random* Game::GetRandom() const { @@ -456,10 +461,18 @@ const size_t Game::GetSlotNum() const { WRAPIMPL_BEGIN( Game, CLASS_GAME ) WRAPIMPL_PROPS + { + "tm", + m_tm->Wrap( true ) + }, { "um", m_um->Wrap( true ) }, + { + "am", + m_am->Wrap( true ) + }, }; WRAPIMPL_END_PTR( Game ) @@ -660,11 +673,7 @@ const MT_Response Game::ProcessRequest( const MT_Request& request, MT_CANCELABLE break; } case BackendRequest::BR_ANIMATION_FINISHED: { - const auto& it = m_running_animations_callbacks.find( r.data.animation_finished.animation_id ); - ASSERT( it != m_running_animations_callbacks.end(), "animation " + std::to_string( r.data.animation_finished.animation_id ) + " is not running" ); - const auto on_complete = it->second; - m_running_animations_callbacks.erase( it ); - on_complete(); + m_am->FinishAnimation( r.data.animation_finished.animation_id ); break; } default: @@ -844,44 +853,6 @@ void Game::RefreshBase( const base::Base* base ) { QueueBaseUpdate( base, BUO_REFRESH ); } -void Game::DefineAnimation( animation::Def* def ) { - Log( "Defining animation ('" + def->m_id + "')" ); - - ASSERT( m_animation_defs.find( def->m_id ) == m_animation_defs.end(), "Animation definition '" + def->m_id + "' already exists" ); - - // backend doesn't need any animation details, just keep track of it's existence for validations - m_animation_defs.insert( - { - def->m_id, - def - } - ); - - auto fr = FrontendRequest( FrontendRequest::FR_ANIMATION_DEFINE ); - NEW( fr.data.animation_define.serialized_animation, std::string, animation::Def::Serialize( def ).ToString() ); - AddFrontendRequest( fr ); -} - -const std::string* Game::ShowAnimationOnTile( const std::string& animation_id, map::tile::Tile* tile, const cb_oncomplete& on_complete ) { - if ( m_animation_defs.find( animation_id ) == m_animation_defs.end() ) { - return new std::string( "Animation '" + animation_id + "' is not defined" ); - } - if ( !tile->IsLocked() ) { - return new std::string( "Tile must be locked before showing animation" ); - } - auto fr = FrontendRequest( FrontendRequest::FR_ANIMATION_SHOW ); - NEW( fr.data.animation_show.animation_id, std::string, animation_id ); - fr.data.animation_show.running_animation_id = AddAnimationCallback( on_complete ); - const auto c = GetTileRenderCoords( tile ); - fr.data.animation_show.render_coords = { - c.x, - c.y, - c.z, - }; - AddFrontendRequest( fr ); - return nullptr; // no error -} - void Game::DefineResource( Resource* resource ) { Log( "Defining resource ('" + resource->m_id + "')" ); @@ -1145,84 +1116,24 @@ void Game::GlobalAdvanceTurn() { AddEvent( new event::AdvanceTurn( m_slot_num, s_turn_id ) ); } -void Game::SendTileLockRequest( const map::tile::positions_t& tile_positions, const cb_oncomplete& on_complete ) { - // testing - /*m_tile_lock_callbacks.push_back( - { - tile_positions, - on_complete - } - );*/ - LockTiles( m_slot_num, tile_positions ); - on_complete(); - //AddEvent( new event::RequestTileLocks( m_slot_num, tile_positions ) ); -} - -void Game::RequestTileLocks( const size_t initiator_slot, const map::tile::positions_t& tile_positions ) { - if ( m_state->IsMaster() ) { - Log( "Tile locks request from " + std::to_string( initiator_slot ) + ": " + map::tile::Tile::TilePositionsToString( tile_positions, "" ) ); - AddTileLockRequest( true, initiator_slot, tile_positions ); - } -} - -void Game::LockTiles( const size_t initiator_slot, const map::tile::positions_t& tile_positions ) { - for ( const auto& pos : tile_positions ) { - auto* tile = m_map->GetTile( pos.x, pos.y ); - ASSERT_NOLOG( !tile->IsLocked(), "tile " + pos.ToString() + " is already locked" ); - tile->Lock( initiator_slot ); - } - for ( auto it = m_tile_lock_callbacks.begin() ; it != m_tile_lock_callbacks.end() ; it++ ) { - const auto& it_positions = it->first; - const auto sz = it_positions.size(); - if ( sz == tile_positions.size() ) { - bool match = true; - for ( size_t i = 0 ; i < sz ; i++ ) { - if ( tile_positions.at( i ) != it_positions.at( i ) ) { - match = false; - break; - } - } - if ( match ) { - const auto cb = it->second; - m_tile_lock_callbacks.erase( it ); - cb(); - break; - } - } - } -} - -void Game::SendTileUnlockRequest( const map::tile::positions_t& tile_positions ) { - // testing - UnlockTiles( m_slot_num, tile_positions ); - //AddEvent( new event::RequestTileUnlocks( m_slot_num, tile_positions ) ); -} - -void Game::RequestTileUnlocks( const size_t initiator_slot, const map::tile::positions_t& tile_positions ) { - if ( m_state->IsMaster() ) { - Log( "Tile unlocks request from " + std::to_string( initiator_slot ) + ": " + map::tile::Tile::TilePositionsToString( tile_positions, "" ) ); - AddTileLockRequest( false, initiator_slot, tile_positions ); - } -} - -void Game::UnlockTiles( const size_t initiator_slot, const map::tile::positions_t& tile_positions ) { - for ( const auto& pos : tile_positions ) { - auto* tile = m_map->GetTile( pos.x, pos.y ); - ASSERT_NOLOG( tile->IsLockedBy( initiator_slot ), "tile " + pos.ToString() + " is not locked by " + std::to_string( initiator_slot ) ); - tile->Unlock(); - } -} - faction::Faction* Game::GetFaction( const std::string& id ) const { auto* faction = m_state->GetFM()->Get( id ); // TODO: store factions in Game itself? ASSERT( faction, "faction not found: " + id ); return faction; } +map::tile::TileManager* Game::GetTM() const { + return m_tm; +} + unit::UnitManager* Game::GetUM() const { return m_um; } +animation::AnimationManager* Game::GetAM() const { + return m_am; +} + void Game::ValidateEvent( event::Event* event ) { if ( !event->m_is_validated ) { const auto* errmsg = event->Validate( this ); @@ -1341,27 +1252,6 @@ void Game::UnserializeBases( types::Buffer& buf ) { Log( "Restored next base id: " + std::to_string( base::Base::GetNextId() ) ); } -void Game::SerializeAnimations( types::Buffer& buf ) const { - Log( "Serializing " + std::to_string( m_animation_defs.size() ) + " animation defs" ); - buf.WriteInt( m_animation_defs.size() ); - for ( const auto& it : m_animation_defs ) { - buf.WriteString( it.first ); - buf.WriteString( animation::Def::Serialize( it.second ).ToString() ); - } -} - -void Game::UnserializeAnimations( types::Buffer& buf ) { - ASSERT( m_animation_defs.empty(), "animation defs not empty" ); - size_t sz = buf.ReadInt(); - Log( "Unserializing " + std::to_string( sz ) + " animation defs" ); - m_animation_defs.reserve( sz ); - for ( size_t i = 0 ; i < sz ; i++ ) { - const auto name = buf.ReadString(); - auto b = types::Buffer( buf.ReadString() ); - DefineAnimation( animation::Def::Unserialize( b ) ); - } -} - void Game::AddFrontendRequest( const FrontendRequest& request ) { //Log( "Sending frontend request (type=" + std::to_string( request.type ) + ")" ); // spammy m_pending_frontend_requests->push_back( request ); @@ -1445,11 +1335,7 @@ void Game::InitGame( MT_Response& response, MT_CANCELABLE ) { connection->m_on_player_leave = [ this, connection ]( const size_t slot_num, slot::Slot* slot, const Player* player ) -> void { Log( "Player " + player->GetFullName() + " left the game." ); connection->GlobalMessage( player->GetFullName() + " left the game." ); - const auto& it = m_tile_locks.find( slot_num ); - if ( it != m_tile_locks.end() ) { - Log( "Releasing " + std::to_string( it->second.size() ) + " tile locks" ); - m_tile_locks.erase( it ); - } + m_tm->ReleaseTileLocks( slot_num ); }; connection->m_on_download_request = [ this ]() -> const std::string { @@ -1487,7 +1373,7 @@ void Game::InitGame( MT_Response& response, MT_CANCELABLE ) { // animations { types::Buffer b; - SerializeAnimations( b ); + m_am->Serialize( b ); buf.WriteString( b.ToString() ); } @@ -1678,7 +1564,7 @@ void Game::InitGame( MT_Response& response, MT_CANCELABLE ) { // animations { auto ab = types::Buffer( buf.ReadString() ); - UnserializeAnimations( ab ); + m_am->Unserialize( ab ); } // get turn info @@ -1729,15 +1615,14 @@ void Game::ResetGame() { m_slot_num = 0; m_slot = nullptr; - m_um->Clear(); - for ( auto& it : m_unprocessed_events ) { delete it; } m_unprocessed_events.clear(); - m_running_animations_callbacks.clear(); - m_next_running_animation_id = 0; + m_tm->Clear(); + m_um->Clear(); + m_am->Clear(); for ( auto& it : m_resources ) { delete it.second; @@ -1755,11 +1640,6 @@ void Game::ResetGame() { } m_bases.clear(); - for ( auto& it : m_animation_defs ) { - delete it.second; - } - m_animation_defs.clear(); - if ( m_map ) { Log( "Resetting map" ); DELETE( m_map ); @@ -1771,11 +1651,6 @@ void Game::ResetGame() { m_base_updates.clear(); - m_tile_lock_requests.clear(); - m_tile_locks.clear(); - - m_tile_lock_callbacks.clear(); - m_current_turn.Reset(); m_is_turn_complete = false; @@ -1892,85 +1767,9 @@ void Game::PushBaseUpdates() { } } -void Game::AddTileLockRequest( const bool is_lock, const size_t initiator_slot, const map::tile::positions_t& tile_positions ) { - ASSERT_NOLOG( m_state && m_state->IsMaster(), "only master can manage tile locks" ); - m_tile_lock_requests.push_back( - { - is_lock, - initiator_slot, - tile_positions - } - ); -} - -void Game::ProcessTileLockRequests() { - if ( m_state && m_game_state == GS_RUNNING && m_state->IsMaster() && !m_tile_lock_requests.empty() ) { - const auto tile_lock_requests = m_tile_lock_requests; - m_tile_lock_requests.clear(); - for ( const auto& req : tile_lock_requests ) { - const auto state = m_state->m_slots->GetSlot( req.initiator_slot ).GetState(); - if ( state != slot::Slot::SS_PLAYER ) { - // player disconnected? - Log( "Skipping tile locks/unlocks for slot " + std::to_string( req.initiator_slot ) + " (invalid slot state: " + std::to_string( state ) + ")" ); - continue; - } - auto it = m_tile_locks.find( req.initiator_slot ); - if ( it == m_tile_locks.end() ) { - it = m_tile_locks.insert( - { - req.initiator_slot, - {} - } - ).first; - } - auto& locks = it->second; - if ( req.is_lock ) { - // lock - Log( "Locking tiles for " + std::to_string( req.initiator_slot ) + ": " + map::tile::Tile::TilePositionsToString( req.tile_positions ) ); - locks.push_back( TileLock{ req.tile_positions } ); - auto e = new event::LockTiles( m_slot_num, req.tile_positions, req.initiator_slot ); - e->SetDestinationSlot( req.initiator_slot ); - AddEvent( e ); - } - else { - // unlock - bool found = false; - for ( auto locks_it = locks.begin() ; locks_it != locks.end() ; locks_it++ ) { - if ( locks_it->Matches( req.tile_positions ) ) { - found = true; - Log( "Unlocking tiles for " + std::to_string( req.initiator_slot ) + ": " + map::tile::Tile::TilePositionsToString( req.tile_positions ) ); - locks.erase( locks_it ); - if ( locks.empty() ) { - m_tile_locks.erase( it ); - } - auto e = new event::UnlockTiles( m_slot_num, req.tile_positions, req.initiator_slot ); - e->SetDestinationSlot( req.initiator_slot ); - AddEvent( e ); - break; - } - } - if ( !found ) { - Log( "Could not find matching tile locks for " + std::to_string( req.initiator_slot ) + ": " + map::tile::Tile::TilePositionsToString( req.tile_positions ) ); - } - } - } - } -} - const bool Game::IsRunning() const { return m_game_state == GS_RUNNING; } -const size_t Game::AddAnimationCallback( const game::backend::Game::cb_oncomplete& on_complete ) { - const auto running_animation_id = ++m_next_running_animation_id; - m_running_animations_callbacks.insert( - { - running_animation_id, - on_complete - } - ); - return running_animation_id; -} - } } diff --git a/src/game/backend/Game.h b/src/game/backend/Game.h index 75fd0181..3edb1313 100644 --- a/src/game/backend/Game.h +++ b/src/game/backend/Game.h @@ -17,7 +17,6 @@ #include "game/FrontendRequest.h" #include "game/BackendRequest.h" #include "game/backend/turn/Turn.h" -#include "TileLock.h" // TODO: remove those #include "game/backend/map/tile/Tile.h" @@ -48,6 +47,12 @@ namespace faction { class Faction; } +namespace map { +namespace tile { +class TileManager; +} +} + namespace unit { class MoraleSet; class Def; @@ -60,6 +65,10 @@ class PopDef; class Base; } +namespace animation { +class AnimationManager; +} + class Resource; namespace event { @@ -336,8 +345,6 @@ CLASS2( Game, MTModule, gse::Wrappable ) base::Base* GetBase( const size_t id ) const; const gse::Value AddEvent( event::Event* event ); void RefreshBase( const base::Base* base ); - void DefineAnimation( animation::Def* def ); - const std::string* ShowAnimationOnTile( const std::string& animation_id, map::tile::Tile* tile, const cb_oncomplete& on_complete ); void DefineResource( Resource* resource ); void DefinePop( base::PopDef* pop_def ); void SpawnBase( base::Base* base ); @@ -354,17 +361,11 @@ CLASS2( Game, MTModule, gse::Wrappable ) void GlobalProcessTurnFinalized( const size_t slot_num, const util::crc32::crc_t checksum ); void GlobalAdvanceTurn(); - void SendTileLockRequest( const map::tile::positions_t& tile_positions, const cb_oncomplete& on_complete ); - void RequestTileLocks( const size_t initiator_slot, const map::tile::positions_t& tile_positions ); - void LockTiles( const size_t initiator_slot, const map::tile::positions_t& tile_positions ); - - void SendTileUnlockRequest( const map::tile::positions_t& tile_positions ); - void RequestTileUnlocks( const size_t initiator_slot, const map::tile::positions_t& tile_positions ); - void UnlockTiles( const size_t initiator_slot, const map::tile::positions_t& tile_positions ); - faction::Faction* GetFaction( const std::string& id ) const; + map::tile::TileManager* GetTM() const; unit::UnitManager* GetUM() const; + animation::AnimationManager* GetAM() const; private: @@ -379,17 +380,15 @@ CLASS2( Game, MTModule, gse::Wrappable ) void SerializeResources( types::Buffer& buf ) const; void UnserializeResources( types::Buffer& buf ); + map::tile::TileManager* m_tm = nullptr; unit::UnitManager* m_um = nullptr; + animation::AnimationManager* m_am = nullptr; std::unordered_map< std::string, base::PopDef* > m_base_popdefs = {}; std::map< size_t, base::Base* > m_bases = {}; void SerializeBases( types::Buffer& buf ) const; void UnserializeBases( types::Buffer& buf ); - std::unordered_map< std::string, animation::Def* > m_animation_defs = {}; - void SerializeAnimations( types::Buffer& buf ) const; - void UnserializeAnimations( types::Buffer& buf ); - enum game_state_t { GS_NONE, GS_PREPARING_MAP, @@ -432,9 +431,6 @@ CLASS2( Game, MTModule, gse::Wrappable ) bool m_is_turn_complete = false; void CheckTurnComplete(); - size_t m_next_running_animation_id = 1; - std::unordered_map< size_t, cb_oncomplete > m_running_animations_callbacks = {}; - enum base_update_op_t : uint8_t { BUO_NONE = 0, BUO_SPAWN = 1 << 0, @@ -448,20 +444,6 @@ CLASS2( Game, MTModule, gse::Wrappable ) std::unordered_map< size_t, base_update_t > m_base_updates = {}; void QueueBaseUpdate( const base::Base* base, const base_update_op_t op ); - // server-side lock tracking - struct tile_lock_request_t { - const bool is_lock; // lock or unlock - const size_t initiator_slot; - const map::tile::positions_t tile_positions; - }; - typedef std::vector< tile_lock_request_t > tile_lock_requests_t; // requests fifo - tile_lock_requests_t m_tile_lock_requests = {}; - void AddTileLockRequest( const bool is_lock, const size_t initiator_slot, const map::tile::positions_t& tile_positions ); - std::unordered_map< size_t, std::vector< TileLock > > m_tile_locks = {}; // slot id, locks - void ProcessTileLockRequests(); - - std::vector< std::pair< map::tile::positions_t, cb_oncomplete > > m_tile_lock_callbacks = {}; // tile positions (for matching), callback - std::unordered_set< std::string > m_registered_base_names = {}; private: @@ -469,11 +451,12 @@ CLASS2( Game, MTModule, gse::Wrappable ) void PushBaseUpdates(); private: + friend class map::tile::TileManager; friend class unit::UnitManager; + friend class animation::AnimationManager; void AddFrontendRequest( const FrontendRequest& request ); const bool IsRunning() const; - const size_t AddAnimationCallback( const cb_oncomplete& on_complete ); }; diff --git a/src/game/backend/animation/AnimationManager.cpp b/src/game/backend/animation/AnimationManager.cpp new file mode 100644 index 00000000..e0f0bb17 --- /dev/null +++ b/src/game/backend/animation/AnimationManager.cpp @@ -0,0 +1,141 @@ +#include "AnimationManager.h" + +#include "game/backend/Game.h" +#include "Def.h" + +#include "gse/context/Context.h" +#include "gse/callable/Native.h" +#include "gse/type/Array.h" + +namespace game { +namespace backend { +namespace animation { + +AnimationManager::AnimationManager( Game* game ) + : m_game( game ) { + // +} + +AnimationManager::~AnimationManager() { + Clear(); +} + +void AnimationManager::Clear() { + m_running_animations_callbacks.clear(); + m_next_running_animation_id = 0; + for ( auto& it : m_animation_defs ) { + delete it.second; + } + m_animation_defs.clear(); +} + +void AnimationManager::DefineAnimation( animation::Def* def ) { + Log( "Defining animation ('" + def->m_id + "')" ); + + ASSERT( m_animation_defs.find( def->m_id ) == m_animation_defs.end(), "Animation definition '" + def->m_id + "' already exists" ); + + // backend doesn't need any animation details, just keep track of it's existence for validations + m_animation_defs.insert( + { + def->m_id, + def + } + ); + + auto fr = FrontendRequest( FrontendRequest::FR_ANIMATION_DEFINE ); + NEW( fr.data.animation_define.serialized_animation, std::string, animation::Def::Serialize( def ).ToString() ); + m_game->AddFrontendRequest( fr ); +} + +const std::string* AnimationManager::ShowAnimation( const std::string& animation_id, map::tile::Tile* tile, const cb_oncomplete& on_complete ) { + if ( m_animation_defs.find( animation_id ) == m_animation_defs.end() ) { + return new std::string( "Animation '" + animation_id + "' is not defined" ); + } + if ( !tile->IsLocked() ) { + return new std::string( "Tile must be locked before showing animation" ); + } + auto fr = FrontendRequest( FrontendRequest::FR_ANIMATION_SHOW ); + NEW( fr.data.animation_show.animation_id, std::string, animation_id ); + fr.data.animation_show.running_animation_id = AddAnimationCallback( on_complete ); + const auto c = m_game->GetTileRenderCoords( tile ); + fr.data.animation_show.render_coords = { + c.x, + c.y, + c.z, + }; + m_game->AddFrontendRequest( fr ); + return nullptr; // no error +} + +void AnimationManager::FinishAnimation( const size_t animation_id ) { + const auto& it = m_running_animations_callbacks.find( animation_id ); + ASSERT( it != m_running_animations_callbacks.end(), "animation " + std::to_string( animation_id ) + " is not running" ); + const auto on_complete = it->second; + m_running_animations_callbacks.erase( it ); + on_complete(); +} + +WRAPIMPL_BEGIN( AnimationManager, CLASS_AM ) + WRAPIMPL_PROPS + { + "show_animation", + NATIVE_CALL( this ) { + N_EXPECT_ARGS( 3 ); + N_GETVALUE( id, 0, String ); + N_GETVALUE_UNWRAP( tile, 1, map::tile::Tile ); + N_PERSIST_CALLABLE( on_complete, 2 ); + const auto* errmsg = ShowAnimation( id, tile, [ on_complete, ctx, call_si ]() { + on_complete->Run( ctx, call_si, {} ); + N_UNPERSIST_CALLABLE( on_complete ); + }); + if ( errmsg ) { + GSE_ERROR( gse::EC.GAME_ERROR, *errmsg ); + delete errmsg; + } + return VALUE( gse::type::Undefined ); + } ) + } + }; +WRAPIMPL_END_PTR( AnimationManager ) + +UNWRAPIMPL_PTR( AnimationManager ) + +void AnimationManager::Serialize( types::Buffer& buf ) const { + Log( "Serializing " + std::to_string( m_animation_defs.size() ) + " animation defs" ); + buf.WriteInt( m_animation_defs.size() ); + for ( const auto& it : m_animation_defs ) { + buf.WriteString( it.first ); + buf.WriteString( animation::Def::Serialize( it.second ).ToString() ); + } + buf.WriteInt( m_next_running_animation_id ); + Log( "Saved next animation id: " + std::to_string( m_next_running_animation_id ) ); +} + +void AnimationManager::Unserialize( types::Buffer& buf ) { + ASSERT( m_animation_defs.empty(), "animation defs not empty" ); + size_t sz = buf.ReadInt(); + Log( "Unserializing " + std::to_string( sz ) + " animation defs" ); + m_animation_defs.reserve( sz ); + for ( size_t i = 0 ; i < sz ; i++ ) { + const auto name = buf.ReadString(); + auto b = types::Buffer( buf.ReadString() ); + DefineAnimation( animation::Def::Unserialize( b ) ); + } + m_next_running_animation_id = buf.ReadInt(); + Log( "Restored next animation id: " + std::to_string( m_next_running_animation_id ) ); +} + +const size_t AnimationManager::AddAnimationCallback( const cb_oncomplete& on_complete ) { + const auto running_animation_id = ++m_next_running_animation_id; + m_running_animations_callbacks.insert( + { + running_animation_id, + on_complete + } + ); + return running_animation_id; +} + +} +} +} diff --git a/src/game/backend/animation/AnimationManager.h b/src/game/backend/animation/AnimationManager.h new file mode 100644 index 00000000..488ed0dc --- /dev/null +++ b/src/game/backend/animation/AnimationManager.h @@ -0,0 +1,50 @@ +#pragma once + +#include "common/Common.h" + +#include "gse/Wrappable.h" +#include "gse/type/Object.h" + +namespace game { +namespace backend { + +class Game; + +namespace map::tile { +class Tile; +} + +namespace animation { + +class Def; + +CLASS2( AnimationManager, common::Class, gse::Wrappable ) +public: + AnimationManager( Game* game ); + ~AnimationManager(); + + typedef std::function< void() > cb_oncomplete; + + void Clear(); + void DefineAnimation( Def* def ); + const std::string* ShowAnimation( const std::string& animation_id, map::tile::Tile* tile, const cb_oncomplete& on_complete ); + const size_t AddAnimationCallback( const cb_oncomplete& on_complete ); + void FinishAnimation( const size_t animation_id ); + + WRAPDEFS_PTR( AnimationManager ) + + void Serialize( types::Buffer& buf ) const; + void Unserialize( types::Buffer& buf ); + +private: + Game* m_game = nullptr; + + std::unordered_map< std::string, animation::Def* > m_animation_defs = {}; + size_t m_next_running_animation_id = 1; + std::unordered_map< size_t, cb_oncomplete > m_running_animations_callbacks = {}; + +}; + +} +} +} diff --git a/src/game/backend/animation/CMakeLists.txt b/src/game/backend/animation/CMakeLists.txt index 2f07ccd5..358b8904 100644 --- a/src/game/backend/animation/CMakeLists.txt +++ b/src/game/backend/animation/CMakeLists.txt @@ -1,5 +1,6 @@ SET( SRC ${SRC} + ${PWD}/AnimationManager.cpp ${PWD}/Def.cpp ${PWD}/FramesRow.cpp diff --git a/src/game/backend/bindings/Animations.cpp b/src/game/backend/bindings/Animations.cpp index a6c81152..602ff1b1 100644 --- a/src/game/backend/bindings/Animations.cpp +++ b/src/game/backend/bindings/Animations.cpp @@ -9,6 +9,7 @@ #include "gse/type/Object.h" #include "gse/type/Undefined.h" #include "game/backend/Game.h" +#include "game/backend/animation/AnimationManager.h" #include "Bindings.h" #include "game/backend/event/DefineAnimation.h" #include "game/backend/animation/FramesRow.h" @@ -79,7 +80,7 @@ BINDING_IMPL( animations ) { N_GETVALUE( id, 0, String ); N_GETVALUE_UNWRAP( tile, 1, map::tile::Tile ); N_PERSIST_CALLABLE( on_complete, 2 ); - const auto* errmsg = GAME->ShowAnimationOnTile( id, tile, [ on_complete, ctx, call_si ]() { + const auto* errmsg = GAME->GetAM()->ShowAnimation( id, tile, [ on_complete, ctx, call_si ]() { on_complete->Run( ctx, call_si, {} ); N_UNPERSIST_CALLABLE( on_complete ); }); diff --git a/src/game/backend/bindings/Tiles.cpp b/src/game/backend/bindings/Tiles.cpp index 58d66c7c..2fabdd03 100644 --- a/src/game/backend/bindings/Tiles.cpp +++ b/src/game/backend/bindings/Tiles.cpp @@ -7,6 +7,7 @@ #include "gse/type/Object.h" #include "gse/type/Undefined.h" #include "game/backend/Game.h" +#include "game/backend/map/tile/TileManager.h" #include "game/backend/event/RequestTileLocks.h" #include "game/backend/map/tile/Tile.h" #include "Bindings.h" @@ -30,14 +31,14 @@ BINDING_IMPL( tiles ) { tile_positions.push_back( tile->coord ); } - GAME->SendTileLockRequest( tile_positions, [ this, on_complete, tile_positions, ctx, call_si ]() { + GAME->GetTM()->SendTileLockRequest( tile_positions, [ this, on_complete, tile_positions, ctx, call_si ]() { on_complete->Run( ctx, call_si, { VALUE( gse::callable::Native, [ this, tile_positions ]( gse::context::Context* ctx, const gse::si_t& call_si, const gse::type::function_arguments_t& arguments ) -> gse::Value { - GAME->SendTileUnlockRequest( tile_positions ); + GAME->GetTM()->SendTileUnlockRequest( tile_positions ); return VALUE( gse::type::Undefined ); }), }); diff --git a/src/game/backend/event/DefineAnimation.cpp b/src/game/backend/event/DefineAnimation.cpp index 8dbce84e..678ea54a 100644 --- a/src/game/backend/event/DefineAnimation.cpp +++ b/src/game/backend/event/DefineAnimation.cpp @@ -4,6 +4,7 @@ #include "engine/Engine.h" #include "loader/sound/SoundLoader.h" +#include "game/backend/animation/AnimationManager.h" #include "game/backend/animation/Def.h" #include "gse/type/Undefined.h" @@ -28,7 +29,7 @@ const std::string* DefineAnimation::Validate( Game* game ) const { } const gse::Value DefineAnimation::Apply( Game* game ) const { - game->DefineAnimation( m_def ); + game->GetAM()->DefineAnimation( m_def ); return VALUE( gse::type::Undefined ); } diff --git a/src/game/backend/event/LockTiles.cpp b/src/game/backend/event/LockTiles.cpp index cea643fa..40e55971 100644 --- a/src/game/backend/event/LockTiles.cpp +++ b/src/game/backend/event/LockTiles.cpp @@ -2,6 +2,7 @@ #include "game/backend/Game.h" #include "game/backend/State.h" +#include "game/backend/map/tile/TileManager.h" #include "gse/type/Undefined.h" namespace game { @@ -26,7 +27,7 @@ const std::string* LockTiles::Validate( Game* game ) const { } const gse::Value LockTiles::Apply( Game* game ) const { - game->LockTiles( m_lock_owner_slot, m_tile_positions ); + game->GetTM()->LockTiles( m_lock_owner_slot, m_tile_positions ); return VALUE( gse::type::Undefined ); } diff --git a/src/game/backend/event/RequestTileLocks.cpp b/src/game/backend/event/RequestTileLocks.cpp index 87d7d5e9..7f6d3fd9 100644 --- a/src/game/backend/event/RequestTileLocks.cpp +++ b/src/game/backend/event/RequestTileLocks.cpp @@ -2,6 +2,7 @@ #include "game/backend/Game.h" #include "game/backend/State.h" +#include "game/backend/map/tile/TileManager.h" #include "gse/type/Undefined.h" namespace game { @@ -22,7 +23,7 @@ const std::string* RequestTileLocks::Validate( Game* game ) const { } const gse::Value RequestTileLocks::Apply( Game* game ) const { - game->RequestTileLocks( m_initiator_slot, m_tile_positions ); + game->GetTM()->RequestTileLocks( m_initiator_slot, m_tile_positions ); return VALUE( gse::type::Undefined ); } diff --git a/src/game/backend/event/RequestTileUnlocks.cpp b/src/game/backend/event/RequestTileUnlocks.cpp index c71c9134..b7c4b5d1 100644 --- a/src/game/backend/event/RequestTileUnlocks.cpp +++ b/src/game/backend/event/RequestTileUnlocks.cpp @@ -2,6 +2,7 @@ #include "game/backend/Game.h" #include "game/backend/State.h" +#include "game/backend/map/tile/TileManager.h" #include "gse/type/Undefined.h" namespace game { @@ -22,7 +23,7 @@ const std::string* RequestTileUnlocks::Validate( Game* game ) const { } const gse::Value RequestTileUnlocks::Apply( Game* game ) const { - game->RequestTileUnlocks( m_initiator_slot, m_tile_positions ); + game->GetTM()->RequestTileUnlocks( m_initiator_slot, m_tile_positions ); return VALUE( gse::type::Undefined ); } diff --git a/src/game/backend/event/UnlockTiles.cpp b/src/game/backend/event/UnlockTiles.cpp index c158d41c..7ad7297d 100644 --- a/src/game/backend/event/UnlockTiles.cpp +++ b/src/game/backend/event/UnlockTiles.cpp @@ -2,6 +2,7 @@ #include "game/backend/Game.h" #include "game/backend/State.h" +#include "game/backend/map/tile/TileManager.h" #include "gse/type/Undefined.h" namespace game { @@ -26,7 +27,7 @@ const std::string* UnlockTiles::Validate( Game* game ) const { } const gse::Value UnlockTiles::Apply( Game* game ) const { - game->UnlockTiles( m_lock_owner_slot, m_tile_positions ); + game->GetTM()->UnlockTiles( m_lock_owner_slot, m_tile_positions ); return VALUE( gse::type::Undefined ); } diff --git a/src/game/backend/map/tile/CMakeLists.txt b/src/game/backend/map/tile/CMakeLists.txt index 89f648be..5b79a723 100644 --- a/src/game/backend/map/tile/CMakeLists.txt +++ b/src/game/backend/map/tile/CMakeLists.txt @@ -1,7 +1,9 @@ SET( SRC ${SRC} + ${PWD}/TileManager.cpp ${PWD}/Tile.cpp ${PWD}/TileState.cpp ${PWD}/Tiles.cpp + ${PWD}/TileLock.cpp PARENT_SCOPE ) diff --git a/src/game/backend/TileLock.cpp b/src/game/backend/map/tile/TileLock.cpp similarity index 90% rename from src/game/backend/TileLock.cpp rename to src/game/backend/map/tile/TileLock.cpp index 392bd22c..b828b985 100644 --- a/src/game/backend/TileLock.cpp +++ b/src/game/backend/map/tile/TileLock.cpp @@ -1,9 +1,11 @@ #include "TileLock.h" -#include "Game.h" +#include "game/backend/Game.h" namespace game { namespace backend { +namespace map { +namespace tile { const size_t TileLock::MAX_WAIT_MS = 10000; @@ -28,3 +30,5 @@ TileLock& TileLock::operator=( const TileLock& other ) { } } +} +} diff --git a/src/game/backend/TileLock.h b/src/game/backend/map/tile/TileLock.h similarity index 92% rename from src/game/backend/TileLock.h rename to src/game/backend/map/tile/TileLock.h index 052026ec..1c901b75 100644 --- a/src/game/backend/TileLock.h +++ b/src/game/backend/map/tile/TileLock.h @@ -8,6 +8,9 @@ namespace backend { class Game; +namespace map { +namespace tile { + class TileLock { public: static const size_t MAX_WAIT_MS; @@ -25,3 +28,5 @@ class TileLock { } } +} +} diff --git a/src/game/backend/map/tile/TileManager.cpp b/src/game/backend/map/tile/TileManager.cpp new file mode 100644 index 00000000..c2718dfe --- /dev/null +++ b/src/game/backend/map/tile/TileManager.cpp @@ -0,0 +1,219 @@ +#include "TileManager.h" + +#include "game/backend/Game.h" +#include "game/backend/State.h" +#include "game/backend/slot/Slot.h" +#include "game/backend/slot/Slots.h" +#include "game/backend/map/Map.h" +#include "game/backend/event/LockTiles.h" +#include "game/backend/event/UnlockTiles.h" + +#include "gse/context/Context.h" +#include "gse/callable/Native.h" +#include "gse/type/Array.h" + +namespace game { +namespace backend { +namespace map { +namespace tile { + +TileManager::TileManager( Game* game ) + : m_game( game ) { + // +} + +TileManager::~TileManager() { + // +} + +void TileManager::Clear() { + m_tile_lock_requests.clear(); + m_tile_locks.clear(); + m_tile_lock_callbacks.clear(); +} + +void TileManager::SendTileLockRequest( const map::tile::positions_t& tile_positions, const cb_oncomplete& on_complete ) { + // testing + /*m_tile_lock_callbacks.push_back( + { + tile_positions, + on_complete + } + );*/ + LockTiles( m_game->GetSlotNum(), tile_positions ); + on_complete(); + //AddEvent( new event::RequestTileLocks( m_slot_num, tile_positions ) ); +} + +void TileManager::RequestTileLocks( const size_t initiator_slot, const map::tile::positions_t& tile_positions ) { + if ( m_game->GetState()->IsMaster() ) { // huh? + Log( "Tile locks request from " + std::to_string( initiator_slot ) + ": " + map::tile::Tile::TilePositionsToString( tile_positions, "" ) ); + AddTileLockRequest( true, initiator_slot, tile_positions ); + } +} + +void TileManager::LockTiles( const size_t initiator_slot, const map::tile::positions_t& tile_positions ) { + for ( const auto& pos : tile_positions ) { + auto* tile = m_game->GetMap()->GetTile( pos.x, pos.y ); + ASSERT_NOLOG( !tile->IsLocked(), "tile " + pos.ToString() + " is already locked" ); + tile->Lock( initiator_slot ); + } + for ( auto it = m_tile_lock_callbacks.begin() ; it != m_tile_lock_callbacks.end() ; it++ ) { + const auto& it_positions = it->first; + const auto sz = it_positions.size(); + if ( sz == tile_positions.size() ) { + bool match = true; + for ( size_t i = 0 ; i < sz ; i++ ) { + if ( tile_positions.at( i ) != it_positions.at( i ) ) { + match = false; + break; + } + } + if ( match ) { + const auto cb = it->second; + m_tile_lock_callbacks.erase( it ); + cb(); + break; + } + } + } +} + +void TileManager::SendTileUnlockRequest( const map::tile::positions_t& tile_positions ) { + // testing + UnlockTiles( m_game->GetSlotNum(), tile_positions ); + //AddEvent( new event::RequestTileUnlocks( m_slot_num, tile_positions ) ); +} + +void TileManager::RequestTileUnlocks( const size_t initiator_slot, const map::tile::positions_t& tile_positions ) { + if ( m_game->GetState()->IsMaster() ) { + Log( "Tile unlocks request from " + std::to_string( initiator_slot ) + ": " + map::tile::Tile::TilePositionsToString( tile_positions, "" ) ); + AddTileLockRequest( false, initiator_slot, tile_positions ); + } +} + +void TileManager::UnlockTiles( const size_t initiator_slot, const map::tile::positions_t& tile_positions ) { + for ( const auto& pos : tile_positions ) { + auto* tile = m_game->GetMap()->GetTile( pos.x, pos.y ); + ASSERT_NOLOG( tile->IsLockedBy( initiator_slot ), "tile " + pos.ToString() + " is not locked by " + std::to_string( initiator_slot ) ); + tile->Unlock(); + } +} + +void TileManager::ProcessTileLockRequests() { + if ( m_game->IsRunning() ) { + auto* state = m_game->GetState(); + if ( state->IsMaster() && !m_tile_lock_requests.empty() ) { + const auto tile_lock_requests = m_tile_lock_requests; + m_tile_lock_requests.clear(); + const auto slot_num = m_game->GetSlotNum(); + for ( const auto& req : tile_lock_requests ) { + const auto slot_state = state->m_slots->GetSlot( req.initiator_slot ).GetState(); + if ( slot_state != slot::Slot::SS_PLAYER ) { + // player disconnected? + Log( "Skipping tile locks/unlocks for slot " + std::to_string( req.initiator_slot ) + " (invalid slot state: " + std::to_string( slot_state ) + ")" ); + continue; + } + auto it = m_tile_locks.find( req.initiator_slot ); + if ( it == m_tile_locks.end() ) { + it = m_tile_locks.insert( + { + req.initiator_slot, + {} + } + ).first; + } + auto& locks = it->second; + if ( req.is_lock ) { + // lock + Log( "Locking tiles for " + std::to_string( req.initiator_slot ) + ": " + map::tile::Tile::TilePositionsToString( req.tile_positions ) ); + locks.push_back( TileLock{ req.tile_positions } ); + auto e = new event::LockTiles( slot_num, req.tile_positions, req.initiator_slot ); + e->SetDestinationSlot( req.initiator_slot ); + m_game->AddEvent( e ); + } + else { + // unlock + bool found = false; + for ( auto locks_it = locks.begin() ; locks_it != locks.end() ; locks_it++ ) { + if ( locks_it->Matches( req.tile_positions ) ) { + found = true; + Log( "Unlocking tiles for " + std::to_string( req.initiator_slot ) + ": " + map::tile::Tile::TilePositionsToString( req.tile_positions ) ); + locks.erase( locks_it ); + if ( locks.empty() ) { + m_tile_locks.erase( it ); + } + auto e = new event::UnlockTiles( slot_num, req.tile_positions, req.initiator_slot ); + e->SetDestinationSlot( req.initiator_slot ); + m_game->AddEvent( e ); + break; + } + } + if ( !found ) { + Log( "Could not find matching tile locks for " + std::to_string( req.initiator_slot ) + ": " + map::tile::Tile::TilePositionsToString( req.tile_positions ) ); + } + } + } + } + } +} + +void TileManager::ReleaseTileLocks( const size_t initiator_slot ) { + const auto& it = m_tile_locks.find( initiator_slot ); + if ( it != m_tile_locks.end() ) { + Log( "Releasing " + std::to_string( it->second.size() ) + " tile locks" ); + m_tile_locks.erase( it ); + } +} + +WRAPIMPL_BEGIN( TileManager, CLASS_TM ) + WRAPIMPL_PROPS + { + "lock_tiles", + NATIVE_CALL( this ) { + N_EXPECT_ARGS( 2 ); + N_GETVALUE( tiles, 0, Array ); + N_PERSIST_CALLABLE( on_complete, 1 ); + map::tile::positions_t tile_positions = {}; + tile_positions.reserve( tiles.size() ); + for ( const auto& tileobj : tiles ) { + N_UNWRAP( tile, tileobj, map::tile::Tile ); + tile_positions.push_back( tile->coord ); + } + SendTileLockRequest( tile_positions, [ this, on_complete, tile_positions, ctx, call_si ]() { + on_complete->Run( ctx, call_si, { + VALUE( gse::callable::Native, [ this, tile_positions ]( + gse::context::Context* ctx, + const gse::si_t& call_si, + const gse::type::function_arguments_t& arguments + ) -> gse::Value { + SendTileUnlockRequest( tile_positions ); + return VALUE( gse::type::Undefined ); + }), + }); + N_UNPERSIST_CALLABLE( on_complete ); + }); + return VALUE( gse::type::Undefined ); + }) + + }, + }; +WRAPIMPL_END_PTR( TileManager ) + +UNWRAPIMPL_PTR( TileManager ) + +void TileManager::AddTileLockRequest( const bool is_lock, const size_t initiator_slot, const map::tile::positions_t& tile_positions ) { + ASSERT_NOLOG( m_game->GetState() && m_game->GetState()->IsMaster(), "only master can manage tile locks" ); + m_tile_lock_requests.push_back( + { + is_lock, + initiator_slot, + tile_positions + } + ); +} + +} +} +} +} diff --git a/src/game/backend/map/tile/TileManager.h b/src/game/backend/map/tile/TileManager.h new file mode 100644 index 00000000..864ebc19 --- /dev/null +++ b/src/game/backend/map/tile/TileManager.h @@ -0,0 +1,62 @@ +#pragma once + +#include "common/Common.h" + +#include "gse/Wrappable.h" +#include "gse/type/Object.h" + +#include "Types.h" +#include "TileLock.h" + +namespace game { +namespace backend { + +class Game; + +namespace map { +namespace tile { + +CLASS2( TileManager, common::Class, gse::Wrappable ) +public: + TileManager( Game* game ); + ~TileManager(); + + typedef std::function< void() > cb_oncomplete; + + void Clear(); + + void SendTileLockRequest( const map::tile::positions_t& tile_positions, const cb_oncomplete& on_complete ); + void RequestTileLocks( const size_t initiator_slot, const map::tile::positions_t& tile_positions ); + void LockTiles( const size_t initiator_slot, const map::tile::positions_t& tile_positions ); + + void SendTileUnlockRequest( const map::tile::positions_t& tile_positions ); + void RequestTileUnlocks( const size_t initiator_slot, const map::tile::positions_t& tile_positions ); + void UnlockTiles( const size_t initiator_slot, const map::tile::positions_t& tile_positions ); + + void ProcessTileLockRequests(); + void ReleaseTileLocks( const size_t initiator_slot ); + + WRAPDEFS_PTR( TileManager ) + +private: + Game* m_game = nullptr; + + // server-side lock tracking + struct tile_lock_request_t { + const bool is_lock; // lock or unlock + const size_t initiator_slot; + const map::tile::positions_t tile_positions; + }; + typedef std::vector< tile_lock_request_t > tile_lock_requests_t; // requests fifo + tile_lock_requests_t m_tile_lock_requests = {}; + std::unordered_map< size_t, std::vector< TileLock > > m_tile_locks = {}; // slot id, locks + std::vector< std::pair< map::tile::positions_t, cb_oncomplete > > m_tile_lock_callbacks = {}; // tile positions (for matching), callback + + void AddTileLockRequest( const bool is_lock, const size_t initiator_slot, const map::tile::positions_t& tile_positions ); + +}; + +} +} +} +} diff --git a/src/game/backend/unit/UnitManager.cpp b/src/game/backend/unit/UnitManager.cpp index 9cb2169d..6edcdafc 100644 --- a/src/game/backend/unit/UnitManager.cpp +++ b/src/game/backend/unit/UnitManager.cpp @@ -5,6 +5,7 @@ #include "Unit.h" #include "game/backend/Game.h" +#include "game/backend/animation/AnimationManager.h" #include "game/backend/State.h" #include "game/backend/bindings/Bindings.h" #include "game/backend/slot/Slots.h" @@ -252,37 +253,8 @@ void UnitManager::PushUnitUpdates() { } } -WRAPIMPL_BEGIN( UnitManager, CLASS_UNITS ) +WRAPIMPL_BEGIN( UnitManager, CLASS_UM ) WRAPIMPL_PROPS - { - "lock_tiles", - NATIVE_CALL( this ) { - N_EXPECT_ARGS( 2 ); - N_GETVALUE( tiles, 0, Array ); - N_PERSIST_CALLABLE( on_complete, 1 ); - map::tile::positions_t tile_positions = {}; - tile_positions.reserve( tiles.size() ); - for ( const auto& tileobj : tiles ) { - N_UNWRAP( tile, tileobj, map::tile::Tile ); - tile_positions.push_back( tile->coord ); - } - m_game->SendTileLockRequest( tile_positions, [ this, on_complete, tile_positions, ctx, call_si ]() { - on_complete->Run( ctx, call_si, { - VALUE( gse::callable::Native, [ this, tile_positions ]( - gse::context::Context* ctx, - const gse::si_t& call_si, - const gse::type::function_arguments_t& arguments - ) -> gse::Value { - m_game->SendTileUnlockRequest( tile_positions ); - return VALUE( gse::type::Undefined ); - }), - }); - N_UNPERSIST_CALLABLE( on_complete ); - }); - return VALUE( gse::type::Undefined ); - }) - - }, /* { "add", NATIVE_CALL( this ) { @@ -556,7 +528,7 @@ const std::string* UnitManager::MoveUnitToTile( Unit* unit, map::tile::Tile* dst dst_tile->coord.x, dst_tile->coord.y }; - fr.data.unit_move.running_animation_id = m_game->AddAnimationCallback( + fr.data.unit_move.running_animation_id = m_game->GetAM()->AddAnimationCallback( [ on_complete, unit, dst_tile ]() { unit->SetTile( dst_tile ); on_complete(); diff --git a/src/gse/type/Object.h b/src/gse/type/Object.h index 4ca362af..8d65dff4 100644 --- a/src/gse/type/Object.h +++ b/src/gse/type/Object.h @@ -31,15 +31,17 @@ class Object : public Type { CLASS_SYSTEM, CLASS_STATE, CLASS_GAME, + CLASS_TM, CLASS_TILE, CLASS_PLAYER, CLASS_FACTION, CLASS_FACTIONS, - CLASS_UNITS, + CLASS_UM, CLASS_UNITDEF, CLASS_UNIT, CLASS_BASE, CLASS_BASE_POP, + CLASS_AM, }; static const std::string& GetClassString( const object_class_t object_class ); From 905a830e008606b03c839bc41b2275df8fa82da1 Mon Sep 17 00:00:00 2001 From: afwbkbc Date: Tue, 12 Nov 2024 21:32:05 +0200 Subject: [PATCH 06/13] replaced some of #game globals with game properties --- GLSMAC_data/default/main.gls.js | 242 +++++++------ GLSMAC_data/default/units/combat.gls.js | 8 +- GLSMAC_data/default/units/movement.gls.js | 2 +- src/game/backend/CMakeLists.txt | 1 + src/game/backend/Game.cpp | 335 ++++-------------- src/game/backend/Game.h | 50 +-- src/game/backend/Random.cpp | 55 +++ src/game/backend/Random.h | 24 ++ src/game/backend/base/Base.cpp | 8 +- src/game/backend/base/BaseManager.cpp | 311 ++++++++++++++++ src/game/backend/base/BaseManager.h | 84 +++++ src/game/backend/base/CMakeLists.txt | 1 + src/game/backend/base/Pop.cpp | 5 +- src/game/backend/bindings/Bindings.cpp | 9 +- src/game/backend/bindings/Map.cpp | 2 +- src/game/backend/bindings/Random.cpp | 2 +- src/game/backend/bindings/Tiles.cpp | 2 +- src/game/backend/event/DefinePop.cpp | 4 +- src/game/backend/event/SpawnBase.cpp | 3 +- src/game/backend/map/Map.cpp | 4 +- src/game/backend/map/Map.h | 6 +- .../backend/map/module/CalculateCoords.cpp | 2 +- src/game/backend/map/module/Coastlines1.cpp | 2 +- src/game/backend/map/module/Coastlines2.cpp | 2 +- src/game/backend/map/module/Finalize.cpp | 2 +- src/game/backend/map/module/LandSurface.cpp | 2 +- src/game/backend/map/module/Module.cpp | 2 +- src/game/backend/map/module/Sprites.cpp | 2 +- src/game/backend/map/tile/TileManager.cpp | 44 ++- .../backend/map_editor/tool/Elevations.cpp | 2 +- src/game/backend/unit/UnitManager.cpp | 181 ++++------ src/game/backend/unit/UnitManager.h | 17 +- src/gse/type/Object.h | 3 + 33 files changed, 848 insertions(+), 571 deletions(-) create mode 100644 src/game/backend/Random.cpp create mode 100644 src/game/backend/Random.h create mode 100644 src/game/backend/base/BaseManager.cpp create mode 100644 src/game/backend/base/BaseManager.h diff --git a/GLSMAC_data/default/main.gls.js b/GLSMAC_data/default/main.gls.js index 415244e4..af9befd1 100644 --- a/GLSMAC_data/default/main.gls.js +++ b/GLSMAC_data/default/main.gls.js @@ -10,146 +10,152 @@ const bases = #include('bases'); e.gm.on('start', (e) => { + let players = []; + let players_sz = 0; + let random_player = () => { + return players[(e.game.random.get_int(0, players_sz - 1))]; + }; + + let random_morale = () => { + return e.game.random.get_int(0, 6); // TODO: get some constants from api + }; + + let random_health = () => { + return e.game.random.get_float(#to_float(0.1), #to_float(1)); + }; + + // will be populated on start + const pop_types = [ + 'Worker', + 'Talent', + 'Doctor', + 'Librarian', + ]; + + let add_pops = (base, count) => { + for (let i = 0; i < count; i++) { + const type = pop_types[(e.game.random.get_int(0, #size(pop_types) - 1))]; + base.create_pop({ + type: type, + }); + } + }; + + let all_bases = []; + e.game.on('configure', (e) => { units.configure(e.game); - // will be populated on start - let players = []; - let players_sz = 0; - let random_player = () => { - return players[(#game.random.get_int(0, players_sz - 1))]; - }; - - let random_morale = () => { - return #game.random.get_int(0, 6); // TODO: get some constants from api - }; - - let random_health = () => { - return #game.random.get_float(#to_float(0.1), #to_float(1)); - }; - - const pop_types = [ - 'Worker', - 'Talent', - 'Doctor', - 'Librarian', - ]; - - let add_pops = ( base, count ) => { - for (let i = 0; i < count; i++) { - const type = pop_types[(#game.random.get_int(0, #size(pop_types) - 1))]; - base.create_pop({ - type: type, - }); - } - }; - - let all_bases = []; - - #game.on.start((e) => { - - // init game data - resources.define(); - map.define(); - units.define(); - bases.define(); - - // init players - players = #game.players.get_all(); - players_sz = #size(players); - - // spawn units - - let units_spawned = 0; - let bases_spawned = 0; - - let w = #game.map.get_width(); - let h = #game.map.get_height(); + }); - for (let y = 0; y < h; y++) { - for (let x = 0; x < w; x++) { - if (x % 2 == y % 2) { - let owner = random_player(); - let tile = #game.map.get_tile(x, y); - if (#game.random.get_int(0, 6) == 0) { - let units_count = #game.random.get_int(1, 2); - for (let i = 0; i < units_count; i++) { - if (tile.is_land) { - if (#game.random.get_int(0, 4) != 0) { - #game.units.spawn('MindWorms', owner, tile, random_morale(), random_health()); - units_spawned++; - } else { - if (tile.has_fungus && #game.random.get_int(0, 3) == 0) { - // morale depends on count of fungus tiles around - let morale = 1; - for (neighbour of tile.get_surrounding_tiles()) { - if (morale >= 6) { - break; - } - if (neighbour.has_fungus) { - morale++; - } + e.game.on('start', (e) => { + + // init game data + resources.define(); + map.define(); + units.define(); + bases.define(); + + // init players + players = e.game.get_players(); + players_sz = #size(players); + + // spawn units + + let units_spawned = 0; + let bases_spawned = 0; + + let w = e.game.tm.get_map_width(); + let h = e.game.tm.get_map_height(); + + for (let y = 0; y < h; y++) { + for (let x = 0; x < w; x++) { + if (x % 2 == y % 2) { + let owner = random_player(); + let tile = e.game.tm.get_tile(x, y); + if (e.game.random.get_int(0, 6) == 0) { + let units_count = e.game.random.get_int(1, 2); + for (let i = 0; i < units_count; i++) { + if (tile.is_land) { + if (e.game.random.get_int(0, 4) != 0) { + e.game.um.spawn_unit('MindWorms', owner, tile, random_morale(), random_health()); + units_spawned++; + } else { + if (tile.has_fungus && e.game.random.get_int(0, 3) == 0) { + // morale depends on count of fungus tiles around + let morale = 1; + for (neighbour of tile.get_surrounding_tiles()) { + if (morale >= 6) { + break; + } + if (neighbour.has_fungus) { + morale++; } - #game.units.spawn('FungalTower', owner, tile, morale, random_health()); - units_spawned++; - } else { - #game.units.spawn('SporeLauncher', owner, tile, random_morale(), random_health()); - units_spawned++; } - } - } else { - if (#game.random.get_int(0, 3) == 0) { - #game.units.spawn('SeaLurk', owner, tile, random_morale(), random_health()); + e.game.um.spawn_unit('FungalTower', owner, tile, morale, random_health()); + units_spawned++; + } else { + e.game.um.spawn_unit('SporeLauncher', owner, tile, random_morale(), random_health()); units_spawned++; } } - } - } - if (#game.random.get_int(0, 5) == 0) { - let has_adjactent_bases = false; - for (neighbour of tile.get_surrounding_tiles()) { - if (neighbour.get_base() != null) { - has_adjactent_bases = true; - break; + } else { + if (e.game.random.get_int(0, 3) == 0) { + e.game.um.spawn_unit('SeaLurk', owner, tile, random_morale(), random_health()); + units_spawned++; } } - if (!has_adjactent_bases) { - let base = #game.bases.spawn( - owner, - tile, - { - // name: 'base name', - } - ); - add_pops(base, #game.random.get_int(1, 7)); - all_bases []= base; - bases_spawned++; + } + } + if (e.game.random.get_int(0, 5) == 0) { + let has_adjactent_bases = false; + for (neighbour of tile.get_surrounding_tiles()) { + if (neighbour.get_base() != null) { + has_adjactent_bases = true; + break; } } + if (!has_adjactent_bases) { + let base = e.game.bm.spawn_base( + owner, + tile, + { + // name: 'base name', + } + ); + add_pops(base, e.game.random.get_int(1, 7)); + all_bases []= base; + bases_spawned++; + } } } } - #game.message('Total units spawned: ' + #to_string(units_spawned)); - #game.message('Total bases spawned: ' + #to_string(bases_spawned)); + } + e.game.message('Total units spawned: ' + #to_string(units_spawned)); + e.game.message('Total bases spawned: ' + #to_string(bases_spawned)); - }); + }); - #game.on.turn((e) => { - for ( base of all_bases ) { - add_pops(base, 1); - } - // - }); + e.game.on('turn', (e) => { + for ( base of all_bases ) { + add_pops(base, 1); + } + // + }); - #game.on.unit_spawn((e) => { - // - }); + e.game.um.on('unit_spawn', (e) => { + // + }); - #game.on.unit_despawn((e) => { - // - }); + e.game.um.on('unit_despawn', (e) => { + // + }); + e.game.bm.on('base_spawn', (e) => { + // }); + }); + }); diff --git a/GLSMAC_data/default/units/combat.gls.js b/GLSMAC_data/default/units/combat.gls.js index 588f74f1..d13eb9b0 100644 --- a/GLSMAC_data/default/units/combat.gls.js +++ b/GLSMAC_data/default/units/combat.gls.js @@ -70,15 +70,15 @@ const result = { let damage_sequence = []; while (attacker_health > 0.0 && defender_health > 0.0) { - let attack_roll = #game.random.get_float(0.0, attack_power); - let defence_roll = #game.random.get_float(0.0, defence_power); + let attack_roll = game.random.get_float(0.0, attack_power); + let defence_roll = game.random.get_float(0.0, defence_power); if (attack_roll >= defence_roll) { - let damage = #min(defender_health, #game.random.get_float(MIN_DAMAGE_VALUE, MAX_DAMAGE_VALUE)); + let damage = #min(defender_health, game.random.get_float(MIN_DAMAGE_VALUE, MAX_DAMAGE_VALUE)); damage_sequence [] = [true, damage]; defender_health -= damage; } if (defence_roll >= attack_roll) { - let damage = #min(attacker_health, #game.random.get_float(MIN_DAMAGE_VALUE, MAX_DAMAGE_VALUE)); + let damage = #min(attacker_health, game.random.get_float(MIN_DAMAGE_VALUE, MAX_DAMAGE_VALUE)); damage_sequence [] = [false, damage]; attacker_health -= damage; } diff --git a/GLSMAC_data/default/units/movement.gls.js b/GLSMAC_data/default/units/movement.gls.js index eabd8393..442786ae 100644 --- a/GLSMAC_data/default/units/movement.gls.js +++ b/GLSMAC_data/default/units/movement.gls.js @@ -81,7 +81,7 @@ const result = { is_movement_successful: (e.unit.movement >= movement_cost) // unit has enough moves || - (#game.random.get_float(0.0, movement_cost) < e.unit.movement) // unit doesn't have enough moves but was lucky + (game.random.get_float(0.0, movement_cost) < e.unit.movement) // unit doesn't have enough moves but was lucky }; }); diff --git a/src/game/backend/CMakeLists.txt b/src/game/backend/CMakeLists.txt index f7b54c5d..faaa1746 100644 --- a/src/game/backend/CMakeLists.txt +++ b/src/game/backend/CMakeLists.txt @@ -14,6 +14,7 @@ SUBDIR( turn ) SET( SRC ${SRC} + ${PWD}/Random.cpp ${PWD}/System.cpp ${PWD}/Game.cpp ${PWD}/State.cpp diff --git a/src/game/backend/Game.cpp b/src/game/backend/Game.cpp index 112f6c7d..38c2cfe8 100644 --- a/src/game/backend/Game.cpp +++ b/src/game/backend/Game.cpp @@ -14,7 +14,7 @@ #include "event/FinalizeTurn.h" #include "event/TurnFinalized.h" #include "event/AdvanceTurn.h" -#include "util/random/Random.h" +#include "Random.h" #include "config/Config.h" #include "slot/Slots.h" #include "Player.h" @@ -39,6 +39,7 @@ #include "unit/UnitManager.h" #include "unit/Unit.h" #include "unit/MoraleSet.h" +#include "base/BaseManager.h" #include "base/PopDef.h" #include "base/Base.h" #include "animation/AnimationManager.h" @@ -168,7 +169,7 @@ void Game::Start() { ASSERT( !m_pending_frontend_requests, "frontend requests already set" ); NEW( m_pending_frontend_requests, std::vector< FrontendRequest > ); - NEW( m_random, util::random::Random ); + NEW( m_random, Random, this ); const auto* config = g_engine->GetConfig(); if ( config->HasLaunchFlag( config::Config::LF_QUICKSTART_SEED ) ) { @@ -180,6 +181,7 @@ void Game::Start() { NEW( m_tm, map::tile::TileManager, this ); NEW( m_um, unit::UnitManager, this ); + NEW( m_bm, base::BaseManager, this ); NEW( m_am, animation::AnimationManager, this ); } @@ -203,6 +205,9 @@ void Game::Stop() { DELETE( m_um ); m_um = nullptr; + DELETE( m_bm ); + m_bm = nullptr; + DELETE( m_am ); m_am = nullptr; @@ -379,11 +384,8 @@ void Game::Iterate() { if ( m_game_state == GS_RUNNING ) { m_um->ProcessUnprocessed(); + m_bm->ProcessUnprocessed(); - for ( auto& it : m_unprocessed_bases ) { - SpawnBase( it ); - } - m_unprocessed_bases.clear(); for ( auto& it : m_unprocessed_events ) { ASSERT( m_connection, "connection not set" ); try { @@ -405,7 +407,12 @@ void Game::Iterate() { g_engine->GetUI()->SetLoaderText( "Starting game...", false ); if ( m_state->IsMaster() ) { try { - m_state->m_bindings->Call( bindings::Bindings::CS_ON_START ); + m_state->m_bindings->Trigger( this, "start", { + { + "game", + Wrap() + }, + }); } catch ( gse::Exception& e ) { Log( (std::string)"Initialization failed: " + e.ToStringAndCleanup() ); @@ -427,12 +434,12 @@ void Game::Iterate() { Log( (std::string)e.what() ); Quit( "Event validation error" ); // TODO: fix reason } - m_um->PushUnitUpdates(); - PushBaseUpdates(); + m_um->PushUpdates(); + m_bm->PushUpdates(); m_tm->ProcessTileLockRequests(); } -util::random::Random* Game::GetRandom() const { +Random* Game::GetRandom() const { return m_random; } @@ -461,17 +468,50 @@ const size_t Game::GetSlotNum() const { WRAPIMPL_BEGIN( Game, CLASS_GAME ) WRAPIMPL_PROPS + { + "random", + m_random->Wrap() + }, { "tm", - m_tm->Wrap( true ) + m_tm->Wrap() }, { "um", - m_um->Wrap( true ) + m_um->Wrap() + }, + { + "bm", + m_bm->Wrap(), }, { "am", - m_am->Wrap( true ) + m_am->Wrap() + }, + { + "message", + NATIVE_CALL( this ) { + N_EXPECT_ARGS( 1 ); + N_GETVALUE( text, 0, String ); + Message( text ); + return VALUE( gse::type::Undefined ); + }) + }, + { + "get_players", + NATIVE_CALL( this ) { + N_EXPECT_ARGS( 0 ); + auto& slots = m_state->m_slots->GetSlots(); + gse::type::array_elements_t elements = {}; + for ( auto& slot : slots ) { + const auto state = slot.GetState(); + if ( state == slot::Slot::SS_OPEN || state == slot::Slot::SS_CLOSED ) { + continue; // skip + } + elements.push_back( slot.Wrap() ); + } + return VALUE( gse::type::Array, elements ); + } ) }, }; WRAPIMPL_END_PTR( Game ) @@ -817,26 +857,6 @@ void Game::OnGSEError( gse::Exception& err ) { AddFrontendRequest( fr ); } -base::PopDef* Game::GetPopDef( const std::string& id ) const { - const auto& it = m_base_popdefs.find( id ); - if ( it != m_base_popdefs.end() ) { - return it->second; - } - else { - return nullptr; - } -} - -base::Base* Game::GetBase( const size_t id ) const { - const auto& it = m_bases.find( id ); - if ( it != m_bases.end() ) { - return it->second; - } - else { - return nullptr; - } -} - const gse::Value Game::AddEvent( event::Event* event ) { ASSERT( event->m_initiator_slot == m_slot_num, "initiator slot mismatch" ); if ( m_connection ) { @@ -849,10 +869,6 @@ const gse::Value Game::AddEvent( event::Event* event ) { return VALUE( gse::type::Undefined ); } -void Game::RefreshBase( const base::Base* base ) { - QueueBaseUpdate( base, BUO_REFRESH ); -} - void Game::DefineResource( Resource* resource ) { Log( "Defining resource ('" + resource->m_id + "')" ); @@ -873,82 +889,6 @@ void Game::DefineResource( Resource* resource ) { m_resource_idx.push_back( resource->m_id ); } -void Game::DefinePop( base::PopDef* pop_def ) { - Log( "Defining base pop ('" + pop_def->m_id + "')" ); - - ASSERT( m_base_popdefs.find( pop_def->m_id ) == m_base_popdefs.end(), "Base pop def '" + pop_def->m_id + "' already exists" ); - - m_base_popdefs.insert( - { - pop_def->m_id, - pop_def - } - ); - - auto fr = FrontendRequest( FrontendRequest::FR_BASE_POP_DEFINE ); - NEW( fr.data.base_pop_define.serialized_popdef, std::string, base::PopDef::Serialize( pop_def ).ToString() ); - AddFrontendRequest( fr ); - -} - -void Game::SpawnBase( base::Base* base ) { - if ( m_game_state != GS_RUNNING ) { - m_unprocessed_bases.push_back( base ); - return; - } - - auto* tile = base->GetTile(); - - // validate and fix name if needed (or assign if empty) - std::vector< std::string > names_to_try = {}; - if ( base->m_name.empty() ) { - const auto& names = base->m_owner->GetPlayer()->GetFaction()->m_base_names; - names_to_try = tile->is_water_tile - ? names.water - : names.land; - } - else if ( m_registered_base_names.find( base->m_name ) != m_registered_base_names.end() ) { - names_to_try = { base->m_name }; - } - if ( !names_to_try.empty() ) { - size_t cycle = 0; - bool found = false; - while ( !found ) { - cycle++; - for ( const auto& name_to_try : names_to_try ) { - base->m_name = cycle == 1 - ? name_to_try - : name_to_try + " " + std::to_string( cycle ); - if ( m_registered_base_names.find( base->m_name ) == m_registered_base_names.end() ) { - found = true; - break; - } - } - } - } - m_registered_base_names.insert( base->m_name ); - - Log( "Spawning base #" + std::to_string( base->m_id ) + " ( " + base->m_name + " ) at " + base->GetTile()->ToString() ); - - ASSERT( m_bases.find( base->m_id ) == m_bases.end(), "duplicate base id" ); - m_bases.insert_or_assign( base->m_id, base ); - - QueueBaseUpdate( base, BUO_SPAWN ); - - if ( m_state->IsMaster() ) { - m_state->m_bindings->Call( - bindings::Bindings::CS_ON_BASE_SPAWN, { - { - "base", - base->Wrap() - }, - } - ); - } - - RefreshBase( base ); -} - const size_t Game::GetTurnId() const { return m_current_turn.GetId(); } @@ -1042,20 +982,23 @@ void Game::AdvanceTurn( const size_t turn_id ) { m_um->RefreshUnit( unit ); } - for ( auto& it : m_bases ) { + for ( auto& it : m_bm->GetBases() ) { auto* base = it.second; - m_state->m_bindings->Call( - bindings::Bindings::CS_ON_BASE_TURN, { - { - "base", - base->Wrap( true ) - }, - } - ); - RefreshBase( base ); + m_state->m_bindings->Trigger( m_bm, "base_turn", { + { + "base", + base->Wrap( true ) + }, + }); + m_bm->RefreshBase( base ); } - m_state->m_bindings->Call( bindings::Bindings::CS_ON_TURN ); + m_state->m_bindings->Trigger( this, "turn", { + { + "game", + Wrap() + } + }); for ( const auto& slot : m_state->m_slots->GetSlots() ) { if ( slot.GetState() == slot::Slot::SS_PLAYER ) { @@ -1130,6 +1073,10 @@ unit::UnitManager* Game::GetUM() const { return m_um; } +base::BaseManager* Game::GetBM() const { + return m_bm; +} + animation::AnimationManager* Game::GetAM() const { return m_am; } @@ -1203,55 +1150,6 @@ void Game::UnserializeResources( types::Buffer& buf ) { } } -void Game::SerializeBases( types::Buffer& buf ) const { - - Log( "Serializing " + std::to_string( m_base_popdefs.size() ) + " base pop defs" ); - buf.WriteInt( m_base_popdefs.size() ); - for ( const auto& it : m_base_popdefs ) { - buf.WriteString( it.first ); - buf.WriteString( base::PopDef::Serialize( it.second ).ToString() ); - } - - Log( "Serializing " + std::to_string( m_bases.size() ) + " bases" ); - buf.WriteInt( m_bases.size() ); - for ( const auto& it : m_bases ) { - buf.WriteInt( it.first ); - buf.WriteString( base::Base::Serialize( it.second ).ToString() ); - } - buf.WriteInt( base::Base::GetNextId() ); - - Log( "Saved next base id: " + std::to_string( base::Base::GetNextId() ) ); -} - -void Game::UnserializeBases( types::Buffer& buf ) { - ASSERT( m_base_popdefs.empty(), "base pop defs not empty" ); - ASSERT( m_bases.empty(), "bases not empty" ); - ASSERT( m_unprocessed_bases.empty(), "unprocessed bases not empty" ); - - size_t sz = buf.ReadInt(); - m_base_popdefs.reserve( sz ); - Log( "Unserializing " + std::to_string( sz ) + " base pop defs" ); - for ( size_t i = 0 ; i < sz ; i++ ) { - const auto name = buf.ReadString(); - auto b = types::Buffer( buf.ReadString() ); - DefinePop( base::PopDef::Unserialize( b ) ); - } - - sz = buf.ReadInt(); - Log( "Unserializing " + std::to_string( sz ) + " bases" ); - if ( m_game_state != GS_RUNNING ) { - m_unprocessed_bases.reserve( sz ); - } - for ( size_t i = 0 ; i < sz ; i++ ) { - const auto base_id = buf.ReadInt(); - auto b = types::Buffer( buf.ReadString() ); - SpawnBase( base::Base::Unserialize( b, this ) ); - } - - base::Base::SetNextId( buf.ReadInt() ); - Log( "Restored next base id: " + std::to_string( base::Base::GetNextId() ) ); -} - void Game::AddFrontendRequest( const FrontendRequest& request ) { //Log( "Sending frontend request (type=" + std::to_string( request.type ) + ")" ); // spammy m_pending_frontend_requests->push_back( request ); @@ -1366,7 +1264,7 @@ void Game::InitGame( MT_Response& response, MT_CANCELABLE ) { // bases { types::Buffer b; - SerializeBases( b ); + m_bm->Serialize( b ); buf.WriteString( b.ToString() ); } @@ -1558,7 +1456,7 @@ void Game::InitGame( MT_Response& response, MT_CANCELABLE ) { // bases { auto bb = types::Buffer( buf.ReadString() ); - UnserializeBases( bb ); + m_bm->Unserialize( bb ); } // animations @@ -1622,6 +1520,7 @@ void Game::ResetGame() { m_tm->Clear(); m_um->Clear(); + m_bm->Clear(); m_am->Clear(); for ( auto& it : m_resources ) { @@ -1631,15 +1530,6 @@ void Game::ResetGame() { m_resource_idx.clear(); m_resource_idx_map.clear(); - for ( auto& it : m_base_popdefs ) { - delete it.second; - } - m_base_popdefs.clear(); - for ( auto& it : m_bases ) { - delete it.second; - } - m_bases.clear(); - if ( m_map ) { Log( "Resetting map" ); DELETE( m_map ); @@ -1649,8 +1539,6 @@ void Game::ResetGame() { ASSERT( m_pending_frontend_requests, "pending events not set" ); m_pending_frontend_requests->clear(); - m_base_updates.clear(); - m_current_turn.Reset(); m_is_turn_complete = false; @@ -1692,81 +1580,6 @@ void Game::CheckTurnComplete() { } } -void Game::QueueBaseUpdate( const base::Base* base, const base_update_op_t op ) { - auto it = m_base_updates.find( base->m_id ); - if ( it == m_base_updates.end() ) { - it = m_base_updates.insert( - { - base->m_id, - { - {}, - base, - } - } - ).first; - } - auto& update = it->second; - if ( op == BUO_DESPAWN ) { - if ( op & BUO_SPAWN ) { - // if base is despawned immediately after spawning - frontend doesn't need to know - m_base_updates.erase( it ); - return; - } - update.ops = BUO_NONE; // clear other actions if base was despawned - } - // add to operations list - update.ops = (base_update_op_t)( (uint8_t)update.ops | (uint8_t)op ); -} - -void Game::PushBaseUpdates() { - if ( m_game_state == GS_RUNNING && !m_base_updates.empty() ) { - for ( const auto& it : m_base_updates ) { - const auto base_id = it.first; - const auto& bu = it.second; - const auto& base = bu.base; - if ( bu.ops & BUO_SPAWN ) { - auto fr = FrontendRequest( FrontendRequest::FR_BASE_SPAWN ); - fr.data.base_spawn.base_id = base->m_id; - fr.data.base_spawn.slot_index = base->m_owner->GetIndex(); - const auto* tile = base->GetTile(); - fr.data.base_spawn.tile_coords = { - tile->coord.x, - tile->coord.y - }; - const auto c = base->GetRenderCoords(); - fr.data.base_spawn.render_coords = { - c.x, - c.y, - c.z - }; - NEW( fr.data.base_spawn.name, std::string, base->m_name ); - AddFrontendRequest( fr ); - } - if ( bu.ops & BUO_REFRESH ) { - auto fr = FrontendRequest( FrontendRequest::FR_BASE_UPDATE ); - fr.data.base_update.base_id = base->m_id; - fr.data.base_update.slot_index = base->m_owner->GetIndex(); - NEW( fr.data.base_update.name, std::string, base->m_name ); - NEW( fr.data.base_update.faction_id, std::string, base->m_faction->m_id ); - NEW( fr.data.base_update.pops, FrontendRequest::base_pops_t, {} ); - for ( const auto& pop : base->m_pops ) { - fr.data.base_update.pops->push_back( - { - pop.m_def->m_id, - pop.m_variant - } - ); - } - AddFrontendRequest( fr ); - } - if ( bu.ops & BUO_DESPAWN ) { - THROW( "TODO: BASE DESPAWN" ); - } - } - m_base_updates.clear(); - } -} - const bool Game::IsRunning() const { return m_game_state == GS_RUNNING; } diff --git a/src/game/backend/Game.h b/src/game/backend/Game.h index 3edb1313..8dbecbdb 100644 --- a/src/game/backend/Game.h +++ b/src/game/backend/Game.h @@ -32,13 +32,11 @@ class Data; } } -namespace util::random { -class Random; -} - namespace game { namespace backend { +class Random; + namespace animation { class Def; } @@ -54,15 +52,11 @@ class TileManager; } namespace unit { -class MoraleSet; -class Def; -class Unit; class UnitManager; } namespace base { -class PopDef; -class Base; +class BaseManager; } namespace animation { @@ -319,7 +313,7 @@ CLASS2( Game, MTModule, gse::Wrappable ) void Stop() override; void Iterate() override; - util::random::Random* GetRandom() const; + Random* GetRandom() const; map::Map* GetMap() const; State* GetState() const; const Player* GetPlayer() const; @@ -341,13 +335,8 @@ CLASS2( Game, MTModule, gse::Wrappable ) void Quit( const std::string& reason ); void OnError( std::runtime_error& err ); void OnGSEError( gse::Exception& e ); - base::PopDef* GetPopDef( const std::string& id ) const; - base::Base* GetBase( const size_t id ) const; const gse::Value AddEvent( event::Event* event ); - void RefreshBase( const base::Base* base ); void DefineResource( Resource* resource ); - void DefinePop( base::PopDef* pop_def ); - void SpawnBase( base::Base* base ); const size_t GetTurnId() const; const bool IsTurnActive() const; const bool IsTurnCompleted( const size_t slot_num ) const; @@ -365,6 +354,7 @@ CLASS2( Game, MTModule, gse::Wrappable ) map::tile::TileManager* GetTM() const; unit::UnitManager* GetUM() const; + base::BaseManager* GetBM() const; animation::AnimationManager* GetAM() const; private: @@ -382,13 +372,9 @@ CLASS2( Game, MTModule, gse::Wrappable ) map::tile::TileManager* m_tm = nullptr; unit::UnitManager* m_um = nullptr; + base::BaseManager* m_bm = nullptr; animation::AnimationManager* m_am = nullptr; - std::unordered_map< std::string, base::PopDef* > m_base_popdefs = {}; - std::map< size_t, base::Base* > m_bases = {}; - void SerializeBases( types::Buffer& buf ) const; - void UnserializeBases( types::Buffer& buf ); - enum game_state_t { GS_NONE, GS_PREPARING_MAP, @@ -414,7 +400,7 @@ CLASS2( Game, MTModule, gse::Wrappable ) void ResetGame(); // seed needs to be consistent during session (to prevent save-scumming and for easier reproducing of bugs) - util::random::Random* m_random = nullptr; + Random* m_random = nullptr; State* m_state = nullptr; connection::Connection* m_connection = nullptr; @@ -423,36 +409,16 @@ CLASS2( Game, MTModule, gse::Wrappable ) map_editor::MapEditor* m_map_editor = nullptr; std::vector< backend::event::Event* > m_unprocessed_events = {}; - // TODO: refactor these? - std::vector< base::Base* > m_unprocessed_bases = {}; turn::Turn m_current_turn = {}; bool m_is_turn_complete = false; void CheckTurnComplete(); - enum base_update_op_t : uint8_t { - BUO_NONE = 0, - BUO_SPAWN = 1 << 0, - BUO_REFRESH = 1 << 1, - BUO_DESPAWN = 1 << 2, - }; - struct base_update_t { - base_update_op_t ops = BUO_NONE; - const base::Base* base = nullptr; - }; - std::unordered_map< size_t, base_update_t > m_base_updates = {}; - void QueueBaseUpdate( const base::Base* base, const base_update_op_t op ); - - std::unordered_set< std::string > m_registered_base_names = {}; - -private: - friend class bindings::Bindings; - void PushBaseUpdates(); - private: friend class map::tile::TileManager; friend class unit::UnitManager; + friend class base::BaseManager; friend class animation::AnimationManager; void AddFrontendRequest( const FrontendRequest& request ); diff --git a/src/game/backend/Random.cpp b/src/game/backend/Random.cpp new file mode 100644 index 00000000..f8747269 --- /dev/null +++ b/src/game/backend/Random.cpp @@ -0,0 +1,55 @@ +#include "Random.h" + +#include "game/backend/Game.h" +#include "game/backend/State.h" +#include "gse/type/Int.h" +#include "gse/type/Float.h" + +namespace game { +namespace backend { + +Random::Random( Game* game, const util::random::value_t seed ) + : util::random::Random( seed ) + , m_game( game ) { + // +} + +WRAPIMPL_BEGIN( Random, CLASS_RANDOM ) + WRAPIMPL_PROPS + { + "get_int", + NATIVE_CALL( this ) { + N_EXPECT_ARGS( 2 ); + N_GETVALUE( min, 0, Int ); + N_GETVALUE( max, 1, Int ); + if ( max < min ) { + GSE_ERROR( gse::EC.INVALID_CALL, "Maximum value is smaller than minimum ( " + std::to_string( max ) + " < " + std::to_string( min ) + " )" ); + } + if ( !m_game->GetState()->IsMaster() ) { + GSE_ERROR( gse::EC.INVALID_CALL, "Only master is allowed to generate random values" ); + } + return VALUE( gse::type::Int, m_game->GetRandom()->GetInt64( min, max ) ); + }) + }, + { + "get_float", + NATIVE_CALL( this ) { + N_EXPECT_ARGS( 2 ); + N_GETVALUE( min, 0, Float ); + N_GETVALUE( max, 1, Float ); + if ( max < min ) { + GSE_ERROR( gse::EC.INVALID_CALL, "Maximum value is smaller than minimum ( " + std::to_string( max ) + " < " + std::to_string( min ) + " )" ); + } + if ( !m_game->GetState()->IsMaster() ) { + GSE_ERROR( gse::EC.INVALID_CALL, "Only master is allowed to generate random values" ); + } + return VALUE( gse::type::Float, m_game->GetRandom()->GetFloat( min, max ) ); + }) + }, + }; +WRAPIMPL_END_PTR( Random ) + +UNWRAPIMPL_PTR( Random ) + +} +} diff --git a/src/game/backend/Random.h b/src/game/backend/Random.h new file mode 100644 index 00000000..09a780b7 --- /dev/null +++ b/src/game/backend/Random.h @@ -0,0 +1,24 @@ +#pragma once + +#include "util/random/Random.h" +#include "gse/Wrappable.h" + +#include "gse/type/Object.h" + +namespace game { +namespace backend { + +class Game; + +CLASS2( Random, util::random::Random, gse::Wrappable ) + + Random( Game* game, const util::random::value_t seed = 0 ); + + WRAPDEFS_PTR( Random ) + +private: + Game* m_game = nullptr; +}; + +} +} diff --git a/src/game/backend/base/Base.cpp b/src/game/backend/base/Base.cpp index db9ac5d3..94c9fc5d 100644 --- a/src/game/backend/base/Base.cpp +++ b/src/game/backend/base/Base.cpp @@ -12,10 +12,10 @@ #include "game/backend/slot/Slots.h" #include "game/backend/map/Map.h" #include "game/backend/faction/Faction.h" +#include "game/backend/base/BaseManager.h" #include "Pop.h" -#include "game/backend/bindings/Binding.h" #include "PopDef.h" -#include "util/random/Random.h" +#include "game/backend/Random.h" namespace game { namespace backend { @@ -55,7 +55,7 @@ Base::Base( void Base::AddPop( const Pop& pop ) { m_pops.push_back( pop ); - m_game->RefreshBase( this ); + m_game->GetBM()->RefreshBase( this ); } const types::Buffer Base::Serialize( const Base* base ) { @@ -105,7 +105,7 @@ WRAPIMPL_DYNAMIC_GETTERS( Base, CLASS_BASE ) N_EXPECT_ARGS( 1 ); N_GETVALUE( data, 0, Object ); N_GETPROP( poptype, data, "type", String ); - auto* def = m_game->GetPopDef( poptype ); + auto* def = m_game->GetBM()->GetPopDef( poptype ); if ( !def ) { GSE_ERROR( gse::EC.INVALID_DEFINITION, "Unknown pop type: " + poptype ); } diff --git a/src/game/backend/base/BaseManager.cpp b/src/game/backend/base/BaseManager.cpp new file mode 100644 index 00000000..f467768f --- /dev/null +++ b/src/game/backend/base/BaseManager.cpp @@ -0,0 +1,311 @@ +#include "BaseManager.h" + +#include "game/backend/Game.h" +#include "game/backend/State.h" +#include "game/backend/bindings/Bindings.h" +#include "game/backend/slot/Slots.h" +#include "game/backend/event/SpawnBase.h" +#include "game/backend/Player.h" +#include "game/backend/faction/Faction.h" + +#include "Base.h" +#include "PopDef.h" + +#include "gse/context/Context.h" +#include "gse/callable/Native.h" +#include "gse/type/Bool.h" +#include "gse/type/Float.h" +#include "gse/type/Array.h" + +namespace game { +namespace backend { +namespace base { + +BaseManager::BaseManager( Game* game ) + : m_game( game ) { + // +} + +BaseManager::~BaseManager() { + Clear(); +} + +void BaseManager::Clear() { + for ( auto& it : m_base_popdefs ) { + delete it.second; + } + m_base_popdefs.clear(); + for ( auto& it : m_bases ) { + delete it.second; + } + m_bases.clear(); + + m_base_updates.clear(); +} + +base::PopDef* BaseManager::GetPopDef( const std::string& id ) const { + const auto& it = m_base_popdefs.find( id ); + if ( it != m_base_popdefs.end() ) { + return it->second; + } + else { + return nullptr; + } +} + +base::Base* BaseManager::GetBase( const size_t id ) const { + const auto& it = m_bases.find( id ); + if ( it != m_bases.end() ) { + return it->second; + } + else { + return nullptr; + } +} + +void BaseManager::DefinePop( base::PopDef* pop_def ) { + Log( "Defining base pop ('" + pop_def->m_id + "')" ); + + ASSERT( m_base_popdefs.find( pop_def->m_id ) == m_base_popdefs.end(), "Base pop def '" + pop_def->m_id + "' already exists" ); + + m_base_popdefs.insert( + { + pop_def->m_id, + pop_def + } + ); + + auto fr = FrontendRequest( FrontendRequest::FR_BASE_POP_DEFINE ); + NEW( fr.data.base_pop_define.serialized_popdef, std::string, base::PopDef::Serialize( pop_def ).ToString() ); + m_game->AddFrontendRequest( fr ); +} + +void BaseManager::SpawnBase( base::Base* base ) { + if ( !m_game->IsRunning() ) { + m_unprocessed_bases.push_back( base ); + return; + } + + auto* tile = base->GetTile(); + + // validate and fix name if needed (or assign if empty) + std::vector< std::string > names_to_try = {}; + if ( base->m_name.empty() ) { + const auto& names = base->m_owner->GetPlayer()->GetFaction()->m_base_names; + names_to_try = tile->is_water_tile + ? names.water + : names.land; + } + else if ( m_registered_base_names.find( base->m_name ) != m_registered_base_names.end() ) { + names_to_try = { base->m_name }; + } + if ( !names_to_try.empty() ) { + size_t cycle = 0; + bool found = false; + while ( !found ) { + cycle++; + for ( const auto& name_to_try : names_to_try ) { + base->m_name = cycle == 1 + ? name_to_try + : name_to_try + " " + std::to_string( cycle ); + if ( m_registered_base_names.find( base->m_name ) == m_registered_base_names.end() ) { + found = true; + break; + } + } + } + } + m_registered_base_names.insert( base->m_name ); + + Log( "Spawning base #" + std::to_string( base->m_id ) + " ( " + base->m_name + " ) at " + base->GetTile()->ToString() ); + + ASSERT( m_bases.find( base->m_id ) == m_bases.end(), "duplicate base id" ); + m_bases.insert_or_assign( base->m_id, base ); + + QueueBaseUpdate( base, BUO_SPAWN ); + + auto* state = m_game->GetState(); + if ( state->IsMaster() ) { + state->m_bindings->Trigger( this, "base_spawn",{ + { + "base", + base->Wrap() + }, + }); + } + + RefreshBase( base ); +} + +const std::map< size_t, Base* >& BaseManager::GetBases() const { + return m_bases; +} + +void BaseManager::ProcessUnprocessed() { + for ( auto& it : m_unprocessed_bases ) { + SpawnBase( it ); + } + m_unprocessed_bases.clear(); +} + +void BaseManager::PushUpdates() { + if ( m_game->IsRunning() && !m_base_updates.empty() ) { + for ( const auto& it : m_base_updates ) { + const auto base_id = it.first; + const auto& bu = it.second; + const auto& base = bu.base; + if ( bu.ops & BUO_SPAWN ) { + auto fr = FrontendRequest( FrontendRequest::FR_BASE_SPAWN ); + fr.data.base_spawn.base_id = base->m_id; + fr.data.base_spawn.slot_index = base->m_owner->GetIndex(); + const auto* tile = base->GetTile(); + fr.data.base_spawn.tile_coords = { + tile->coord.x, + tile->coord.y + }; + const auto c = base->GetRenderCoords(); + fr.data.base_spawn.render_coords = { + c.x, + c.y, + c.z + }; + NEW( fr.data.base_spawn.name, std::string, base->m_name ); + m_game->AddFrontendRequest( fr ); + } + if ( bu.ops & BUO_REFRESH ) { + auto fr = FrontendRequest( FrontendRequest::FR_BASE_UPDATE ); + fr.data.base_update.base_id = base->m_id; + fr.data.base_update.slot_index = base->m_owner->GetIndex(); + NEW( fr.data.base_update.name, std::string, base->m_name ); + NEW( fr.data.base_update.faction_id, std::string, base->m_faction->m_id ); + NEW( fr.data.base_update.pops, FrontendRequest::base_pops_t, {} ); + for ( const auto& pop : base->m_pops ) { + fr.data.base_update.pops->push_back( + { + pop.m_def->m_id, + pop.m_variant + } + ); + } + m_game->AddFrontendRequest( fr ); + } + if ( bu.ops & BUO_DESPAWN ) { + THROW( "TODO: BASE DESPAWN" ); + } + } + m_base_updates.clear(); + } +} + +WRAPIMPL_BEGIN( BaseManager, CLASS_BM ) + WRAPIMPL_PROPS + { + "spawn_base", + NATIVE_CALL( this ) { + N_EXPECT_ARGS_MIN_MAX( 3, 4 ); + N_GETVALUE_UNWRAP( owner, 0, slot::Slot ); + N_GETVALUE_UNWRAP( tile, 1, map::tile::Tile ); + + N_GETVALUE( info, 2, Object ); + N_GETPROP_OPT( std::string, name, info, "name", String, "" ); + + if ( arguments.size() > 3 ) { + N_PERSIST_CALLABLE( on_spawn, 3 ); + } + + return m_game->AddEvent( new event::SpawnBase( + m_game->GetSlotNum(), + owner->GetIndex(), + tile->coord.x, + tile->coord.y, + name + ) ); + }) + }, + }; +WRAPIMPL_END_PTR( BaseManager ) + +UNWRAPIMPL_PTR( BaseManager ) + +void BaseManager::Serialize( types::Buffer& buf ) const { + + Log( "Serializing " + std::to_string( m_base_popdefs.size() ) + " base pop defs" ); + buf.WriteInt( m_base_popdefs.size() ); + for ( const auto& it : m_base_popdefs ) { + buf.WriteString( it.first ); + buf.WriteString( base::PopDef::Serialize( it.second ).ToString() ); + } + + Log( "Serializing " + std::to_string( m_bases.size() ) + " bases" ); + buf.WriteInt( m_bases.size() ); + for ( const auto& it : m_bases ) { + buf.WriteInt( it.first ); + buf.WriteString( base::Base::Serialize( it.second ).ToString() ); + } + buf.WriteInt( base::Base::GetNextId() ); + + Log( "Saved next base id: " + std::to_string( base::Base::GetNextId() ) ); +} + +void BaseManager::Unserialize( types::Buffer& buf ) { + ASSERT( m_base_popdefs.empty(), "base pop defs not empty" ); + ASSERT( m_bases.empty(), "bases not empty" ); + ASSERT( m_unprocessed_bases.empty(), "unprocessed bases not empty" ); + + size_t sz = buf.ReadInt(); + m_base_popdefs.reserve( sz ); + Log( "Unserializing " + std::to_string( sz ) + " base pop defs" ); + for ( size_t i = 0 ; i < sz ; i++ ) { + const auto name = buf.ReadString(); + auto b = types::Buffer( buf.ReadString() ); + DefinePop( base::PopDef::Unserialize( b ) ); + } + + sz = buf.ReadInt(); + Log( "Unserializing " + std::to_string( sz ) + " bases" ); + if ( !m_game->IsRunning() ) { + m_unprocessed_bases.reserve( sz ); + } + for ( size_t i = 0 ; i < sz ; i++ ) { + const auto base_id = buf.ReadInt(); + auto b = types::Buffer( buf.ReadString() ); + SpawnBase( base::Base::Unserialize( b, m_game ) ); + } + + base::Base::SetNextId( buf.ReadInt() ); + Log( "Restored next base id: " + std::to_string( base::Base::GetNextId() ) ); +} + +void BaseManager::RefreshBase( const base::Base* base ) { + QueueBaseUpdate( base, BUO_REFRESH ); +} + +void BaseManager::QueueBaseUpdate( const Base* base, const base_update_op_t op ) { + auto it = m_base_updates.find( base->m_id ); + if ( it == m_base_updates.end() ) { + it = m_base_updates.insert( + { + base->m_id, + { + {}, + base, + } + } + ).first; + } + auto& update = it->second; + if ( op == BUO_DESPAWN ) { + if ( op & BUO_SPAWN ) { + // if base is despawned immediately after spawning - frontend doesn't need to know + m_base_updates.erase( it ); + return; + } + update.ops = BUO_NONE; // clear other actions if base was despawned + } + // add to operations list + update.ops = (base_update_op_t)( (uint8_t)update.ops | (uint8_t)op ); +} + +} +} +} diff --git a/src/game/backend/base/BaseManager.h b/src/game/backend/base/BaseManager.h new file mode 100644 index 00000000..ebb3e45c --- /dev/null +++ b/src/game/backend/base/BaseManager.h @@ -0,0 +1,84 @@ +#pragma once + +#include "common/Common.h" + +#include "gse/Wrappable.h" +#include "gse/type/Object.h" + +#include "Types.h" + +namespace game { +namespace backend { + +class Game; + +namespace map { +class Map; +namespace tile { +class Tile; +} +} + +namespace slot { +class Slot; +} + +namespace base { + +class Base; +class PopDef; + +CLASS2( BaseManager, common::Class, gse::Wrappable ) +public: + BaseManager( Game* game ); + ~BaseManager(); + + void Clear(); + + PopDef* GetPopDef( const std::string& id ) const; + Base* GetBase( const size_t id ) const; + void DefinePop( base::PopDef* pop_def ); + void SpawnBase( base::Base* base ); + + const std::map< size_t, Base* >& GetBases() const; + + void ProcessUnprocessed(); + void PushUpdates(); + + WRAPDEFS_PTR( BaseManager ) + + void Serialize( types::Buffer& buf ) const; + void Unserialize( types::Buffer& buf ); + + void RefreshBase( const base::Base* base ); + +private: + Game* m_game = nullptr; + + std::unordered_map< std::string, base::PopDef* > m_base_popdefs = {}; + std::map< size_t, base::Base* > m_bases = {}; + std::vector< base::Base* > m_unprocessed_bases = {}; + + std::unordered_set< std::string > m_registered_base_names = {}; + + enum base_update_op_t : uint8_t { + BUO_NONE = 0, + BUO_SPAWN = 1 << 0, + BUO_REFRESH = 1 << 1, + BUO_DESPAWN = 1 << 2, + }; + struct base_update_t { + base_update_op_t ops = BUO_NONE; + const base::Base* base = nullptr; + }; + std::unordered_map< size_t, base_update_t > m_base_updates = {}; + + void QueueBaseUpdate( const base::Base* base, const base_update_op_t op ); + +private: + friend class Base; +}; + +} +} +} diff --git a/src/game/backend/base/CMakeLists.txt b/src/game/backend/base/CMakeLists.txt index e5d9c85a..2b2321e1 100644 --- a/src/game/backend/base/CMakeLists.txt +++ b/src/game/backend/base/CMakeLists.txt @@ -1,5 +1,6 @@ SET( SRC ${SRC} + ${PWD}/BaseManager.cpp ${PWD}/Base.cpp ${PWD}/PopDef.cpp ${PWD}/Pop.cpp diff --git a/src/game/backend/base/Pop.cpp b/src/game/backend/base/Pop.cpp index 15d8d5dd..c7a5b532 100644 --- a/src/game/backend/base/Pop.cpp +++ b/src/game/backend/base/Pop.cpp @@ -3,6 +3,7 @@ #include "game/backend/Game.h" #include "game/backend/base/Base.h" #include "game/backend/base/PopDef.h" +#include "game/backend/base/BaseManager.h" #include "gse/type/String.h" #include "gse/type/Int.h" #include "gse/type/Undefined.h" @@ -32,9 +33,9 @@ void Pop::Unserialize( types::Buffer& buf, Game* game ) { ASSERT_NOLOG( !m_base, "pop base is not null" ); ASSERT_NOLOG( !m_def, "pop def is not null" ); - m_base = game->GetBase( buf.ReadInt() ); + m_base = game->GetBM()->GetBase( buf.ReadInt() ); ASSERT_NOLOG( m_base, "base not found" ); - m_def = game->GetPopDef( buf.ReadString() ); + m_def = game->GetBM()->GetPopDef( buf.ReadString() ); ASSERT_NOLOG( m_def, "pop def not found" ); m_variant = buf.ReadInt(); } diff --git a/src/game/backend/bindings/Bindings.cpp b/src/game/backend/bindings/Bindings.cpp index 26e64e4d..5470236d 100644 --- a/src/game/backend/bindings/Bindings.cpp +++ b/src/game/backend/bindings/Bindings.cpp @@ -6,6 +6,7 @@ #include "game/backend/Game.h" #include "game/backend/unit/UnitManager.h" +#include "game/backend/base/BaseManager.h" #include "Binding.h" #include "gse/GSE.h" #include "gse/context/GlobalContext.h" @@ -91,7 +92,8 @@ gse::Value Bindings::Call( const callback_slot_t slot, const callback_arguments_ ); auto* game = m_state->GetGame(); if ( game ) { - game->GetUM()->PushUnitUpdates(); + game->GetUM()->PushUpdates(); + game->GetBM()->PushUpdates(); } if ( result.Get()->type == gse::type::Type::T_NOTHING ) { // return undefined by default @@ -115,6 +117,11 @@ const gse::Value Bindings::Trigger( gse::Wrappable* object, const std::string& e auto result = VALUE( gse::type::Undefined ); try { result = object->Trigger( m_gse_context, m_si_internal, event, args ); + auto* game = m_state->GetGame(); + if ( game ) { + game->GetUM()->PushUpdates(); + game->GetBM()->PushUpdates(); + } if ( result.Get()->type == gse::type::Type::T_NOTHING ) { // return undefined by default return VALUE( gse::type::Undefined ); diff --git a/src/game/backend/bindings/Map.cpp b/src/game/backend/bindings/Map.cpp index 84a4060b..654fbb92 100644 --- a/src/game/backend/bindings/Map.cpp +++ b/src/game/backend/bindings/Map.cpp @@ -54,7 +54,7 @@ BINDING_IMPL( map ) { GSE_ERROR( gse::EC.INVALID_CALL, "X and Y oddity differs ( " + std::to_string( x ) + " % 2 != " + std::to_string( y ) + " % 2 )" ); } return m->GetTile( x, y )->Wrap(); - }), + } ) } }; diff --git a/src/game/backend/bindings/Random.cpp b/src/game/backend/bindings/Random.cpp index c49b49c3..04410d14 100644 --- a/src/game/backend/bindings/Random.cpp +++ b/src/game/backend/bindings/Random.cpp @@ -6,7 +6,7 @@ #include "gse/type/Int.h" #include "gse/type/Float.h" #include "gse/type/Undefined.h" -#include "util/random/Random.h" +#include "game/backend/Random.h" #include "Binding.h" diff --git a/src/game/backend/bindings/Tiles.cpp b/src/game/backend/bindings/Tiles.cpp index 2fabdd03..463205ad 100644 --- a/src/game/backend/bindings/Tiles.cpp +++ b/src/game/backend/bindings/Tiles.cpp @@ -40,7 +40,7 @@ BINDING_IMPL( tiles ) { ) -> gse::Value { GAME->GetTM()->SendTileUnlockRequest( tile_positions ); return VALUE( gse::type::Undefined ); - }), + } ) }); N_UNPERSIST_CALLABLE( on_complete ); }); diff --git a/src/game/backend/event/DefinePop.cpp b/src/game/backend/event/DefinePop.cpp index cb903f9f..bcd118cb 100644 --- a/src/game/backend/event/DefinePop.cpp +++ b/src/game/backend/event/DefinePop.cpp @@ -1,8 +1,8 @@ #include "DefinePop.h" #include "game/backend/Game.h" - #include "game/backend/base/PopDef.h" +#include "game/backend/base/BaseManager.h" #include "gse/type/Undefined.h" namespace game { @@ -23,7 +23,7 @@ const std::string* DefinePop::Validate( Game* game ) const { } const gse::Value DefinePop::Apply( Game* game ) const { - game->DefinePop( m_pop_def ); + game->GetBM()->DefinePop( m_pop_def ); return VALUE( gse::type::Undefined ); } diff --git a/src/game/backend/event/SpawnBase.cpp b/src/game/backend/event/SpawnBase.cpp index 13878205..5f857a91 100644 --- a/src/game/backend/event/SpawnBase.cpp +++ b/src/game/backend/event/SpawnBase.cpp @@ -8,6 +8,7 @@ #include "game/backend/unit/Unit.h" #include "game/backend/base/Base.h" #include "game/backend/base/Pop.h" +#include "game/backend/base/BaseManager.h" #include "gse/type/Undefined.h" #include "game/backend/Player.h" @@ -51,7 +52,7 @@ const gse::Value SpawnBase::Apply( Game* game ) const { m_name, {} // will be added later ); - game->SpawnBase( base ); + game->GetBM()->SpawnBase( base ); return base->Wrap(); } diff --git a/src/game/backend/map/Map.cpp b/src/game/backend/map/Map.cpp index 65cc8724..f0e5f5e3 100644 --- a/src/game/backend/map/Map.cpp +++ b/src/game/backend/map/Map.cpp @@ -5,7 +5,7 @@ #include "game/backend/map/generator/SimplePerlin.h" #include "engine/Engine.h" #include "config/Config.h" -#include "util/random/Random.h" +#include "game/backend/Random.h" #include "util/FS.h" #include "ui/UI.h" #include "loader/texture/TextureLoader.h" @@ -513,7 +513,7 @@ const Map::tile_texture_info_t Map::GetTileTextureInfo( const texture_variants_t return info; } -util::random::Random* Map::GetRandom() const { +Random* Map::GetRandom() const { return m_game->GetRandom(); } diff --git a/src/game/backend/map/Map.h b/src/game/backend/map/Map.h index d2021d03..40564f0c 100644 --- a/src/game/backend/map/Map.h +++ b/src/game/backend/map/Map.h @@ -24,9 +24,6 @@ class Data; } namespace util { -namespace random { -class Random; -} class Perlin; } @@ -34,6 +31,7 @@ namespace game { namespace backend { class Game; +class Random; namespace settings { class MapSettings; @@ -113,7 +111,7 @@ CLASS( Map, types::Serializable ) void SetTexture( const tile::tile_layer_type_t tile_layer, types::texture::Texture* src_texture, const types::texture::add_flag_t mode, const uint8_t rotate = 0, const float alpha = 1.0f ); const tile_texture_info_t GetTileTextureInfo( const texture_variants_type_t type, const tile::Tile* tile, const tile_grouping_criteria_t criteria, const tile::feature_t feature = tile::FEATURE_NONE ) const; - util::random::Random* GetRandom() const; + Random* GetRandom() const; const size_t GetWidth() const; const size_t GetHeight() const; diff --git a/src/game/backend/map/module/CalculateCoords.cpp b/src/game/backend/map/module/CalculateCoords.cpp index b1ceda1b..68a5176c 100644 --- a/src/game/backend/map/module/CalculateCoords.cpp +++ b/src/game/backend/map/module/CalculateCoords.cpp @@ -3,7 +3,7 @@ #include "game/backend/map/Map.h" #include "game/backend/map/Consts.h" #include "game/backend/map/tile/TileState.h" -#include "util/random/Random.h" +#include "game/backend/Random.h" namespace game { namespace backend { diff --git a/src/game/backend/map/module/Coastlines1.cpp b/src/game/backend/map/module/Coastlines1.cpp index 71b1d448..e4d6d762 100644 --- a/src/game/backend/map/module/Coastlines1.cpp +++ b/src/game/backend/map/module/Coastlines1.cpp @@ -5,7 +5,7 @@ #include "game/backend/map/Consts.h" #include "game/backend/map/tile/Tile.h" #include "game/backend/map/tile/TileState.h" -#include "util/random/Random.h" +#include "game/backend/Random.h" #include "util/Perlin.h" namespace game { diff --git a/src/game/backend/map/module/Coastlines2.cpp b/src/game/backend/map/module/Coastlines2.cpp index 69063849..7a23991e 100644 --- a/src/game/backend/map/module/Coastlines2.cpp +++ b/src/game/backend/map/module/Coastlines2.cpp @@ -5,7 +5,7 @@ #include "game/backend/map/Consts.h" #include "game/backend/map/tile/Tile.h" #include "game/backend/map/tile/TileState.h" -#include "util/random/Random.h" +#include "game/backend/Random.h" #include "util/Perlin.h" namespace game { diff --git a/src/game/backend/map/module/Finalize.cpp b/src/game/backend/map/module/Finalize.cpp index 02aca0b8..d895d3a7 100644 --- a/src/game/backend/map/module/Finalize.cpp +++ b/src/game/backend/map/module/Finalize.cpp @@ -5,7 +5,7 @@ #include "game/backend/map/Consts.h" #include "game/backend/map/tile/Tile.h" #include "game/backend/map/tile/TileState.h" -#include "util/random/Random.h" +#include "game/backend/Random.h" #include "types/mesh/Render.h" #include "types/mesh/Data.h" diff --git a/src/game/backend/map/module/LandSurface.cpp b/src/game/backend/map/module/LandSurface.cpp index 12af70a7..69522b9b 100644 --- a/src/game/backend/map/module/LandSurface.cpp +++ b/src/game/backend/map/module/LandSurface.cpp @@ -5,7 +5,7 @@ #include "game/backend/map/Consts.h" #include "game/backend/map/tile/Tile.h" #include "game/backend/map/tile/TileState.h" -#include "util/random/Random.h" +#include "game/backend/Random.h" namespace game { namespace backend { diff --git a/src/game/backend/map/module/Module.cpp b/src/game/backend/map/module/Module.cpp index 80f8a207..8cb9d461 100644 --- a/src/game/backend/map/module/Module.cpp +++ b/src/game/backend/map/module/Module.cpp @@ -1,6 +1,6 @@ #include "Module.h" -#include "util/random/Random.h" +#include "game/backend/Random.h" #include "game/backend/map/Map.h" namespace game { diff --git a/src/game/backend/map/module/Sprites.cpp b/src/game/backend/map/module/Sprites.cpp index a237b06a..522cd1f2 100644 --- a/src/game/backend/map/module/Sprites.cpp +++ b/src/game/backend/map/module/Sprites.cpp @@ -7,7 +7,7 @@ #include "game/backend/map/Consts.h" #include "game/backend/map/tile/Tile.h" #include "game/backend/map/tile/TileState.h" -#include "util/random/Random.h" +#include "game/backend/Random.h" namespace game { namespace backend { diff --git a/src/game/backend/map/tile/TileManager.cpp b/src/game/backend/map/tile/TileManager.cpp index c2718dfe..432555d3 100644 --- a/src/game/backend/map/tile/TileManager.cpp +++ b/src/game/backend/map/tile/TileManager.cpp @@ -168,6 +168,47 @@ void TileManager::ReleaseTileLocks( const size_t initiator_slot ) { WRAPIMPL_BEGIN( TileManager, CLASS_TM ) WRAPIMPL_PROPS + { + "get_map_width", + NATIVE_CALL( this ) { + const auto* m = m_game->GetMap(); + return VALUE( gse::type::Int, m->GetWidth() ); + } ) + }, + { + "get_map_height", + NATIVE_CALL( this ) { + const auto* m = m_game->GetMap(); + return VALUE( gse::type::Int, m->GetHeight() ); + }) + }, + { + "get_tile", + NATIVE_CALL( this ) { + N_EXPECT_ARGS( 2 ); + N_GETVALUE( x, 0, Int ); + N_GETVALUE( y, 1, Int ); + const auto* m = m_game->GetMap(); + const auto w = m->GetWidth(); + const auto h = m->GetHeight(); + if ( x >= w ) { + GSE_ERROR( gse::EC.INVALID_CALL, "X coordinate exceeds map width ( " + std::to_string( x ) + " >= " + std::to_string( w ) + " )" ); + } + if ( y >= h ) { + GSE_ERROR( gse::EC.INVALID_CALL, "Y coordinate exceeds map height ( " + std::to_string( y ) + " >= " + std::to_string( h ) + " )" ); + } + if ( x < 0 ) { + GSE_ERROR( gse::EC.INVALID_CALL, "X coordinate can't be negative ( " + std::to_string( x ) + " < 0" ); + } + if ( y < 0 ) { + GSE_ERROR( gse::EC.INVALID_CALL, "Y coordinate can't be negative ( " + std::to_string( y ) + " < 0" ); + } + if ( x % 2 != y % 2 ) { + GSE_ERROR( gse::EC.INVALID_CALL, "X and Y oddity differs ( " + std::to_string( x ) + " % 2 != " + std::to_string( y ) + " % 2 )" ); + } + return m->GetTile( x, y )->Wrap(); + } ) + }, { "lock_tiles", NATIVE_CALL( this ) { @@ -189,13 +230,12 @@ WRAPIMPL_BEGIN( TileManager, CLASS_TM ) ) -> gse::Value { SendTileUnlockRequest( tile_positions ); return VALUE( gse::type::Undefined ); - }), + } ) }); N_UNPERSIST_CALLABLE( on_complete ); }); return VALUE( gse::type::Undefined ); }) - }, }; WRAPIMPL_END_PTR( TileManager ) diff --git a/src/game/backend/map_editor/tool/Elevations.cpp b/src/game/backend/map_editor/tool/Elevations.cpp index 872dccbd..8c8a2544 100644 --- a/src/game/backend/map_editor/tool/Elevations.cpp +++ b/src/game/backend/map_editor/tool/Elevations.cpp @@ -3,7 +3,7 @@ #include "game/backend/Game.h" #include "game/backend/map/Map.h" #include "game/backend/map/Consts.h" -#include "util/random/Random.h" +#include "game/backend/Random.h" namespace game { namespace backend { diff --git a/src/game/backend/unit/UnitManager.cpp b/src/game/backend/unit/UnitManager.cpp index 6edcdafc..346fcc55 100644 --- a/src/game/backend/unit/UnitManager.cpp +++ b/src/game/backend/unit/UnitManager.cpp @@ -2,6 +2,7 @@ #include "MoraleSet.h" #include "Def.h" +#include "StaticDef.h" #include "Unit.h" #include "game/backend/Game.h" @@ -9,6 +10,7 @@ #include "game/backend/State.h" #include "game/backend/bindings/Bindings.h" #include "game/backend/slot/Slots.h" +#include "game/backend/event/SpawnUnit.h" #include "game/backend/event/DespawnUnit.h" #include "gse/context/Context.h" @@ -36,15 +38,15 @@ void UnitManager::Clear() { } m_unprocessed_units.clear(); - for ( auto& it : m_unit_moralesets ) { + for ( auto& it : m_units ) { delete it.second; } - m_unit_moralesets.clear(); + m_units.clear(); - for ( auto& it : m_units ) { + for ( auto& it : m_unit_moralesets ) { delete it.second; } - m_units.clear(); + m_unit_moralesets.clear(); for ( auto& it : m_unit_defs ) { delete it.second; @@ -54,7 +56,7 @@ void UnitManager::Clear() { m_unit_updates.clear(); } -void UnitManager::DefineMoraleSet( unit::MoraleSet* moraleset ) { +void UnitManager::DefineMoraleSet( MoraleSet* moraleset ) { Log( "Defining unit moraleset ('" + moraleset->m_id + "')" ); ASSERT( m_unit_moralesets.find( moraleset->m_id ) == m_unit_moralesets.end(), "Unit moraleset '" + moraleset->m_id + "' already exists" ); @@ -67,7 +69,7 @@ void UnitManager::DefineMoraleSet( unit::MoraleSet* moraleset ) { ); } -void UnitManager::DefineUnit( unit::Def* def ) { +void UnitManager::DefineUnit( Def* def ) { Log( "Defining unit ('" + def->m_id + "')" ); ASSERT( m_unit_defs.find( def->m_id ) == m_unit_defs.end(), "Unit definition '" + def->m_id + "' already exists" ); @@ -80,7 +82,7 @@ void UnitManager::DefineUnit( unit::Def* def ) { ); auto fr = FrontendRequest( FrontendRequest::FR_UNIT_DEFINE ); - NEW( fr.data.unit_define.serialized_unitdef, std::string, unit::Def::Serialize( def ).ToString() ); + NEW( fr.data.unit_define.serialized_unitdef, std::string, Def::Serialize( def ).ToString() ); m_game->AddFrontendRequest( fr ); } @@ -101,14 +103,12 @@ void UnitManager::SpawnUnit( Unit* unit ) { auto* state = m_game->GetState(); if ( state->IsMaster() ) { - state->m_bindings->Call( - bindings::Bindings::CS_ON_UNIT_SPAWN, { - { - "unit", - unit->Wrap() - }, - } - ); + state->m_bindings->Trigger( this, "unit_spawn",{ + { + "unit", + unit->Wrap() + }, + }); } } @@ -132,14 +132,12 @@ void UnitManager::DespawnUnit( const size_t unit_id ) { auto* state = m_game->GetState(); if ( state->IsMaster() ) { - state->m_bindings->Call( - bindings::Bindings::CS_ON_UNIT_DESPAWN, { - { - "unit", - unit->Wrap() - } + state->m_bindings->Trigger( this, "unit_despawn", { + { + "unit", + unit->Wrap() } - ); + }); } delete unit; @@ -157,7 +155,7 @@ void UnitManager::SkipUnitTurn( const size_t unit_id ) { RefreshUnit( unit ); } -unit::MoraleSet* UnitManager::GetMoraleSet( const std::string& name ) const { +MoraleSet* UnitManager::GetMoraleSet( const std::string& name ) const { const auto& it = m_unit_moralesets.find( name ); if ( it != m_unit_moralesets.end() ) { return it->second; @@ -165,7 +163,7 @@ unit::MoraleSet* UnitManager::GetMoraleSet( const std::string& name ) const { return nullptr; } -unit::Unit* UnitManager::GetUnit( const size_t id ) const { +Unit* UnitManager::GetUnit( const size_t id ) const { const auto& it = m_units.find( id ); if ( it != m_units.end() ) { return it->second; @@ -173,7 +171,7 @@ unit::Unit* UnitManager::GetUnit( const size_t id ) const { return nullptr; } -unit::Def* UnitManager::GetUnitDef( const std::string& name ) const { +Def* UnitManager::GetUnitDef( const std::string& name ) const { const auto& it = m_unit_defs.find( name ); if ( it != m_unit_defs.end() ) { return it->second; @@ -195,7 +193,7 @@ void UnitManager::ProcessUnprocessed() { m_unprocessed_units.clear(); } -void UnitManager::PushUnitUpdates() { +void UnitManager::PushUpdates() { if ( m_game->IsRunning() && !m_unit_updates.empty() ) { for ( const auto& it : m_unit_updates ) { const auto unit_id = it.first; @@ -255,88 +253,34 @@ void UnitManager::PushUnitUpdates() { WRAPIMPL_BEGIN( UnitManager, CLASS_UM ) WRAPIMPL_PROPS -/* { - "add", + { + "spawn_unit", NATIVE_CALL( this ) { - N_EXPECT_ARGS( 2 ); - N_GETVALUE( id, 0, String ); - - N_GETVALUE( faction_def, 1, Object ); - N_GETPROP( name, faction_def, "name", String ); - - auto* faction = new Faction( id, name ); - - N_GETPROP( colors, faction_def, "colors", Object ); - N_GETPROP_UNWRAP( colors_text, colors, "text", types::Color ); - N_GETPROP_UNWRAP( colors_text_shadow, colors, "text_shadow", types::Color ); - N_GETPROP_UNWRAP( colors_border, colors, "border", types::Color ); - faction->m_colors = { - colors_text, - colors_text_shadow, - colors_border - }; - - N_GETPROP_OPT_BOOL( is_naval, faction_def, "is_naval") - if ( is_naval ) { - faction->m_flags |= Faction::FF_NAVAL; - } - N_GETPROP_OPT_BOOL( is_progenitor, faction_def, "is_progenitor") - if ( is_progenitor ) { - faction->m_flags |= Faction::FF_PROGENITOR; - } - - N_GETPROP( bases_def, faction_def, "bases", Object ); - N_GETPROP( bases_render_def, bases_def, "render", Object ); - - N_GETPROP( bases_render_type, bases_render_def, "type", String ); - if ( bases_render_type == "sprite_grid" ) { - N_GETPROP( file, bases_render_def, "file", String ); - N_GETPROP( grid_x, bases_render_def, "grid_x", Int ); - N_GETPROP( grid_y, bases_render_def, "grid_y", Int ); - N_GETPROP( cell_width, bases_render_def, "cell_width", Int ); - N_GETPROP( cell_height, bases_render_def, "cell_height", Int ); - N_GETPROP_OPT( size_t, cell_cx, bases_render_def, "cell_cx", Int, cell_width / 2 ); - N_GETPROP_OPT( size_t, cell_cy, bases_render_def, "cell_cy", Int, cell_height / 2 ); - N_GETPROP( cell_padding, bases_render_def, "cell_padding", Int ); - N_GETPROP_OPT( float, scale_x, bases_render_def, "scale_x", Float, 1.0f ); - N_GETPROP_OPT( float, scale_y, bases_render_def, "scale_y", Float, 1.0f ); - faction->m_bases_render = { - file, - (size_t)grid_x, - (size_t)grid_y, - (size_t)cell_width, - (size_t)cell_height, - cell_cx, - cell_cy, - (size_t)cell_padding, - scale_x, - scale_y - }; - } - else { - delete faction; - GSE_ERROR( gse::EC.GAME_ERROR, "Unsupported bases render type: " + bases_render_type ); - } - - N_GETPROP( base_names, bases_def, "names", Object ); - N_GETPROP( base_names_land, base_names, "land", Array ); - faction->m_base_names.land.reserve( base_names_land.size() ); - for ( size_t i = 0 ; i < base_names_land.size() ; i++ ) { - N_GETELEMENT( v, base_names_land, i, String ); - faction->m_base_names.land.push_back( v ); - } - - N_GETPROP( base_names_water, base_names, "water", Array ); - faction->m_base_names.water.reserve( base_names_water.size() ); - for ( size_t i = 0 ; i < base_names_water.size() ; i++ ) { - N_GETELEMENT( v, base_names_water, i, String ); - faction->m_base_names.water.push_back( v ); - } - - Add( faction ); - return faction->Wrap(); - } ) - },*/ + N_EXPECT_ARGS( 5 ); + N_GETVALUE( def_name, 0, String ); + N_GETVALUE_UNWRAP( owner, 1, slot::Slot ); + N_GETVALUE_UNWRAP( tile, 2, map::tile::Tile ); + N_GETVALUE( morale, 3, Int ); + N_GETVALUE( health, 4, Float ); + return m_game->AddEvent( new event::SpawnUnit( + m_game->GetSlotNum(), + def_name, + owner->GetIndex(), + tile->coord.x, + tile->coord.y, + GetMorale( ctx, call_si, morale ), + GetHealth( ctx, call_si, health ) + ) ); + }) + }, + { + "despawn_unit", + NATIVE_CALL( this ) { + N_EXPECT_ARGS( 1 ); + N_GETVALUE_UNWRAP( unit, 0, Unit ); + return m_game->AddEvent( new event::DespawnUnit( m_game->GetSlotNum(), unit->m_id ) ); + }) + }, }; WRAPIMPL_END_PTR( UnitManager ) @@ -381,7 +325,7 @@ void UnitManager::Unserialize( types::Buffer& buf ) { for ( size_t i = 0 ; i < sz ; i++ ) { const auto name = buf.ReadString(); auto b = types::Buffer( buf.ReadString() ); - DefineMoraleSet( unit::MoraleSet::Unserialize( b ) ); + DefineMoraleSet( MoraleSet::Unserialize( b ) ); } sz = buf.ReadInt(); @@ -390,7 +334,7 @@ void UnitManager::Unserialize( types::Buffer& buf ) { for ( size_t i = 0 ; i < sz ; i++ ) { const auto name = buf.ReadString(); auto b = types::Buffer( buf.ReadString() ); - DefineUnit( unit::Def::Unserialize( b ) ); + DefineUnit( Def::Unserialize( b ) ); } sz = buf.ReadInt(); @@ -434,6 +378,23 @@ void UnitManager::QueueUnitUpdate( const Unit* unit, const unit_update_op_t op ) update.ops = (unit_update_op_t)( (uint8_t)update.ops | (uint8_t)op ); } +const morale_t UnitManager::GetMorale( GSE_CALLABLE, const int64_t& morale ) { + if ( morale < MORALE_MIN || morale > MORALE_MAX ) { + GSE_ERROR( gse::EC.INVALID_CALL, "Invalid morale value: " + std::to_string( morale ) + " (should be between " + std::to_string( MORALE_MIN ) + " and " + std::to_string( MORALE_MAX ) + ", inclusive)" ); + } + return (morale_t)morale; +} + +const health_t UnitManager::GetHealth( GSE_CALLABLE, const float health ) { + if ( health < Unit::MINIMUM_HEALTH_TO_KEEP || health > StaticDef::HEALTH_MAX ) { + GSE_ERROR( gse::EC.INVALID_CALL, "Invalid health value: " + std::to_string( health ) + " (should be between " + std::to_string( Unit::MINIMUM_HEALTH_TO_KEEP ) + " and " + std::to_string( StaticDef::HEALTH_MAX ) + ", inclusive)" ); + } + if ( health == 0 ) { + GSE_ERROR( gse::EC.INVALID_CALL, "Invalid health value: " + std::to_string( health ) + " (you can't spawn a dead unit)" ); + } + return (health_t)health; +} + const std::string* UnitManager::MoveUnitValidate( Unit* unit, map::tile::Tile* dst_tile ) { const auto result = m_game->GetState()->m_bindings->Trigger( this, "unit_move_validate", { { @@ -607,7 +568,7 @@ void UnitManager::AttackUnitApply( Unit* attacker, Unit* defender, const gse::Va } } -void UnitManager::RefreshUnit( const unit::Unit* unit ) { +void UnitManager::RefreshUnit( const Unit* unit ) { if ( unit->m_health <= 0.0f ) { if ( m_game->GetState()->IsMaster() ) { m_game->AddEvent( new event::DespawnUnit( m_game->GetSlotNum(), unit->m_id ) ); diff --git a/src/game/backend/unit/UnitManager.h b/src/game/backend/unit/UnitManager.h index bc137e27..6600121c 100644 --- a/src/game/backend/unit/UnitManager.h +++ b/src/game/backend/unit/UnitManager.h @@ -5,6 +5,8 @@ #include "gse/Wrappable.h" #include "gse/type/Object.h" +#include "Types.h" + namespace game { namespace backend { @@ -33,19 +35,19 @@ CLASS2( UnitManager, common::Class, gse::Wrappable ) ~UnitManager(); void Clear(); - void DefineMoraleSet( unit::MoraleSet* moraleset ); - void DefineUnit( unit::Def* def ); + void DefineMoraleSet( MoraleSet* moraleset ); + void DefineUnit( Def* def ); void SpawnUnit( Unit* unit ); void DespawnUnit( const size_t unit_id ); void SkipUnitTurn( const size_t unit_id ); - unit::MoraleSet* GetMoraleSet( const std::string& name ) const; - unit::Unit* GetUnit( const size_t id ) const; - unit::Def* GetUnitDef( const std::string& name ) const; + MoraleSet* GetMoraleSet( const std::string& name ) const; + Unit* GetUnit( const size_t id ) const; + Def* GetUnitDef( const std::string& name ) const; const std::map< size_t, Unit* >& GetUnits() const; void ProcessUnprocessed(); - void PushUnitUpdates(); + void PushUpdates(); WRAPDEFS_PTR( UnitManager ) @@ -85,6 +87,9 @@ CLASS2( UnitManager, common::Class, gse::Wrappable ) std::unordered_map< size_t, unit_update_t > m_unit_updates = {}; void QueueUnitUpdate( const Unit* unit, const unit_update_op_t op ); + const morale_t GetMorale( GSE_CALLABLE, const int64_t& morale ); + const health_t GetHealth( GSE_CALLABLE, const float health ); + private: friend class Unit; map::Map* GetMap() const; diff --git a/src/gse/type/Object.h b/src/gse/type/Object.h index 8d65dff4..54cbc515 100644 --- a/src/gse/type/Object.h +++ b/src/gse/type/Object.h @@ -27,10 +27,12 @@ class Object : public Type { enum object_class_t { CLASS_NONE, CLASS_EXCEPTION, + CLASS_RANDOM, CLASS_COLOR, CLASS_SYSTEM, CLASS_STATE, CLASS_GAME, + CLASS_MAP, CLASS_TM, CLASS_TILE, CLASS_PLAYER, @@ -39,6 +41,7 @@ class Object : public Type { CLASS_UM, CLASS_UNITDEF, CLASS_UNIT, + CLASS_BM, CLASS_BASE, CLASS_BASE_POP, CLASS_AM, From 95921fa502d12c892a86b084d29a1f83d43168e1 Mon Sep 17 00:00:00 2001 From: afwbkbc Date: Sat, 16 Nov 2024 16:06:22 +0200 Subject: [PATCH 07/13] ResourceManager --- GLSMAC_data/default/main.gls.js | 2 +- GLSMAC_data/default/resources.gls.js | 12 +- src/game/backend/CMakeLists.txt | 2 +- src/game/backend/Game.cpp | 117 ++--------- src/game/backend/Game.h | 16 +- src/game/backend/bindings/Resources.cpp | 4 +- src/game/backend/event/DefineResource.cpp | 11 +- src/game/backend/event/DefineResource.h | 6 +- src/game/backend/faction/FactionManager.cpp | 2 +- src/game/backend/map/Map.cpp | 4 +- src/game/backend/resource/CMakeLists.txt | 6 + src/game/backend/{ => resource}/Resource.cpp | 2 + src/game/backend/{ => resource}/Resource.h | 2 + src/game/backend/resource/ResourceManager.cpp | 190 ++++++++++++++++++ src/game/backend/resource/ResourceManager.h | 53 +++++ src/gse/type/Object.cpp | 2 +- src/gse/type/Object.h | 3 +- 17 files changed, 308 insertions(+), 126 deletions(-) create mode 100644 src/game/backend/resource/CMakeLists.txt rename src/game/backend/{ => resource}/Resource.cpp (99%) rename src/game/backend/{ => resource}/Resource.h (97%) create mode 100644 src/game/backend/resource/ResourceManager.cpp create mode 100644 src/game/backend/resource/ResourceManager.h diff --git a/GLSMAC_data/default/main.gls.js b/GLSMAC_data/default/main.gls.js index af9befd1..fbec6879 100644 --- a/GLSMAC_data/default/main.gls.js +++ b/GLSMAC_data/default/main.gls.js @@ -52,7 +52,7 @@ const bases = #include('bases'); e.game.on('start', (e) => { // init game data - resources.define(); + resources.define(e.game.rm); map.define(); units.define(); bases.define(); diff --git a/GLSMAC_data/default/resources.gls.js b/GLSMAC_data/default/resources.gls.js index c83f4680..8fb06e91 100644 --- a/GLSMAC_data/default/resources.gls.js +++ b/GLSMAC_data/default/resources.gls.js @@ -1,6 +1,6 @@ -const define = (id, levels_y, minusplus_y) => { +const define = (rm, id, levels_y, minusplus_y) => { - #game.resources.define(id, { + rm.define(id, { name: id, render: { type: 'sprite_map', @@ -23,10 +23,10 @@ const define = (id, levels_y, minusplus_y) => { }; const result = { - define: () => { - define('Nutrients', 304, 13); - define('Minerals', 345, 36); - define('Energy', 386, 59); + define: (rm) => { + define(rm, 'Nutrients', 304, 13); + define(rm, 'Minerals', 345, 36); + define(rm, 'Energy', 386, 59); }, }; diff --git a/src/game/backend/CMakeLists.txt b/src/game/backend/CMakeLists.txt index faaa1746..297805ed 100644 --- a/src/game/backend/CMakeLists.txt +++ b/src/game/backend/CMakeLists.txt @@ -1,5 +1,6 @@ SUBDIR( map ) SUBDIR( map_editor ) +SUBDIR( resource ) SUBDIR( rules ) SUBDIR( settings ) SUBDIR( slot ) @@ -21,6 +22,5 @@ SET( SRC ${SRC} ${PWD}/Account.cpp ${PWD}/Player.cpp ${PWD}/MapObject.cpp - ${PWD}/Resource.cpp PARENT_SCOPE ) diff --git a/src/game/backend/Game.cpp b/src/game/backend/Game.cpp index 38c2cfe8..18b70d19 100644 --- a/src/game/backend/Game.cpp +++ b/src/game/backend/Game.cpp @@ -31,9 +31,9 @@ #include "map/tile/TileManager.h" #include "map/tile/Tiles.h" #include "map/MapState.h" +#include "resource/ResourceManager.h" #include "bindings/Bindings.h" #include "graphics/Graphics.h" -#include "Resource.h" #include "animation/Def.h" #include "unit/Def.h" #include "unit/UnitManager.h" @@ -180,6 +180,7 @@ void Game::Start() { NEW( m_map_editor, map_editor::MapEditor, this ); NEW( m_tm, map::tile::TileManager, this ); + NEW( m_rm, resource::ResourceManager, this ); NEW( m_um, unit::UnitManager, this ); NEW( m_bm, base::BaseManager, this ); NEW( m_am, animation::AnimationManager, this ); @@ -202,6 +203,9 @@ void Game::Stop() { DELETE( m_tm ); m_tm = nullptr; + DELETE( m_rm ); + m_rm = nullptr; + DELETE( m_um ); m_um = nullptr; @@ -476,6 +480,10 @@ WRAPIMPL_BEGIN( Game, CLASS_GAME ) "tm", m_tm->Wrap() }, + { + "rm", + m_rm->Wrap() + }, { "um", m_um->Wrap() @@ -665,46 +673,12 @@ const MT_Response Game::ProcessRequest( const MT_Request& request, MT_CANCELABLE switch ( r.type ) { case BackendRequest::BR_GET_TILE_DATA: { const auto& tile = m_map->GetTile( r.data.get_tile_data.tile_x, r.data.get_tile_data.tile_y ); - const auto result = m_state->m_bindings->Call( - bindings::Bindings::CS_ON_GET_TILE_YIELDS, { - { - "tile", - tile->Wrap() - }, - { - "player", - m_slot->Wrap() - }, - } - ); - if ( result.Get()->type != gse::type::Type::T_OBJECT ) { - break; // TMP //THROW( "unexpected return type: expected Object, got " + result.GetTypeString() ); - } - const auto& values = ( (gse::type::Object*)result.Get() )->value; - for ( const auto& v : values ) { - if ( m_resources.find( v.first ) == m_resources.end() ) { - THROW( "unknown resource type: " + v.first ); - } - } - NEWV( yields, FrontendRequest::tile_yields_t, {} ); - yields->reserve( m_resource_idx.size() ); - for ( const auto& idx : m_resource_idx ) { - const auto& v = values.find( idx ); - if ( v == values.end() ) { - DELETE( yields ); - THROW( "missing yields for resource: " + idx ); - } - if ( v->second.Get()->type != gse::type::Type::T_INT ) { - DELETE( yields ); - THROW( "invalid resource value, expected Int, got " + v->second.GetTypeString() + ": " + v->second.ToString() ); - } - yields->push_back( - { - idx, - ( (gse::type::Int*)v->second.Get() )->value - } - ); - } + + NEWV( yields, FrontendRequest::tile_yields_t, m_rm->GetYields( + tile, + m_slot + ) ); + auto fr = FrontendRequest( FrontendRequest::FR_TILE_DATA ); fr.data.tile_data.tile_x = tile->coord.x; fr.data.tile_data.tile_y = tile->coord.y; @@ -869,26 +843,6 @@ const gse::Value Game::AddEvent( event::Event* event ) { return VALUE( gse::type::Undefined ); } -void Game::DefineResource( Resource* resource ) { - Log( "Defining resource ('" + resource->m_id + "')" ); - - ASSERT( m_resources.find( resource->m_id ) == m_resources.end(), "Resource '" + resource->m_id + "' already exists" ); - - m_resources.insert( - { - resource->m_id, - resource - } - ); - m_resource_idx_map.insert( - { - resource->m_id, - m_resource_idx.size() - } - ); - m_resource_idx.push_back( resource->m_id ); -} - const size_t Game::GetTurnId() const { return m_current_turn.GetId(); } @@ -1069,6 +1023,10 @@ map::tile::TileManager* Game::GetTM() const { return m_tm; } +resource::ResourceManager* Game::GetRM() const { + return m_rm; +} + unit::UnitManager* Game::GetUM() const { return m_um; } @@ -1125,31 +1083,6 @@ const types::Vec3 Game::GetTileRenderCoords( const map::tile::Tile* tile ) { }; } -void Game::SerializeResources( types::Buffer& buf ) const { - Log( "Serializing " + std::to_string( m_resources.size() ) + " resources" ); - buf.WriteInt( m_resources.size() ); - for ( const auto& it : m_resource_idx ) { - ASSERT( m_resources.find( it ) != m_resources.end(), "invalid resource idx" ); - const auto& res = m_resources.at( it ); - buf.WriteString( res->m_id ); - buf.WriteString( Resource::Serialize( res ).ToString() ); - } -} - -void Game::UnserializeResources( types::Buffer& buf ) { - ASSERT( m_resources.empty(), "resources not empty" ); - size_t sz = buf.ReadInt(); - Log( "Unserializing " + std::to_string( sz ) + " resources" ); - m_resources.reserve( sz ); - m_resource_idx.reserve( sz ); - m_resource_idx_map.reserve( sz ); - for ( size_t i = 0 ; i < sz ; i++ ) { - const auto name = buf.ReadString(); - auto b = types::Buffer( buf.ReadString() ); - DefineResource( Resource::Unserialize( b ) ); - } -} - void Game::AddFrontendRequest( const FrontendRequest& request ) { //Log( "Sending frontend request (type=" + std::to_string( request.type ) + ")" ); // spammy m_pending_frontend_requests->push_back( request ); @@ -1250,7 +1183,7 @@ void Game::InitGame( MT_Response& response, MT_CANCELABLE ) { // resources { types::Buffer b; - SerializeResources( b ); + m_rm->Serialize( b ); buf.WriteString( b.ToString() ); } @@ -1444,7 +1377,7 @@ void Game::InitGame( MT_Response& response, MT_CANCELABLE ) { // resources { auto ub = types::Buffer( buf.ReadString() ); - UnserializeResources( ub ); + m_rm->Unserialize( ub ); } // units @@ -1519,17 +1452,11 @@ void Game::ResetGame() { m_unprocessed_events.clear(); m_tm->Clear(); + m_rm->Clear(); m_um->Clear(); m_bm->Clear(); m_am->Clear(); - for ( auto& it : m_resources ) { - delete it.second; - } - m_resources.clear(); - m_resource_idx.clear(); - m_resource_idx_map.clear(); - if ( m_map ) { Log( "Resetting map" ); DELETE( m_map ); diff --git a/src/game/backend/Game.h b/src/game/backend/Game.h index 8dbecbdb..f797d3b1 100644 --- a/src/game/backend/Game.h +++ b/src/game/backend/Game.h @@ -51,6 +51,10 @@ class TileManager; } } +namespace resource { +class ResourceManager; +} + namespace unit { class UnitManager; } @@ -63,8 +67,6 @@ namespace animation { class AnimationManager; } -class Resource; - namespace event { class Event; } @@ -336,7 +338,6 @@ CLASS2( Game, MTModule, gse::Wrappable ) void OnError( std::runtime_error& err ); void OnGSEError( gse::Exception& e ); const gse::Value AddEvent( event::Event* event ); - void DefineResource( Resource* resource ); const size_t GetTurnId() const; const bool IsTurnActive() const; const bool IsTurnCompleted( const size_t slot_num ) const; @@ -353,6 +354,7 @@ CLASS2( Game, MTModule, gse::Wrappable ) faction::Faction* GetFaction( const std::string& id ) const; map::tile::TileManager* GetTM() const; + resource::ResourceManager* GetRM() const; unit::UnitManager* GetUM() const; base::BaseManager* GetBM() const; animation::AnimationManager* GetAM() const; @@ -364,13 +366,8 @@ CLASS2( Game, MTModule, gse::Wrappable ) const types::Vec3 GetTileRenderCoords( const map::tile::Tile* tile ); - std::unordered_map< std::string, Resource* > m_resources = {}; - std::vector< std::string > m_resource_idx = {}; - std::unordered_map< std::string, size_t > m_resource_idx_map = {}; - void SerializeResources( types::Buffer& buf ) const; - void UnserializeResources( types::Buffer& buf ); - map::tile::TileManager* m_tm = nullptr; + resource::ResourceManager* m_rm = nullptr; unit::UnitManager* m_um = nullptr; base::BaseManager* m_bm = nullptr; animation::AnimationManager* m_am = nullptr; @@ -417,6 +414,7 @@ CLASS2( Game, MTModule, gse::Wrappable ) private: friend class map::tile::TileManager; + friend class resource::ResourceManager; friend class unit::UnitManager; friend class base::BaseManager; friend class animation::AnimationManager; diff --git a/src/game/backend/bindings/Resources.cpp b/src/game/backend/bindings/Resources.cpp index 0734e425..5805a606 100644 --- a/src/game/backend/bindings/Resources.cpp +++ b/src/game/backend/bindings/Resources.cpp @@ -9,7 +9,7 @@ #include "gse/Exception.h" #include "gse/type/Undefined.h" -#include "game/backend/Resource.h" +#include "game/backend/resource/Resource.h" #include "game/backend/event/DefineResource.h" namespace game { @@ -48,7 +48,7 @@ BINDING_IMPL( resources ) { X( plus ) X( minus ) - auto* resource = new Resource( + auto* resource = new resource::Resource( id, name, { diff --git a/src/game/backend/event/DefineResource.cpp b/src/game/backend/event/DefineResource.cpp index b4179711..a821b77d 100644 --- a/src/game/backend/event/DefineResource.cpp +++ b/src/game/backend/event/DefineResource.cpp @@ -2,14 +2,15 @@ #include "game/backend/Game.h" -#include "game/backend/Resource.h" +#include "game/backend/resource/ResourceManager.h" +#include "game/backend/resource/Resource.h" #include "gse/type/Undefined.h" namespace game { namespace backend { namespace event { -DefineResource::DefineResource( const size_t initiator_slot, Resource* resource ) +DefineResource::DefineResource( const size_t initiator_slot, resource::Resource* resource ) : Event( initiator_slot, ET_RESOURCE_DEFINE ) , m_resource( resource ) { // @@ -23,7 +24,7 @@ const std::string* DefineResource::Validate( Game* game ) const { } const gse::Value DefineResource::Apply( Game* game ) const { - game->DefineResource( m_resource ); + game->GetRM()->DefineResource( m_resource ); return VALUE( gse::type::Undefined ); } @@ -34,12 +35,12 @@ TS_BEGIN( DefineResource ) TS_END() void DefineResource::Serialize( types::Buffer& buf, const DefineResource* event ) { - buf.WriteString( Resource::Serialize( event->m_resource ).ToString() ); + buf.WriteString( resource::Resource::Serialize( event->m_resource ).ToString() ); } DefineResource* DefineResource::Unserialize( types::Buffer& buf, const size_t initiator_slot ) { auto b = types::Buffer( buf.ReadString() ); - return new DefineResource( initiator_slot, Resource::Unserialize( b ) ); + return new DefineResource( initiator_slot, resource::Resource::Unserialize( b ) ); } } diff --git a/src/game/backend/event/DefineResource.h b/src/game/backend/event/DefineResource.h index 4b7cb968..fa6ce9ed 100644 --- a/src/game/backend/event/DefineResource.h +++ b/src/game/backend/event/DefineResource.h @@ -5,13 +5,15 @@ namespace game { namespace backend { +namespace resource { class Resource; +} namespace event { class DefineResource : public Event { public: - DefineResource( const size_t initiator_slot, Resource* resource ); + DefineResource( const size_t initiator_slot, resource::Resource* resource ); const std::string* Validate( Game* game ) const override; const gse::Value Apply( Game* game ) const override; @@ -24,7 +26,7 @@ class DefineResource : public Event { static DefineResource* Unserialize( types::Buffer& buf, const size_t initiator_slot ); private: - Resource* m_resource; + resource::Resource* m_resource; }; } diff --git a/src/game/backend/faction/FactionManager.cpp b/src/game/backend/faction/FactionManager.cpp index 75e3e40d..a2008730 100644 --- a/src/game/backend/faction/FactionManager.cpp +++ b/src/game/backend/faction/FactionManager.cpp @@ -62,7 +62,7 @@ const std::vector< Faction* > FactionManager::GetAll() const { return result; } -WRAPIMPL_BEGIN( FactionManager, CLASS_FACTIONS ) +WRAPIMPL_BEGIN( FactionManager, CLASS_FM ) WRAPIMPL_PROPS { "add", diff --git a/src/game/backend/map/Map.cpp b/src/game/backend/map/Map.cpp index f0e5f5e3..b5762c22 100644 --- a/src/game/backend/map/Map.cpp +++ b/src/game/backend/map/Map.cpp @@ -94,8 +94,8 @@ Map::Map( Game* game ) ); // main source textures - m_textures.source.texture_pcx = g_engine->GetTextureLoader()->LoadTexture( resource::PCX_TEXTURE ); - m_textures.source.ter1_pcx = g_engine->GetTextureLoader()->LoadTexture( resource::PCX_TER1 ); + m_textures.source.texture_pcx = g_engine->GetTextureLoader()->LoadTexture( ::resource::PCX_TEXTURE ); + m_textures.source.ter1_pcx = g_engine->GetTextureLoader()->LoadTexture( ::resource::PCX_TER1 ); // add map modules // order of passes is important diff --git a/src/game/backend/resource/CMakeLists.txt b/src/game/backend/resource/CMakeLists.txt new file mode 100644 index 00000000..a8f4470d --- /dev/null +++ b/src/game/backend/resource/CMakeLists.txt @@ -0,0 +1,6 @@ +SET( SRC ${SRC} + + ${PWD}/ResourceManager.cpp + ${PWD}/Resource.cpp + + PARENT_SCOPE ) diff --git a/src/game/backend/Resource.cpp b/src/game/backend/resource/Resource.cpp similarity index 99% rename from src/game/backend/Resource.cpp rename to src/game/backend/resource/Resource.cpp index 449ca3b3..4e65cfe8 100644 --- a/src/game/backend/Resource.cpp +++ b/src/game/backend/resource/Resource.cpp @@ -2,6 +2,7 @@ namespace game { namespace backend { +namespace resource { Resource::Resource( const std::string& id, const std::string& name, const render_info_t& render_info ) : m_id( id ) @@ -96,3 +97,4 @@ Resource* Resource::Unserialize( types::Buffer& buf ) { } } +} diff --git a/src/game/backend/Resource.h b/src/game/backend/resource/Resource.h similarity index 97% rename from src/game/backend/Resource.h rename to src/game/backend/resource/Resource.h index 176ef2de..6d539b24 100644 --- a/src/game/backend/Resource.h +++ b/src/game/backend/resource/Resource.h @@ -7,6 +7,7 @@ namespace game { namespace backend { +namespace resource { class Resource { public: @@ -48,3 +49,4 @@ class Resource { } } +} diff --git a/src/game/backend/resource/ResourceManager.cpp b/src/game/backend/resource/ResourceManager.cpp new file mode 100644 index 00000000..ebb56556 --- /dev/null +++ b/src/game/backend/resource/ResourceManager.cpp @@ -0,0 +1,190 @@ +#include "ResourceManager.h" + +#include "Resource.h" + +#include "gse/callable/Native.h" +#include "game/backend/Game.h" +#include "game/backend/State.h" +#include "game/backend/slot/Slot.h" +#include "game/backend/bindings/Bindings.h" +#include "game/backend/event/DefineResource.h" + +namespace game { +namespace backend { +namespace resource { + +ResourceManager::ResourceManager( Game* game ) + : m_game( game ) { + // +} + +ResourceManager::~ResourceManager() { + Clear(); +} + +void ResourceManager::Clear() { + for ( auto& it : m_resources ) { + delete it.second; + } + m_resources.clear(); + m_resource_idx.clear(); + m_resource_idx_map.clear(); +} + +void ResourceManager::DefineResource( resource::Resource* resource ) { + Log( "Defining resource ('" + resource->m_id + "')" ); + + ASSERT( m_resources.find( resource->m_id ) == m_resources.end(), "Resource '" + resource->m_id + "' already exists" ); + + m_resources.insert( + { + resource->m_id, + resource + } + ); + m_resource_idx_map.insert( + { + resource->m_id, + m_resource_idx.size() + } + ); + m_resource_idx.push_back( resource->m_id ); +} + +const ResourceManager::yields_t ResourceManager::GetYields( map::tile::Tile* tile, slot::Slot* slot ) const { + const auto result = m_game->GetState()->m_bindings->Call( + bindings::Bindings::CS_ON_GET_TILE_YIELDS, { + { + "tile", + tile->Wrap() + }, + { + "player", + slot->Wrap() + }, + } + ); + if ( result.Get()->type != gse::type::Type::T_OBJECT ) { + THROW( "unexpected return type: expected Object, got " + result.GetTypeString() ); + } + const auto& values = ( (gse::type::Object*)result.Get() )->value; + for ( const auto& v : values ) { + if ( m_resources.find( v.first ) == m_resources.end() ) { + THROW( "unknown resource type: " + v.first ); + } + } + yields_t yields = {}; + yields.reserve( m_resource_idx.size() ); + for ( const auto& idx : m_resource_idx ) { + const auto& v = values.find( idx ); + if ( v == values.end() ) { + THROW( "missing yields for resource: " + idx ); + } + if ( v->second.Get()->type != gse::type::Type::T_INT ) { + THROW( "invalid resource value, expected Int, got " + v->second.GetTypeString() + ": " + v->second.ToString() ); + } + yields.push_back( + { + idx, + ( (gse::type::Int*)v->second.Get() )->value + } + ); + } + return yields; +} + +WRAPIMPL_BEGIN( ResourceManager, CLASS_RM ) + WRAPIMPL_PROPS + { + "define", + NATIVE_CALL( this ) { + N_EXPECT_ARGS( 2 ); + N_GETVALUE( id, 0, String ); + N_GETVALUE( def, 1, Object ); + N_GETPROP( name, def, "name", String ); + N_GETPROP( render, def, "render", Object ); + N_GETPROP( type, render, "type", String ); + + if ( type == "sprite_map" ) { + N_GETPROP( file, render, "file", String ); + + N_GETPROP( yields, render, "yields", Object ); + N_GETPROP( grid_x, yields, "grid_x", Int ); + N_GETPROP( grid_y, yields, "grid_y", Int ); + N_GETPROP( grid_margin, yields, "grid_margin", Int ); + N_GETPROP( cell_width, yields, "cell_width", Int ); + N_GETPROP( cell_height, yields, "cell_height", Int ); + N_GETPROP( cells_count, yields, "cells_count", Int ); + +#define X( _n ) \ + N_GETPROP( _n, render, "plus", Object ); \ + N_GETPROP( _n ## _x, _n, "x", Int ); \ + N_GETPROP( _n ## _y, _n, "y", Int ); \ + N_GETPROP( _n ## _width, _n, "width", Int ); \ + N_GETPROP( _n ## _height, _n, "height", Int ); + X( plus ) + X( minus ) + + auto* resource = new resource::Resource( + id, + name, + { + file, + { + (uint16_t)grid_x, (uint16_t)grid_y, (uint8_t)grid_margin, + (uint16_t)cell_width, (uint16_t)cell_height, (uint8_t)cells_count, + }, + { + (uint16_t)plus_x, (uint16_t)plus_y, + (uint16_t)plus_width, (uint16_t)plus_height, + }, + { + (uint16_t)minus_x, (uint16_t)minus_y, + (uint16_t)minus_width, (uint16_t)minus_height, + }, + } + ); + return m_game->AddEvent( new event::DefineResource( m_game->GetSlotNum(), resource ) ); + } + else { + GSE_ERROR( gse::EC.GAME_ERROR, "Unsupported resource type: " + type ); + } + + return VALUE( gse::type::Undefined ); + } ) + }, + }; +WRAPIMPL_END_PTR( ResourceManager ) + +UNWRAPIMPL_PTR( ResourceManager ) + +void ResourceManager::Serialize( types::Buffer& buf ) const { + Log( "Serializing " + std::to_string( m_resources.size() ) + " resources" ); + buf.WriteInt( m_resources.size() ); + for ( const auto& it : m_resource_idx ) { + ASSERT( m_resources.find( it ) != m_resources.end(), "invalid resource idx" ); + const auto& res = m_resources.at( it ); + buf.WriteString( res->m_id ); + buf.WriteString( resource::Resource::Serialize( res ).ToString() ); + } +} + +void ResourceManager::Unserialize( types::Buffer& buf ) { + Clear(); + ASSERT( m_resources.empty(), "resources not empty" ); + size_t sz = buf.ReadInt(); + Log( "Unserializing " + std::to_string( sz ) + " resources" ); + m_resources.reserve( sz ); + m_resource_idx.reserve( sz ); + m_resource_idx_map.reserve( sz ); + for ( size_t i = 0 ; i < sz ; i++ ) { + const auto name = buf.ReadString(); + auto b = types::Buffer( buf.ReadString() ); + DefineResource( resource::Resource::Unserialize( b ) ); + } +} + + +} +} +} diff --git a/src/game/backend/resource/ResourceManager.h b/src/game/backend/resource/ResourceManager.h new file mode 100644 index 00000000..8ad530a6 --- /dev/null +++ b/src/game/backend/resource/ResourceManager.h @@ -0,0 +1,53 @@ +#pragma once + +#include +#include + +#include "common/Common.h" +#include "gse/Wrappable.h" +#include "gse/type/Object.h" + +namespace game { +namespace backend { + +class Game; + +namespace slot { +class Slot; +} + +namespace map::tile { +class Tile; +} + +namespace resource { + +class Resource; + +CLASS2( ResourceManager, common::Class, gse::Wrappable ) + + ResourceManager( Game* game ); + ~ResourceManager(); + + void Clear(); + void DefineResource( resource::Resource* resource ); + + typedef std::vector< std::pair< std::string, size_t > > yields_t; + const yields_t GetYields( map::tile::Tile* tile, slot::Slot* slot ) const; + + WRAPDEFS_PTR( ResourceManager ) + + void Serialize( types::Buffer& buf ) const; + void Unserialize( types::Buffer& buf ); + +private: + Game* m_game; + + std::unordered_map< std::string, resource::Resource* > m_resources = {}; + std::vector< std::string > m_resource_idx = {}; + std::unordered_map< std::string, size_t > m_resource_idx_map = {}; +}; + +} +} +} diff --git a/src/gse/type/Object.cpp b/src/gse/type/Object.cpp index 5df04f80..d60be887 100644 --- a/src/gse/type/Object.cpp +++ b/src/gse/type/Object.cpp @@ -52,7 +52,7 @@ static const std::unordered_map< Object::object_class_t, std::string > s_object_ "#faction" }, { - Object::CLASS_FACTIONS, + Object::CLASS_FM, "#factions" }, { diff --git a/src/gse/type/Object.h b/src/gse/type/Object.h index 54cbc515..9739aa45 100644 --- a/src/gse/type/Object.h +++ b/src/gse/type/Object.h @@ -33,11 +33,12 @@ class Object : public Type { CLASS_STATE, CLASS_GAME, CLASS_MAP, + CLASS_RM, CLASS_TM, CLASS_TILE, CLASS_PLAYER, CLASS_FACTION, - CLASS_FACTIONS, + CLASS_FM, CLASS_UM, CLASS_UNITDEF, CLASS_UNIT, From c8c2e415be49e0214c56d7e511347839adbaee60 Mon Sep 17 00:00:00 2001 From: afwbkbc Date: Sat, 16 Nov 2024 16:09:41 +0200 Subject: [PATCH 08/13] fix --- GLSMAC_data/default/main.gls.js | 2 - GLSMAC_data/default/map.gls.js | 46 ------------------- GLSMAC_data/default/resources.gls.js | 41 +++++++++++++++++ src/game/backend/resource/ResourceManager.cpp | 5 +- src/game/backend/resource/ResourceManager.h | 2 +- 5 files changed, 44 insertions(+), 52 deletions(-) delete mode 100644 GLSMAC_data/default/map.gls.js diff --git a/GLSMAC_data/default/main.gls.js b/GLSMAC_data/default/main.gls.js index fbec6879..7ef04cd3 100644 --- a/GLSMAC_data/default/main.gls.js +++ b/GLSMAC_data/default/main.gls.js @@ -1,6 +1,5 @@ const factions = #include('factions'); const resources = #include('resources'); -const map = #include('map'); const units = #include('units'); const bases = #include('bases'); @@ -53,7 +52,6 @@ const bases = #include('bases'); // init game data resources.define(e.game.rm); - map.define(); units.define(); bases.define(); diff --git a/GLSMAC_data/default/map.gls.js b/GLSMAC_data/default/map.gls.js deleted file mode 100644 index b31d7413..00000000 --- a/GLSMAC_data/default/map.gls.js +++ /dev/null @@ -1,46 +0,0 @@ -const result = { - - define: () => { - - #game.on.get_tile_yields((e) => { - - let result = { - Nutrients: 0, - Minerals: 0, - Energy: 0, - }; - - // TODO: fix increments of properties in GSE - - if (e.tile.has_fungus) { - if (e.player.faction.is_progenitor) { // tmp - //result.Energy += 2; - result.Energy = result.Energy + 2; - } - } else { - if (e.tile.is_land) { - //result.Nutrients += (e.tile.moisture - 1); - result.Nutrients = result.Nutrients + (e.tile.moisture - 1); - if (e.tile.rockiness > 1) { - //result.Minerals++; - result.Minerals = result.Minerals + 1; - } - if (e.tile.has_river) { - result.Energy = result.Energy + 1; - } - } else { - //result.Nutrients++; - result.Nutrients = result.Energy + 1; - if (e.player.faction.id == 'PIRATES') { // tmp - //result.Minerals++; - result.Minerals = result.Minerals + 1; - } - } - } - return result; - }); - }, - -}; - -return result; diff --git a/GLSMAC_data/default/resources.gls.js b/GLSMAC_data/default/resources.gls.js index 8fb06e91..c6b35627 100644 --- a/GLSMAC_data/default/resources.gls.js +++ b/GLSMAC_data/default/resources.gls.js @@ -24,9 +24,50 @@ const define = (rm, id, levels_y, minusplus_y) => { const result = { define: (rm) => { + define(rm, 'Nutrients', 304, 13); define(rm, 'Minerals', 345, 36); define(rm, 'Energy', 386, 59); + + rm.on('get_yields', (e) => { + + let result = { + Nutrients: 0, + Minerals: 0, + Energy: 0, + }; + + // TODO: fix increments of properties in GSE + + if (e.tile.has_fungus) { + if (e.player.faction.is_progenitor) { // tmp + //result.Energy += 2; + result.Energy = result.Energy + 2; + } + } else { + if (e.tile.is_land) { + //result.Nutrients += (e.tile.moisture - 1); + result.Nutrients = result.Nutrients + (e.tile.moisture - 1); + if (e.tile.rockiness > 1) { + //result.Minerals++; + result.Minerals = result.Minerals + 1; + } + if (e.tile.has_river) { + result.Energy = result.Energy + 1; + } + } else { + //result.Nutrients++; + result.Nutrients = result.Energy + 1; + if (e.player.faction.id == 'PIRATES') { // tmp + //result.Minerals++; + result.Minerals = result.Minerals + 1; + } + } + } + return result; + }); + + }, }; diff --git a/src/game/backend/resource/ResourceManager.cpp b/src/game/backend/resource/ResourceManager.cpp index ebb56556..2caff2e0 100644 --- a/src/game/backend/resource/ResourceManager.cpp +++ b/src/game/backend/resource/ResourceManager.cpp @@ -51,9 +51,8 @@ void ResourceManager::DefineResource( resource::Resource* resource ) { m_resource_idx.push_back( resource->m_id ); } -const ResourceManager::yields_t ResourceManager::GetYields( map::tile::Tile* tile, slot::Slot* slot ) const { - const auto result = m_game->GetState()->m_bindings->Call( - bindings::Bindings::CS_ON_GET_TILE_YIELDS, { +const ResourceManager::yields_t ResourceManager::GetYields( map::tile::Tile* tile, slot::Slot* slot ) { + const auto result = m_game->GetState()->m_bindings->Trigger( this, "get_yields", { { "tile", tile->Wrap() diff --git a/src/game/backend/resource/ResourceManager.h b/src/game/backend/resource/ResourceManager.h index 8ad530a6..cd935fe4 100644 --- a/src/game/backend/resource/ResourceManager.h +++ b/src/game/backend/resource/ResourceManager.h @@ -33,7 +33,7 @@ CLASS2( ResourceManager, common::Class, gse::Wrappable ) void DefineResource( resource::Resource* resource ); typedef std::vector< std::pair< std::string, size_t > > yields_t; - const yields_t GetYields( map::tile::Tile* tile, slot::Slot* slot ) const; + const yields_t GetYields( map::tile::Tile* tile, slot::Slot* slot ); WRAPDEFS_PTR( ResourceManager ) From 3f2f46495db09fa4e9af657a517b15275c71167e Mon Sep 17 00:00:00 2001 From: afwbkbc Date: Sat, 16 Nov 2024 16:52:19 +0200 Subject: [PATCH 09/13] removed remaining #game calls from scripts --- GLSMAC_data/default/bases.gls.js | 4 +- GLSMAC_data/default/bases/pops.gls.js | 28 +++--- GLSMAC_data/default/factions.gls.js | 49 ++++++---- GLSMAC_data/default/factions/angels.gls.js | 14 --- GLSMAC_data/default/factions/believers.gls.js | 14 --- .../default/factions/caretakers.gls.js | 15 --- .../default/factions/consciousness.gls.js | 14 --- GLSMAC_data/default/factions/drones.gls.js | 14 --- GLSMAC_data/default/factions/gaians.gls.js | 16 ---- GLSMAC_data/default/factions/hive.gls.js | 14 --- .../default/factions/morganites.gls.js | 14 --- .../default/factions/peacekeepers.gls.js | 14 --- GLSMAC_data/default/factions/pirates.gls.js | 15 --- .../default/factions/planetcult.gls.js | 14 --- GLSMAC_data/default/factions/spartans.gls.js | 14 --- .../default/factions/university.gls.js | 14 --- GLSMAC_data/default/factions/usurpers.gls.js | 15 --- GLSMAC_data/default/main.gls.js | 4 +- GLSMAC_data/default/resources.gls.js | 2 +- GLSMAC_data/default/units.gls.js | 6 +- GLSMAC_data/default/units/animations.gls.js | 4 +- GLSMAC_data/default/units/defs.gls.js | 6 +- .../backend/animation/AnimationManager.cpp | 55 +++++++++++ src/game/backend/base/BaseManager.cpp | 59 ++++++++++++ src/game/backend/bindings/Binding.h | 8 -- src/game/backend/bindings/Bindings.cpp | 40 -------- src/game/backend/bindings/Bindings.h | 22 ----- src/game/backend/bindings/On.cpp | 13 --- src/game/backend/faction/Faction.cpp | 6 +- src/game/backend/faction/Faction.h | 4 +- src/game/backend/faction/FactionManager.cpp | 48 +++++++++- src/game/backend/unit/UnitManager.cpp | 96 +++++++++++++++++++ src/gse/builtins/String.cpp | 4 +- src/gse/callable/Native.h | 18 ++-- 34 files changed, 331 insertions(+), 346 deletions(-) delete mode 100644 GLSMAC_data/default/factions/angels.gls.js delete mode 100644 GLSMAC_data/default/factions/believers.gls.js delete mode 100644 GLSMAC_data/default/factions/caretakers.gls.js delete mode 100644 GLSMAC_data/default/factions/consciousness.gls.js delete mode 100644 GLSMAC_data/default/factions/drones.gls.js delete mode 100644 GLSMAC_data/default/factions/gaians.gls.js delete mode 100644 GLSMAC_data/default/factions/hive.gls.js delete mode 100644 GLSMAC_data/default/factions/morganites.gls.js delete mode 100644 GLSMAC_data/default/factions/peacekeepers.gls.js delete mode 100644 GLSMAC_data/default/factions/pirates.gls.js delete mode 100644 GLSMAC_data/default/factions/planetcult.gls.js delete mode 100644 GLSMAC_data/default/factions/spartans.gls.js delete mode 100644 GLSMAC_data/default/factions/university.gls.js delete mode 100644 GLSMAC_data/default/factions/usurpers.gls.js diff --git a/GLSMAC_data/default/bases.gls.js b/GLSMAC_data/default/bases.gls.js index 6d74abd5..7c27be7d 100644 --- a/GLSMAC_data/default/bases.gls.js +++ b/GLSMAC_data/default/bases.gls.js @@ -6,8 +6,8 @@ const result = { //pops.init(); }, - define: () => { - pops.define(); + define: (bm) => { + pops.define(bm); }, }; diff --git a/GLSMAC_data/default/bases/pops.gls.js b/GLSMAC_data/default/bases/pops.gls.js index 54865822..1462fab9 100644 --- a/GLSMAC_data/default/bases/pops.gls.js +++ b/GLSMAC_data/default/bases/pops.gls.js @@ -1,4 +1,4 @@ -const define = (id, name, renders_human_x, renders_progenitor_x, properties) => { +const define = (bm, id, name, renders_human_x, renders_progenitor_x, properties) => { // TODO: if (!properties) { ... } let rh = []; for ( x of renders_human_x ) { @@ -18,7 +18,7 @@ const define = (id, name, renders_human_x, renders_progenitor_x, properties) => w: 38, h: 48, }; } - #game.bases.define_pop(id, { + bm.define_pop(id, { name: name, renders_human: rh, renders_progenitor: rp, @@ -26,19 +26,19 @@ const define = (id, name, renders_human_x, renders_progenitor_x, properties) => }; const result = { - define: () => { + define: (bm) => { - define('Worker', 'Worker', [79, 118], [40], {tile_worker: true}); - define('Talent', 'Talent', [1, 40], [1], {tile_worker: true}); - define('Doctor', 'Doctor', [352], [196], {}); - define('Technician', 'Technician', [313], [157], {}); - define('Librarian', 'Librarian', [391], [235], {}); - define('Engineer', 'Engineer', [430], [274], {}); - define('Empath', 'Empath', [469], [313], {}); - define('Thinker', 'Thinker', [508], [352], {}); - define('Transcend', 'Transcend', [547], [391], {}); - define('Drone', 'Drone', [157, 196], [79], {}); - define('DronePlus', 'Drone', [235, 274], [118], {}); + define(bm, 'Worker', 'Worker', [79, 118], [40], {tile_worker: true}); + define(bm, 'Talent', 'Talent', [1, 40], [1], {tile_worker: true}); + define(bm, 'Doctor', 'Doctor', [352], [196], {}); + define(bm, 'Technician', 'Technician', [313], [157], {}); + define(bm, 'Librarian', 'Librarian', [391], [235], {}); + define(bm, 'Engineer', 'Engineer', [430], [274], {}); + define(bm, 'Empath', 'Empath', [469], [313], {}); + define(bm, 'Thinker', 'Thinker', [508], [352], {}); + define(bm, 'Transcend', 'Transcend', [547], [391], {}); + define(bm, 'Drone', 'Drone', [157, 196], [79], {}); + define(bm, 'DronePlus', 'Drone', [235, 274], [118], {}); }, }; diff --git a/GLSMAC_data/default/factions.gls.js b/GLSMAC_data/default/factions.gls.js index 1270d83c..ed3e33c7 100644 --- a/GLSMAC_data/default/factions.gls.js +++ b/GLSMAC_data/default/factions.gls.js @@ -1,23 +1,38 @@ +const factions = [ + ['Gaians', 'gaians', {}], + ['Hive', 'hive', {}], + ['University', 'univ', {}], + ['Morganites', 'morgan', {}], + ['Spartans', 'spartans', {}], + ['Believers', 'believe', {}], + ['Peacekeepers', 'peace', {}], + ['Consciousness', 'cyborg', {}], + ['Pirates', 'pirates', {is_naval: true}], + ['Drones', 'drone', {}], + ['Angels', 'angels', {}], + ['Planetcult', 'fungboy', {}], + ['Caretakers', 'caretake', {is_progenitor: true}], + ['Usurpers', 'usurper', {is_progenitor: true}], +]; + return { configure: (fm) => { - for (faction of [ - 'gaians', - 'hive', - 'university', - 'morganites', - 'spartans', - 'believers', - 'peacekeepers', - 'consciousness', - 'pirates', - 'drones', - 'angels', - 'planetcult', - 'caretakers', - 'usurpers' - ]) { - fm.add(#to_uppercase(faction), #include('./factions/' + faction)); + for (f of factions) { + fm.add(#uppercase(f[0]), { + name: f[0], + colors: fm.import_colors(f[1] + '.pcx'), + bases: { + render: { + type: 'sprite_grid', + file: f[1] + '.pcx', + grid_x: 1, grid_y: 1, + cell_width: 100, cell_height: 75, + cell_padding: 1, + }, + names: fm.import_base_names(f[1] + '.txt'), + }, + } + f[2]); } }, diff --git a/GLSMAC_data/default/factions/angels.gls.js b/GLSMAC_data/default/factions/angels.gls.js deleted file mode 100644 index 90f4c8d2..00000000 --- a/GLSMAC_data/default/factions/angels.gls.js +++ /dev/null @@ -1,14 +0,0 @@ -return { - name: 'Angels', - colors: #game.factions.import_colors('angels.pcx'), - bases: { - render: { - type: 'sprite_grid', - file: 'angels.pcx', - grid_x: 1, grid_y: 1, - cell_width: 100, cell_height: 75, - cell_padding: 1, - }, - names: #game.factions.import_base_names('angels.txt'), - }, -}; diff --git a/GLSMAC_data/default/factions/believers.gls.js b/GLSMAC_data/default/factions/believers.gls.js deleted file mode 100644 index 7383f124..00000000 --- a/GLSMAC_data/default/factions/believers.gls.js +++ /dev/null @@ -1,14 +0,0 @@ -return { - name: 'Believers', - colors: #game.factions.import_colors('believe.pcx'), - bases: { - render: { - type: 'sprite_grid', - file: 'believe.pcx', - grid_x: 1, grid_y: 1, - cell_width: 100, cell_height: 75, - cell_padding: 1, - }, - names: #game.factions.import_base_names('believe.txt'), - }, -}; diff --git a/GLSMAC_data/default/factions/caretakers.gls.js b/GLSMAC_data/default/factions/caretakers.gls.js deleted file mode 100644 index 021223a8..00000000 --- a/GLSMAC_data/default/factions/caretakers.gls.js +++ /dev/null @@ -1,15 +0,0 @@ -return { - name: 'Caretakers', - colors: #game.factions.import_colors('caretake.pcx'), - bases: { - render: { - type: 'sprite_grid', - file: 'caretake.pcx', - grid_x: 1, grid_y: 1, - cell_width: 100, cell_height: 75, - cell_padding: 1, - }, - names: #game.factions.import_base_names('caretake.txt'), - }, - is_progenitor: true, -}; diff --git a/GLSMAC_data/default/factions/consciousness.gls.js b/GLSMAC_data/default/factions/consciousness.gls.js deleted file mode 100644 index 4a8bdfc8..00000000 --- a/GLSMAC_data/default/factions/consciousness.gls.js +++ /dev/null @@ -1,14 +0,0 @@ -return { - name: 'Consciousness', - colors: #game.factions.import_colors('cyborg.pcx'), - bases: { - render: { - type: 'sprite_grid', - file: 'cyborg.pcx', - grid_x: 1, grid_y: 1, - cell_width: 100, cell_height: 75, - cell_padding: 1, - }, - names: #game.factions.import_base_names('cyborg.txt'), - }, -}; diff --git a/GLSMAC_data/default/factions/drones.gls.js b/GLSMAC_data/default/factions/drones.gls.js deleted file mode 100644 index bf70c73a..00000000 --- a/GLSMAC_data/default/factions/drones.gls.js +++ /dev/null @@ -1,14 +0,0 @@ -return { - name: 'Drones', - colors: #game.factions.import_colors('drone.pcx'), - bases: { - render: { - type: 'sprite_grid', - file: 'drone.pcx', - grid_x: 1, grid_y: 1, - cell_width: 100, cell_height: 75, - cell_padding: 1, - }, - names: #game.factions.import_base_names('drone.txt'), - }, -}; diff --git a/GLSMAC_data/default/factions/gaians.gls.js b/GLSMAC_data/default/factions/gaians.gls.js deleted file mode 100644 index 1b944258..00000000 --- a/GLSMAC_data/default/factions/gaians.gls.js +++ /dev/null @@ -1,16 +0,0 @@ -const faction = { - name: 'Gaians', - colors: #game.factions.import_colors('gaians.pcx'), - bases: { - render: { - type: 'sprite_grid', - file: 'gaians.pcx', - grid_x: 1, grid_y: 1, - cell_width: 100, cell_height: 75, - cell_padding: 1, - }, - names: #game.factions.import_base_names('gaians.txt'), - }, -}; - -return faction; \ No newline at end of file diff --git a/GLSMAC_data/default/factions/hive.gls.js b/GLSMAC_data/default/factions/hive.gls.js deleted file mode 100644 index b05b73b9..00000000 --- a/GLSMAC_data/default/factions/hive.gls.js +++ /dev/null @@ -1,14 +0,0 @@ -return { - name: 'Hive', - colors: #game.factions.import_colors('hive.pcx'), - bases: { - render: { - type: 'sprite_grid', - file: 'hive.pcx', - grid_x: 1, grid_y: 1, - cell_width: 100, cell_height: 75, - cell_padding: 1, - }, - names: #game.factions.import_base_names('hive.txt'), - }, -}; diff --git a/GLSMAC_data/default/factions/morganites.gls.js b/GLSMAC_data/default/factions/morganites.gls.js deleted file mode 100644 index 4618b49f..00000000 --- a/GLSMAC_data/default/factions/morganites.gls.js +++ /dev/null @@ -1,14 +0,0 @@ -return { - name: 'Morganites', - colors: #game.factions.import_colors('morgan.pcx'), - bases: { - render: { - type: 'sprite_grid', - file: 'morgan.pcx', - grid_x: 1, grid_y: 1, - cell_width: 100, cell_height: 75, - cell_padding: 1, - }, - names: #game.factions.import_base_names('morgan.txt'), - }, -}; diff --git a/GLSMAC_data/default/factions/peacekeepers.gls.js b/GLSMAC_data/default/factions/peacekeepers.gls.js deleted file mode 100644 index 486f76e1..00000000 --- a/GLSMAC_data/default/factions/peacekeepers.gls.js +++ /dev/null @@ -1,14 +0,0 @@ -return { - name: 'Peacekeepers', - colors: #game.factions.import_colors('peace.pcx'), - bases: { - render: { - type: 'sprite_grid', - file: 'peace.pcx', - grid_x: 1, grid_y: 1, - cell_width: 100, cell_height: 75, - cell_padding: 1, - }, - names: #game.factions.import_base_names('peace.txt'), - }, -}; diff --git a/GLSMAC_data/default/factions/pirates.gls.js b/GLSMAC_data/default/factions/pirates.gls.js deleted file mode 100644 index e3c92643..00000000 --- a/GLSMAC_data/default/factions/pirates.gls.js +++ /dev/null @@ -1,15 +0,0 @@ -return { - name: 'Pirates', - colors: #game.factions.import_colors('pirates.pcx'), - bases: { - render: { - type: 'sprite_grid', - file: 'pirates.pcx', - grid_x: 1, grid_y: 1, - cell_width: 100, cell_height: 75, - cell_padding: 1, - }, - names: #game.factions.import_base_names('pirates.txt'), - }, - is_naval: true, -}; diff --git a/GLSMAC_data/default/factions/planetcult.gls.js b/GLSMAC_data/default/factions/planetcult.gls.js deleted file mode 100644 index 874201c0..00000000 --- a/GLSMAC_data/default/factions/planetcult.gls.js +++ /dev/null @@ -1,14 +0,0 @@ -return { - name: 'Planet Cult', - colors: #game.factions.import_colors('fungboy.pcx'), - bases: { - render: { - type: 'sprite_grid', - file: 'fungboy.pcx', - grid_x: 1, grid_y: 1, - cell_width: 100, cell_height: 75, - cell_padding: 1, - }, - names: #game.factions.import_base_names('fungboy.txt'), - }, -}; diff --git a/GLSMAC_data/default/factions/spartans.gls.js b/GLSMAC_data/default/factions/spartans.gls.js deleted file mode 100644 index df015be8..00000000 --- a/GLSMAC_data/default/factions/spartans.gls.js +++ /dev/null @@ -1,14 +0,0 @@ -return { - name: 'Spartans', - colors: #game.factions.import_colors('spartans.pcx'), - bases: { - render: { - type: 'sprite_grid', - file: 'spartans.pcx', - grid_x: 1, grid_y: 1, - cell_width: 100, cell_height: 75, - cell_padding: 1, - }, - names: #game.factions.import_base_names('spartans.txt'), - }, -}; diff --git a/GLSMAC_data/default/factions/university.gls.js b/GLSMAC_data/default/factions/university.gls.js deleted file mode 100644 index 55453a74..00000000 --- a/GLSMAC_data/default/factions/university.gls.js +++ /dev/null @@ -1,14 +0,0 @@ -return { - name: 'University', - colors: #game.factions.import_colors('univ.pcx'), - bases: { - render: { - type: 'sprite_grid', - file: 'univ.pcx', - grid_x: 1, grid_y: 1, - cell_width: 100, cell_height: 75, - cell_padding: 1, - }, - names: #game.factions.import_base_names('univ.txt'), - }, -}; diff --git a/GLSMAC_data/default/factions/usurpers.gls.js b/GLSMAC_data/default/factions/usurpers.gls.js deleted file mode 100644 index bddd7574..00000000 --- a/GLSMAC_data/default/factions/usurpers.gls.js +++ /dev/null @@ -1,15 +0,0 @@ -return { - name: 'Usurpers', - colors: #game.factions.import_colors('usurper.pcx'), - bases: { - render: { - type: 'sprite_grid', - file: 'usurper.pcx', - grid_x: 1, grid_y: 1, - cell_width: 100, cell_height: 75, - cell_padding: 1, - }, - names: #game.factions.import_base_names('usurper.txt'), - }, - is_progenitor: true, -}; diff --git a/GLSMAC_data/default/main.gls.js b/GLSMAC_data/default/main.gls.js index 7ef04cd3..f306d581 100644 --- a/GLSMAC_data/default/main.gls.js +++ b/GLSMAC_data/default/main.gls.js @@ -52,8 +52,8 @@ const bases = #include('bases'); // init game data resources.define(e.game.rm); - units.define(); - bases.define(); + units.define(e.game); + bases.define(e.game.bm); // init players players = e.game.get_players(); diff --git a/GLSMAC_data/default/resources.gls.js b/GLSMAC_data/default/resources.gls.js index c6b35627..07711e4d 100644 --- a/GLSMAC_data/default/resources.gls.js +++ b/GLSMAC_data/default/resources.gls.js @@ -58,7 +58,7 @@ const result = { } else { //result.Nutrients++; result.Nutrients = result.Energy + 1; - if (e.player.faction.id == 'PIRATES') { // tmp + if (e.player.faction.is_naval) { // tmp //result.Minerals++; result.Minerals = result.Minerals + 1; } diff --git a/GLSMAC_data/default/units.gls.js b/GLSMAC_data/default/units.gls.js index d78c315f..43ff0cd2 100644 --- a/GLSMAC_data/default/units.gls.js +++ b/GLSMAC_data/default/units.gls.js @@ -12,9 +12,9 @@ const result = { turns.configure(game); }, - define: () => { - defs.define(); - animations.define(); + define: (game) => { + defs.define(game.um); + animations.define(game.am); }, }; diff --git a/GLSMAC_data/default/units/animations.gls.js b/GLSMAC_data/default/units/animations.gls.js index abc15e55..62c4925e 100644 --- a/GLSMAC_data/default/units/animations.gls.js +++ b/GLSMAC_data/default/units/animations.gls.js @@ -4,7 +4,7 @@ const result = { DEATH_PSI: 'DEATH_PSI', - define: () => { + define: (am) => { const animations = [ @@ -39,7 +39,7 @@ const result = { ]; for (animation of animations) { - #game.animations.define(animation[0], animation[1]); + am.define_animation(animation[0], animation[1]); } }, diff --git a/GLSMAC_data/default/units/defs.gls.js b/GLSMAC_data/default/units/defs.gls.js index 220e47ec..1417084b 100644 --- a/GLSMAC_data/default/units/defs.gls.js +++ b/GLSMAC_data/default/units/defs.gls.js @@ -39,14 +39,14 @@ const units = [ ]; const result = { - define: () => { + define: (um) => { for (morale of morales) { - #game.units.define_morales(morale[0], morale[1]); + um.define_morales(morale[0], morale[1]); } for (unit of units) { - #game.units.define(unit[0], unit[1]); + um.define_unit(unit[0], unit[1]); } }, diff --git a/src/game/backend/animation/AnimationManager.cpp b/src/game/backend/animation/AnimationManager.cpp index e0f0bb17..2d5e11ba 100644 --- a/src/game/backend/animation/AnimationManager.cpp +++ b/src/game/backend/animation/AnimationManager.cpp @@ -6,6 +6,11 @@ #include "gse/context/Context.h" #include "gse/callable/Native.h" #include "gse/type/Array.h" +#include "gse/type/Float.h" +#include "engine/Engine.h" +#include "loader/sound/SoundLoader.h" +#include "game/backend/animation/FramesRow.h" +#include "game/backend/event/DefineAnimation.h" namespace game { namespace backend { @@ -77,6 +82,56 @@ void AnimationManager::FinishAnimation( const size_t animation_id ) { WRAPIMPL_BEGIN( AnimationManager, CLASS_AM ) WRAPIMPL_PROPS + { + "define_animation", + NATIVE_CALL( this ) { + N_EXPECT_ARGS( 2 ); + N_GETVALUE( id, 0, String ); + N_GETVALUE( animation_def, 1, Object ); + N_GETPROP( type, animation_def, "type", String ); + + if ( type == "frames_row" ) { + N_GETPROP( file, animation_def, "file", String ); + N_GETPROP( row_x, animation_def, "row_x", Int ); + N_GETPROP( row_y, animation_def, "row_y", Int ); + N_GETPROP( frame_width, animation_def, "frame_width", Int ); + N_GETPROP( frame_height, animation_def, "frame_height", Int ); + N_GETPROP_OPT( int64_t, frame_center_x, animation_def, "frame_center_x", Int, frame_width / 2 ); + N_GETPROP_OPT( int64_t, frame_center_y, animation_def, "frame_center_y", Int, frame_height / 2 ); + N_GETPROP( frame_padding, animation_def, "frame_padding", Int ); + N_GETPROP( frames_count, animation_def, "frames_count", Int ); + N_GETPROP_OPT( int64_t, frames_per_row, animation_def, "frames_per_row", Int, frames_count ); + N_GETPROP_OPT( float, scale_x, animation_def, "scale_x", Float, 1.0f ); + N_GETPROP_OPT( float, scale_y, animation_def, "scale_y", Float, 1.0f ); + N_GETPROP( duration_ms, animation_def, "duration_ms", Int ); + N_GETPROP( sound, animation_def, "sound", String ); + if ( !g_engine->GetSoundLoader()->LoadCustomSound( sound ) ) { + GSE_ERROR( gse::EC.GAME_ERROR, "Failed to load animation sound '" + sound + "'" ); + } + auto* def = new animation::FramesRow( + id, + file, + row_x, + row_y, + frame_width, + frame_height, + frame_center_x, + frame_center_y, + frame_padding, + frames_count, + frames_per_row, + scale_x, + scale_y, + duration_ms, + sound + ); + return m_game->AddEvent( new event::DefineAnimation( m_game->GetSlotNum(), def ) ); + } + else { + GSE_ERROR( gse::EC.GAME_ERROR, "Unsupported animation type: " + type ); + } + }) + }, { "show_animation", NATIVE_CALL( this ) { diff --git a/src/game/backend/base/BaseManager.cpp b/src/game/backend/base/BaseManager.cpp index f467768f..17bcd814 100644 --- a/src/game/backend/base/BaseManager.cpp +++ b/src/game/backend/base/BaseManager.cpp @@ -4,6 +4,7 @@ #include "game/backend/State.h" #include "game/backend/bindings/Bindings.h" #include "game/backend/slot/Slots.h" +#include "game/backend/event/DefinePop.h" #include "game/backend/event/SpawnBase.h" #include "game/backend/Player.h" #include "game/backend/faction/Faction.h" @@ -199,6 +200,64 @@ void BaseManager::PushUpdates() { WRAPIMPL_BEGIN( BaseManager, CLASS_BM ) WRAPIMPL_PROPS + { + "define_pop", + NATIVE_CALL( this ) { + N_EXPECT_ARGS( 2 ); + N_GETVALUE( id, 0, String ); + N_GETVALUE( def, 1, Object ); + + N_GETPROP( name, def, "name", String ); + + base::pop_render_infos_t rh = {}; + base::pop_render_infos_t rp = {}; + const auto& f_read_renders = [ &def, &arg, &call_si, &ctx, &getprop_val, &obj_it ]( const std::string& key, base::pop_render_infos_t& out ) { + N_GETPROP( renders, def, key, Array ); + out.reserve( renders.size() ); + for ( const auto& v : renders ) { + if ( v.Get()->type != gse::type::Type::T_OBJECT ) { + GSE_ERROR( gse::EC.INVALID_CALL, "Pop render elements must be objects" ); + } + const auto* obj = (gse::type::Object*)v.Get(); + const auto& ov = obj->value; + N_GETPROP( type, ov, "type", String ); + if ( type == "sprite" ) { + N_GETPROP( file, ov, "file", String ); + N_GETPROP( x, ov, "x", Int ); + N_GETPROP( y, ov, "y", Int ); + N_GETPROP( w, ov, "w", Int ); + N_GETPROP( h, ov, "h", Int ); + out.push_back( + base::pop_render_info_t{ + file, + (uint16_t)x, + (uint16_t)y, + (uint16_t)w, + (uint16_t)h + } + ); + } + else { + GSE_ERROR( gse::EC.INVALID_CALL, "Only sprite pops are supported for now" ); + } + + } + }; + f_read_renders( "renders_human", rh ); + f_read_renders( "renders_progenitor", rp ); + + base::PopDef::pop_flags_t flags = base::PopDef::PF_NONE; + N_GETPROP_OPT( bool, can_work_tiles, def, "tile_worker", Bool, false ); + if ( can_work_tiles ) { + flags |= base::PopDef::PF_TILE_WORKER; + } + + return m_game->AddEvent( new event::DefinePop( + m_game->GetSlotNum(), + new base::PopDef( id, name, rh, rp, flags ) + ) ); + } ) + }, { "spawn_base", NATIVE_CALL( this ) { diff --git a/src/game/backend/bindings/Binding.h b/src/game/backend/bindings/Binding.h index 3fdbc94d..92c6f267 100644 --- a/src/game/backend/bindings/Binding.h +++ b/src/game/backend/bindings/Binding.h @@ -52,14 +52,6 @@ class Binding { #define GAME m_bindings->GetGame( ctx, call_si ) -#define CALLBACK( _type ) NATIVE_CALL( this ) { \ - N_EXPECT_ARGS( 1 ); \ - N_GET( cb, 0 ); \ - N_CHECKARG( cb.Get(), 0, Callable ); \ - m_bindings->SetCallback( _type, cb, ctx, call_si ); \ - return VALUE( gse::type::Undefined ); \ -}) - namespace game { namespace backend { namespace bindings { diff --git a/src/game/backend/bindings/Bindings.cpp b/src/game/backend/bindings/Bindings.cpp index 5470236d..5db24e6d 100644 --- a/src/game/backend/bindings/Bindings.cpp +++ b/src/game/backend/bindings/Bindings.cpp @@ -80,39 +80,6 @@ void Bindings::RunMain() { m_gse->GetInclude( m_gse_context, m_si_internal, m_entry_script ); } -gse::Value Bindings::Call( const callback_slot_t slot, const callback_arguments_t& arguments ) { - const auto& it = m_callbacks.find( slot ); - if ( it != m_callbacks.end() ) { - try { - gse::type::object_properties_t properties = arguments; - const gse::Value result = ( (gse::type::Callable*)it->second.Get() )->Run( - m_gse_context, m_si_internal, { - VALUE( gse::type::Object, properties ), - } - ); - auto* game = m_state->GetGame(); - if ( game ) { - game->GetUM()->PushUpdates(); - game->GetBM()->PushUpdates(); - } - if ( result.Get()->type == gse::type::Type::T_NOTHING ) { - // return undefined by default - return VALUE( gse::type::Undefined ); - } - return result; - } - catch ( gse::Exception& e ) { - if ( m_state->m_on_gse_error ) { - m_state->m_on_gse_error( e ); - } - else { - throw std::runtime_error( e.ToStringAndCleanup() ); - } - } - } - return VALUE( gse::type::Undefined ); -} - const gse::Value Bindings::Trigger( gse::Wrappable* object, const std::string& event, const gse::type::object_properties_t& args ) { auto result = VALUE( gse::type::Undefined ); try { @@ -150,13 +117,6 @@ Game* Bindings::GetGame( GSE_CALLABLE ) const { return game; } -void Bindings::SetCallback( const callback_slot_t slot, const gse::Value& callback, gse::context::Context* context, const gse::si_t& si ) { - if ( m_callbacks.find( slot ) != m_callbacks.end() ) { - throw gse::Exception( gse::EC.GAME_ERROR, "Callback slot already in use", context, si ); - } - m_callbacks.insert_or_assign( slot, callback ); -} - } } } diff --git a/src/game/backend/bindings/Bindings.h b/src/game/backend/bindings/Bindings.h index a898b264..13904251 100644 --- a/src/game/backend/bindings/Bindings.h +++ b/src/game/backend/bindings/Bindings.h @@ -40,31 +40,11 @@ class Bindings : public gse::Bindings { void RunMain(); - enum callback_slot_t { - CS_ON_CONFIGURE, - CS_ON_START, - CS_ON_TURN, - CS_ON_GET_TILE_YIELDS, - CS_ON_UNIT_SPAWN, - CS_ON_UNIT_DESPAWN, - CS_ON_UNIT_MOVE_VALIDATE, - CS_ON_UNIT_MOVE_RESOLVE, - CS_ON_UNIT_MOVE_APPLY, - CS_ON_UNIT_ATTACK_VALIDATE, - CS_ON_UNIT_ATTACK_RESOLVE, - CS_ON_UNIT_ATTACK_APPLY, - CS_ON_UNIT_TURN, - CS_ON_BASE_SPAWN, - CS_ON_BASE_TURN, - }; typedef std::map< std::string, gse::Value > callback_arguments_t; - gse::Value Call( const callback_slot_t slot, const callback_arguments_t& arguments = {} ); - const gse::Value Trigger( gse::Wrappable* object, const std::string& event, const gse::type::object_properties_t& args ); State* GetState() const; Game* GetGame( GSE_CALLABLE ) const; - void SetCallback( const callback_slot_t slot, const gse::Value& callback, gse::context::Context* context, const gse::si_t& si ); private: @@ -73,8 +53,6 @@ class Bindings : public gse::Bindings { const gse::si_t m_si_internal = { "" }; const gse::type::function_arguments_t m_no_arguments = {}; - std::unordered_map< callback_slot_t, gse::Value > m_callbacks = {}; - State* m_state = nullptr; const std::string m_entry_script; diff --git a/src/game/backend/bindings/On.cpp b/src/game/backend/bindings/On.cpp index 5203622e..0049dac4 100644 --- a/src/game/backend/bindings/On.cpp +++ b/src/game/backend/bindings/On.cpp @@ -14,19 +14,6 @@ BINDING_IMPL( on ) { #define ON( _property, _cbtype ) { _property, CALLBACK( Bindings::_cbtype ) } const gse::type::object_properties_t properties = { - ON( "configure", CS_ON_CONFIGURE ), - ON( "start", CS_ON_START ), - ON( "turn", CS_ON_TURN ), - ON( "get_tile_yields", CS_ON_GET_TILE_YIELDS ), - ON( "unit_spawn", CS_ON_UNIT_SPAWN ), - ON( "unit_despawn", CS_ON_UNIT_DESPAWN ), - ON( "unit_move_validate", CS_ON_UNIT_MOVE_VALIDATE ), - ON( "unit_move_resolve", CS_ON_UNIT_MOVE_RESOLVE ), - ON( "unit_move_apply", CS_ON_UNIT_MOVE_APPLY ), - ON( "unit_attack_validate", CS_ON_UNIT_ATTACK_VALIDATE ), - ON( "unit_attack_resolve", CS_ON_UNIT_ATTACK_RESOLVE ), - ON( "unit_attack_apply", CS_ON_UNIT_ATTACK_APPLY ), - ON( "unit_turn", CS_ON_UNIT_TURN ), }; #undef ON return VALUE( gse::type::Object, properties ); diff --git a/src/game/backend/faction/Faction.cpp b/src/game/backend/faction/Faction.cpp index c503a081..0e9442ae 100644 --- a/src/game/backend/faction/Faction.cpp +++ b/src/game/backend/faction/Faction.cpp @@ -77,9 +77,13 @@ WRAPIMPL_BEGIN( Faction, CLASS_FACTION ) "name", VALUE( gse::type::String, m_name ) }, + { + "is_naval", + VALUE( gse::type::Bool, m_flags & Faction::FF_NAVAL ) + }, { "is_progenitor", - VALUE( gse::type::Bool, ( m_flags & Faction::FF_PROGENITOR ) == Faction::FF_PROGENITOR ) + VALUE( gse::type::Bool, m_flags & Faction::FF_PROGENITOR ) }, }; WRAPIMPL_END_PTR( Faction ) diff --git a/src/game/backend/faction/Faction.h b/src/game/backend/faction/Faction.h index 35e2a04f..b06120eb 100644 --- a/src/game/backend/faction/Faction.h +++ b/src/game/backend/faction/Faction.h @@ -20,8 +20,8 @@ CLASS2( Faction, types::Serializable, gse::Wrappable ) typedef uint8_t faction_flag_t; static constexpr Faction::faction_flag_t FF_NONE = 0; - static constexpr Faction::faction_flag_t FF_PROGENITOR = 1 << 0; - static constexpr Faction::faction_flag_t FF_NAVAL = 1 << 1; + static constexpr Faction::faction_flag_t FF_NAVAL = 1 << 0; + static constexpr Faction::faction_flag_t FF_PROGENITOR = 1 << 1; std::string m_id = ""; std::string m_name = ""; diff --git a/src/game/backend/faction/FactionManager.cpp b/src/game/backend/faction/FactionManager.cpp index a2008730..b2db7ada 100644 --- a/src/game/backend/faction/FactionManager.cpp +++ b/src/game/backend/faction/FactionManager.cpp @@ -7,6 +7,12 @@ #include "gse/type/Float.h" #include "gse/type/Array.h" +#include "engine/Engine.h" +#include "loader/txt/TXTLoaders.h" +#include "loader/txt/FactionTXTLoader.h" +#include "loader/texture/TextureLoader.h" +#include "types/texture/Texture.h" + namespace game { namespace backend { namespace faction { @@ -64,12 +70,52 @@ const std::vector< Faction* > FactionManager::GetAll() const { WRAPIMPL_BEGIN( FactionManager, CLASS_FM ) WRAPIMPL_PROPS + { + "import_base_names", + NATIVE_CALL() { + N_EXPECT_ARGS( 1 ); + N_GETVALUE( filename, 0, String ); + const auto& data = g_engine->GetTXTLoaders()->factions->GetFactionData( filename ); + std::vector< gse::Value > land_names = {}; + land_names.reserve( data.bases_names.land.size() ); + for ( const auto& name : data.bases_names.land ) { + land_names.push_back( VALUE( gse::type::String, name ) ); + } + std::vector< gse::Value > water_names = {}; + water_names.reserve( data.bases_names.water.size() ); + for ( const auto& name : data.bases_names.water ) { + water_names.push_back( VALUE( gse::type::String, name ) ); + } + const auto properties = gse::type::object_properties_t{ + { "land", VALUE( gse::type::Array, land_names ) }, + { "water", VALUE( gse::type::Array, water_names ) }, + }; + return VALUE( gse::type::Object, properties ); + } ) + }, + { + "import_colors", + NATIVE_CALL() { + N_EXPECT_ARGS( 1 ); + N_GETVALUE( filename, 0, String ); + const auto* texture = g_engine->GetTextureLoader()->LoadCustomTexture( filename ); + const auto properties = gse::type::object_properties_t{ + { "faction", types::Color::FromRGBA( texture->GetPixel( 4, 739 ) ).Wrap() }, + { "faction_shadow", types::Color::FromRGBA( texture->GetPixel( 4, 747 ) ).Wrap() }, + { "text", types::Color::FromRGBA( texture->GetPixel( 4, 755 ) ).Wrap() }, + { "text_shadow", types::Color::FromRGBA( texture->GetPixel( 4, 763 ) ).Wrap() }, + { "border", types::Color::FromRGBA( texture->GetPixel( 161, 749 ) ).Wrap() }, + { "border_alpha", types::Color::FromRGBA( texture->GetPixel( 161, 757 ) ).Wrap() }, + { "vehicle", types::Color::FromRGBA( texture->GetPixel( 435, 744 ) ).Wrap() }, + }; + return VALUE( gse::type::Object, properties ); + } ) + }, { "add", NATIVE_CALL( this ) { N_EXPECT_ARGS( 2 ); N_GETVALUE( id, 0, String ); - N_GETVALUE( faction_def, 1, Object ); N_GETPROP( name, faction_def, "name", String ); diff --git a/src/game/backend/unit/UnitManager.cpp b/src/game/backend/unit/UnitManager.cpp index 346fcc55..af71600f 100644 --- a/src/game/backend/unit/UnitManager.cpp +++ b/src/game/backend/unit/UnitManager.cpp @@ -10,8 +10,11 @@ #include "game/backend/State.h" #include "game/backend/bindings/Bindings.h" #include "game/backend/slot/Slots.h" +#include "game/backend/event/DefineMorales.h" +#include "game/backend/event/DefineUnit.h" #include "game/backend/event/SpawnUnit.h" #include "game/backend/event/DespawnUnit.h" +#include "game/backend/unit/SpriteRender.h" #include "gse/context/Context.h" #include "gse/callable/Native.h" @@ -253,6 +256,99 @@ void UnitManager::PushUpdates() { WRAPIMPL_BEGIN( UnitManager, CLASS_UM ) WRAPIMPL_PROPS + { + "define_morales", + NATIVE_CALL( this ) { + N_EXPECT_ARGS( 2 ); + N_GETVALUE( id, 0, String ); + N_GETVALUE( arr, 1, Array ); + const uint8_t expected_count = unit::MORALE_MAX - unit::MORALE_MIN + 1; + if ( arr.size() != expected_count ) { + GSE_ERROR( gse::EC.INVALID_CALL, "Morale set must have exactly " + std::to_string( expected_count ) + " values (found " + std::to_string( arr.size() ) + ")"); + } + unit::MoraleSet::morale_values_t values = {}; + for ( const auto& v : arr ) { + if ( v.Get()->type != gse::type::Type::T_OBJECT ) { + GSE_ERROR( gse::EC.INVALID_CALL, "Morale set elements must be objects"); + } + const auto* obj = (gse::type::Object*)v.Get(); + N_GETPROP( name, obj->value, "name", String ); + values.push_back( unit::Morale{ name } ); + } + return m_game->AddEvent( new event::DefineMorales( m_game->GetSlotNum(), new unit::MoraleSet( id, values ) ) ); + } ) + }, + { + "define_unit", + NATIVE_CALL( this ) { + N_EXPECT_ARGS( 2 ); + N_GETVALUE( id, 0, String ); + N_GETVALUE( unit_def, 1, Object ); + N_GETPROP( name, unit_def, "name", String ); + N_GETPROP( morale, unit_def, "morale", String ); + N_GETPROP( unit_type, unit_def, "type", String ); + if ( unit_type == "static" ) { + N_GETPROP( movement_type_str, unit_def, "movement_type", String ); + unit::movement_type_t movement_type; + if ( movement_type_str == "land" ) { + movement_type = unit::MT_LAND; + } + else if ( movement_type_str == "water" ) { + movement_type = unit::MT_WATER; + } + else if ( movement_type_str == "air" ) { + movement_type = unit::MT_AIR; + } + else if ( movement_type_str == "immovable" ) { + movement_type = unit::MT_IMMOVABLE; + } + else { + GSE_ERROR( gse::EC.INVALID_CALL, "Invalid movement type: " + movement_type_str + ". Specify one of: land water air immovable"); + } + N_GETPROP( movement_per_turn, unit_def, "movement_per_turn", Int ); + N_GETPROP( render_def, unit_def, "render", Object ); + N_GETPROP( render_type, render_def, "type", String ); + if ( render_type == "sprite" ) { + N_GETPROP( sprite_file, render_def, "file", String ); + N_GETPROP( sprite_x, render_def, "x", Int ); + N_GETPROP( sprite_y, render_def, "y", Int ); + N_GETPROP( sprite_w, render_def, "w", Int ); + N_GETPROP( sprite_h, render_def, "h", Int ); + N_GETPROP( sprite_cx, render_def, "cx", Int ); + N_GETPROP( sprite_cy, render_def, "cy", Int ); + N_GETPROP_OPT_INT( sprite_morale_based_xshift, render_def, "morale_based_xshift" ); + const auto* moraleset = m_game->GetUM()->GetMoraleSet( morale ); + if ( !moraleset ) { + GSE_ERROR( gse::EC.INVALID_CALL, "Morale type '" + morale + "' is not defined"); + } + auto* def = new unit::StaticDef( + id, + moraleset, + name, + movement_type, + movement_per_turn, + new unit::SpriteRender( + sprite_file, + sprite_x, + sprite_y, + sprite_w, + sprite_h, + sprite_cx, + sprite_cy, + sprite_morale_based_xshift + ) + ); + return m_game->AddEvent( new event::DefineUnit( m_game->GetSlotNum(), def ) ); + } + else { + GSE_ERROR( gse::EC.GAME_ERROR, "Unsupported render type: " + render_type ); + } + } + else { + GSE_ERROR( gse::EC.GAME_ERROR, "Unsupported unit type: " + unit_type ); + } + }) + }, { "spawn_unit", NATIVE_CALL( this ) { diff --git a/src/gse/builtins/String.cpp b/src/gse/builtins/String.cpp index 50586ece..3e477feb 100644 --- a/src/gse/builtins/String.cpp +++ b/src/gse/builtins/String.cpp @@ -13,14 +13,14 @@ namespace builtins { void String::AddToContext( context::Context* ctx ) { - ctx->CreateBuiltin( "to_uppercase", NATIVE_CALL() { + ctx->CreateBuiltin( "uppercase", NATIVE_CALL() { N_EXPECT_ARGS( 1 ); N_GETVALUE_NONCONST( text, 0, String ); std::transform( text.begin(), text.end(), text.begin(), ::toupper ); return VALUE( type::String, text ); } ) ); - ctx->CreateBuiltin( "to_lowercase", NATIVE_CALL() { + ctx->CreateBuiltin( "lowercase", NATIVE_CALL() { N_EXPECT_ARGS( 1 ); N_GETVALUE_NONCONST( text, 0, String ); std::transform( text.begin(), text.end(), text.begin(), ::tolower ); diff --git a/src/gse/callable/Native.h b/src/gse/callable/Native.h index a6966a7b..4f4a223f 100644 --- a/src/gse/callable/Native.h +++ b/src/gse/callable/Native.h @@ -57,12 +57,12 @@ namespace callable { ctx->UnpersistValue( _callable ); #define N_GETVALUE_NONCONST( _var, _index, _type ) \ ASSERT_NOLOG( _index < arguments.size(), "argument index overflow" ); \ - arg = arguments.at( _index ).Get(); \ + arg = arguments.at( _index ).Get()->Deref(); \ N_CHECKARG( arg, _index, _type ); \ auto _var = ((gse::type::_type*)arg)->value; #define N_GETELEMENT( _var, _arr, _index, _type ) \ ASSERT_NOLOG( _index < _arr.size(), #_arr " index overflow" ); \ - arg = _arr.at( _index ).Get(); \ + arg = _arr.at( _index ).Get()->Deref(); \ N_CHECKARG( arg, _index, _type ); \ const auto& _var = ((gse::type::_type*)arg)->value; #define N_GETVALUE( _var, _index, _type ) \ @@ -79,7 +79,7 @@ namespace callable { auto* _var = _type::Unwrap( obj_val ); #define N_GETVALUE_OBJ( _index, _class ) \ ASSERT_NOLOG( _index < arguments.size(), "argument index overflow" ); \ - arg = arguments.at( _index ).Get(); \ + arg = arguments.at( _index ).Get()->Deref(); \ N_CHECKARG( arg, _index, Object ); \ if ( ((gse::type::Object*)arg)->object_class != _class ) { \ throw gse::Exception( gse::EC.INVALID_CALL, "Argument " + std::to_string( _index ) + " is expected to be object of class " + gse::type::Object::GetClassString( _class ) + ", found class: " + gse::type::Object::GetClassString( ((gse::type::Object*)arg)->object_class ), ctx, call_si ); \ @@ -92,12 +92,6 @@ namespace callable { if ( ((gse::type::Object*)_var)->object_class != _class ) { \ throw gse::Exception( gse::EC.INVALID_CALL, "Value is expected to be object of class " + gse::type::Object::GetClassString( _class ) + ", found class: " + gse::type::Object::GetClassString( ((gse::type::Object*)_var)->object_class ), ctx, call_si ); \ } -#define N_GETOBJECT( _var, _index, _class ) \ - ASSERT_NOLOG( _index < arguments.size(), "argument index overflow" ); \ - arg = arguments.at( _index ).Get(); \ - N_CHECKARG( arg, _index, Object ); \ - N_CHECK_OBJECT_CLASS( arg, _class ); \ - const auto& _var = ((gse::type::Object*)arg)->value; #define N_GETPROP_VAL( _obj, _key, _type ) \ obj_it = _obj.find( _key ); \ if ( obj_it == _obj.end() ) { \ @@ -106,13 +100,13 @@ namespace callable { getprop_val = obj_it->second; #define N_GETPROP_ARG( _obj, _key, _type ) \ N_GETPROP_VAL( _obj, _key, _type ); \ - arg = getprop_val.Get(); \ + arg = getprop_val.Get()->Deref(); \ if ( arg->type != gse::type::_type::GetType() ) { \ throw gse::Exception( gse::EC.INVALID_CALL, (std::string)"Property '" + _key + "' is expected to be " + #_type + ", found: " + arg->GetTypeString( arg->type ), ctx, call_si ); \ } #define N_GETPROP_UNWRAP( _var, _obj, _key, _type ) \ N_GETPROP_VAL( _obj, _key, Object ); \ - N_CHECK_OBJECT_CLASS( getprop_val.Get(), _type::WRAP_CLASS ); \ + N_CHECK_OBJECT_CLASS( getprop_val.Get()->Deref(), _type::WRAP_CLASS ); \ const auto _var = _type::Unwrap( getprop_val ); #define N_GETPROP( _var, _obj, _key, _type ) \ N_GETPROP_ARG( _obj, _key, _type ); \ @@ -126,7 +120,7 @@ namespace callable { obj_it = _obj.find( _key ); \ if ( obj_it != _obj.end() ) { \ getprop_val = obj_it->second; \ - arg = getprop_val.Get(); \ + arg = getprop_val.Get()->Deref(); \ if ( arg->type != gse::type::_type::GetType() ) { \ throw gse::Exception( gse::EC.INVALID_CALL, (std::string)"Property '" + _key + "' is expected to be " + #_type + ", found: " + arg->GetTypeString( arg->type ), ctx, call_si ); \ } \ From c8354d6597b4cb575631596a07b63fb7b81283e4 Mon Sep 17 00:00:00 2001 From: afwbkbc Date: Sat, 16 Nov 2024 17:24:19 +0200 Subject: [PATCH 10/13] renamed entrypoint to #main, removed legacy bindings --- GLSMAC_data/default/main.gls.js | 6 +- src/game/backend/{bindings => }/Bindings.cpp | 49 ++--- src/game/backend/{bindings => }/Bindings.h | 9 +- src/game/backend/CMakeLists.txt | 5 +- src/game/backend/Game.cpp | 2 +- src/game/backend/State.cpp | 20 +- src/game/backend/State.h | 10 +- src/game/backend/System.cpp | 15 -- src/game/backend/System.h | 17 -- src/game/backend/base/BaseManager.cpp | 2 +- src/game/backend/bindings/Animations.cpp | 100 ---------- src/game/backend/bindings/Bases.cpp | 116 ------------ src/game/backend/bindings/Binding.cpp | 21 --- src/game/backend/bindings/Binding.h | 86 --------- src/game/backend/bindings/CMakeLists.txt | 18 -- src/game/backend/bindings/Exit.cpp | 31 --- src/game/backend/bindings/Factions.cpp | 77 -------- src/game/backend/bindings/Map.cpp | 66 ------- src/game/backend/bindings/Message.cpp | 25 --- src/game/backend/bindings/On.cpp | 24 --- src/game/backend/bindings/Players.cpp | 41 ---- src/game/backend/bindings/Random.cpp | 55 ------ src/game/backend/bindings/Resources.cpp | 89 --------- src/game/backend/bindings/Tiles.cpp | 56 ------ src/game/backend/bindings/Units.cpp | 178 ------------------ src/game/backend/resource/ResourceManager.cpp | 2 +- src/game/backend/unit/UnitManager.cpp | 2 +- 27 files changed, 33 insertions(+), 1089 deletions(-) rename src/game/backend/{bindings => }/Bindings.cpp (76%) rename src/game/backend/{bindings => }/Bindings.h (86%) delete mode 100644 src/game/backend/System.cpp delete mode 100644 src/game/backend/System.h delete mode 100644 src/game/backend/bindings/Animations.cpp delete mode 100644 src/game/backend/bindings/Bases.cpp delete mode 100644 src/game/backend/bindings/Binding.cpp delete mode 100644 src/game/backend/bindings/Binding.h delete mode 100644 src/game/backend/bindings/CMakeLists.txt delete mode 100644 src/game/backend/bindings/Exit.cpp delete mode 100644 src/game/backend/bindings/Factions.cpp delete mode 100644 src/game/backend/bindings/Map.cpp delete mode 100644 src/game/backend/bindings/Message.cpp delete mode 100644 src/game/backend/bindings/On.cpp delete mode 100644 src/game/backend/bindings/Players.cpp delete mode 100644 src/game/backend/bindings/Random.cpp delete mode 100644 src/game/backend/bindings/Resources.cpp delete mode 100644 src/game/backend/bindings/Tiles.cpp delete mode 100644 src/game/backend/bindings/Units.cpp diff --git a/GLSMAC_data/default/main.gls.js b/GLSMAC_data/default/main.gls.js index f306d581..e7d0ca71 100644 --- a/GLSMAC_data/default/main.gls.js +++ b/GLSMAC_data/default/main.gls.js @@ -3,11 +3,11 @@ const resources = #include('resources'); const units = #include('units'); const bases = #include('bases'); -#system.on('configure', (e) => { +#main((gm) => { - factions.configure(e.gm.fm); + factions.configure(gm.fm); - e.gm.on('start', (e) => { + gm.on('start', (e) => { let players = []; let players_sz = 0; diff --git a/src/game/backend/bindings/Bindings.cpp b/src/game/backend/Bindings.cpp similarity index 76% rename from src/game/backend/bindings/Bindings.cpp rename to src/game/backend/Bindings.cpp index 5db24e6d..14160932 100644 --- a/src/game/backend/bindings/Bindings.cpp +++ b/src/game/backend/Bindings.cpp @@ -1,29 +1,23 @@ #include "Bindings.h" -#include - #include "util/FS.h" #include "game/backend/Game.h" #include "game/backend/unit/UnitManager.h" #include "game/backend/base/BaseManager.h" -#include "Binding.h" #include "gse/GSE.h" #include "gse/context/GlobalContext.h" #include "gse/Exception.h" #include "gse/type/String.h" -#include "gse/type/Object.h" #include "gse/type/Callable.h" #include "gse/type/Undefined.h" #include "gse/callable/Native.h" #include "engine/Engine.h" #include "config/Config.h" #include "game/backend/State.h" -#include "game/backend/System.h" namespace game { namespace backend { -namespace bindings { Bindings::Bindings( State* state ) : m_state( state ) @@ -41,45 +35,35 @@ Bindings::Bindings( State* state ) m_gse_context = m_gse->CreateGlobalContext(); m_gse_context->IncRefs(); -#define B( _name ) new BINDING( _name )( this ) - m_bindings = { - B( message ), - B( exit ), - B( random ), - B( on ), - B( players ), - B( factions ), - B( tiles ), - B( units ), - B( bases ), - B( animations ), - B( map ), - B( resources ), - }; -#undef B } Bindings::~Bindings() { - for ( auto& it : m_bindings ) { - delete it; - } m_gse_context->DecRefs(); DELETE( m_gse ); } void Bindings::AddToContext( gse::context::Context* ctx ) { - gse::type::object_properties_t methods = {}; - for ( auto& it : m_bindings ) { - it->Add( methods ); - } - ctx->CreateBuiltin( "game", VALUE( gse::type::Object, methods ) ); - ctx->CreateBuiltin( "system", m_state->GetSystem()->Wrap( true ) ); + ctx->CreateBuiltin( "main", NATIVE_CALL(this) { + N_EXPECT_ARGS( 1 ); + const auto& main = arguments.at(0); + N_CHECKARG( main.Get(), 0, Callable ); + m_main_callables.push_back( main ); + return VALUE( gse::type::Undefined ); + } ) ); } -void Bindings::RunMain() { +void Bindings::RunMainScript() { m_gse->GetInclude( m_gse_context, m_si_internal, m_entry_script ); } +void Bindings::RunMain() { + for ( const auto& main : m_main_callables ) { + ASSERT_NOLOG( main.Get()->type == gse::type::Type::T_CALLABLE, "main not callable" ); + auto gm = m_state->Wrap(); + ((gse::type::Callable*)main.Get())->Run( m_gse_context, m_si_internal, { gm }); + } +} + const gse::Value Bindings::Trigger( gse::Wrappable* object, const std::string& event, const gse::type::object_properties_t& args ) { auto result = VALUE( gse::type::Undefined ); try { @@ -119,4 +103,3 @@ Game* Bindings::GetGame( GSE_CALLABLE ) const { } } -} diff --git a/src/game/backend/bindings/Bindings.h b/src/game/backend/Bindings.h similarity index 86% rename from src/game/backend/bindings/Bindings.h rename to src/game/backend/Bindings.h index 13904251..e059151c 100644 --- a/src/game/backend/bindings/Bindings.h +++ b/src/game/backend/Bindings.h @@ -27,10 +27,6 @@ class System; class Game; class State; -namespace bindings { - -class Binding; - class Bindings : public gse::Bindings { public: Bindings( State* state ); @@ -38,9 +34,9 @@ class Bindings : public gse::Bindings { void AddToContext( gse::context::Context* ctx ) override; + void RunMainScript(); void RunMain(); - typedef std::map< std::string, gse::Value > callback_arguments_t; const gse::Value Trigger( gse::Wrappable* object, const std::string& event, const gse::type::object_properties_t& args ); State* GetState() const; @@ -48,7 +44,7 @@ class Bindings : public gse::Bindings { private: - std::vector< Binding* > m_bindings = {}; + std::vector< gse::Value > m_main_callables = {}; const gse::si_t m_si_internal = { "" }; const gse::type::function_arguments_t m_no_arguments = {}; @@ -63,4 +59,3 @@ class Bindings : public gse::Bindings { } } -} diff --git a/src/game/backend/CMakeLists.txt b/src/game/backend/CMakeLists.txt index 297805ed..3241d0f4 100644 --- a/src/game/backend/CMakeLists.txt +++ b/src/game/backend/CMakeLists.txt @@ -9,16 +9,15 @@ SUBDIR( faction ) SUBDIR( animation ) SUBDIR( unit ) SUBDIR( base ) -SUBDIR( bindings ) SUBDIR( event ) SUBDIR( turn ) SET( SRC ${SRC} ${PWD}/Random.cpp - ${PWD}/System.cpp - ${PWD}/Game.cpp + ${PWD}/Bindings.cpp ${PWD}/State.cpp + ${PWD}/Game.cpp ${PWD}/Account.cpp ${PWD}/Player.cpp ${PWD}/MapObject.cpp diff --git a/src/game/backend/Game.cpp b/src/game/backend/Game.cpp index 18b70d19..7402b539 100644 --- a/src/game/backend/Game.cpp +++ b/src/game/backend/Game.cpp @@ -32,7 +32,7 @@ #include "map/tile/Tiles.h" #include "map/MapState.h" #include "resource/ResourceManager.h" -#include "bindings/Bindings.h" +#include "Bindings.h" #include "graphics/Graphics.h" #include "animation/Def.h" #include "unit/Def.h" diff --git a/src/game/backend/State.cpp b/src/game/backend/State.cpp index e26b87bd..b286b536 100644 --- a/src/game/backend/State.cpp +++ b/src/game/backend/State.cpp @@ -1,10 +1,9 @@ #include "State.h" -#include "System.h" #include "game/backend/faction/FactionManager.h" #include "Game.h" #include "game/backend/connection/Connection.h" -#include "game/backend/bindings/Bindings.h" +#include "Bindings.h" #include "game/backend/slot/Slots.h" #include "Player.h" @@ -13,7 +12,6 @@ namespace backend { State::State() : m_slots( new slot::Slots( this ) ) { - NEW( m_system, System ); NEW( m_fm, faction::FactionManager ); } @@ -23,7 +21,6 @@ State::~State() { delete m_bindings; } delete m_slots; - DELETE( m_system ); DELETE( m_fm ); } @@ -115,9 +112,9 @@ connection::Connection* State::GetConnection() const { void State::InitBindings() { if ( !m_bindings ) { Log( "Initializing bindings" ); - m_bindings = new bindings::Bindings( this ); + m_bindings = new Bindings( this ); try { - m_bindings->RunMain(); + m_bindings->RunMainScript(); } catch ( gse::Exception& err ) { if ( m_game ) { @@ -137,12 +134,7 @@ void State::Configure() { m_fm->Clear(); - m_bindings->Trigger( m_system, "configure", { - { - "gm", - Wrap() - } - }); + m_bindings->RunMain(); if ( m_fm->GetAll().empty() ) { THROW( "no factions were defined" ); @@ -176,10 +168,6 @@ void State::DetachConnection() { m_connection = nullptr; } -System* State::GetSystem() const { - return m_system; -} - faction::FactionManager* State::GetFM() const { return m_fm; } diff --git a/src/game/backend/State.h b/src/game/backend/State.h index 5a218ed3..dbff12f0 100644 --- a/src/game/backend/State.h +++ b/src/game/backend/State.h @@ -14,21 +14,17 @@ namespace game { namespace backend { -class System; - namespace faction { class FactionManager; } +class Bindings; class Game; class Player; namespace connection { class Connection; } -namespace bindings { -class Bindings; -} namespace slot { class Slots; } @@ -45,7 +41,7 @@ CLASS2( State, common::Class, gse::Wrappable ) settings::Settings m_settings = {}; slot::Slots* m_slots; - bindings::Bindings* m_bindings = nullptr; + Bindings* m_bindings = nullptr; std::function< void( gse::Exception& ) > m_on_gse_error = nullptr; @@ -70,7 +66,6 @@ CLASS2( State, common::Class, gse::Wrappable ) void Reset(); void DetachConnection(); - System* GetSystem() const; faction::FactionManager* GetFM() const; WRAPDEFS_PTR( State ) @@ -79,7 +74,6 @@ CLASS2( State, common::Class, gse::Wrappable ) void Unserialize( types::Buffer buf ); private: - System* m_system = nullptr; faction::FactionManager* m_fm = nullptr; Game* m_game = nullptr; diff --git a/src/game/backend/System.cpp b/src/game/backend/System.cpp deleted file mode 100644 index 634988cd..00000000 --- a/src/game/backend/System.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#include "System.h" - -namespace game { -namespace backend { - -WRAPIMPL_BEGIN( System, CLASS_SYSTEM ) - WRAPIMPL_PROPS - - }; -WRAPIMPL_END_PTR( System ) - -UNWRAPIMPL_PTR( System ) - -} -} diff --git a/src/game/backend/System.h b/src/game/backend/System.h deleted file mode 100644 index 3610b843..00000000 --- a/src/game/backend/System.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include "gse/Wrappable.h" -#include "gse/type/Object.h" - -namespace game { -namespace backend { - -class System : public gse::Wrappable { -public: - - WRAPDEFS_PTR( System ) - -}; - -} -} diff --git a/src/game/backend/base/BaseManager.cpp b/src/game/backend/base/BaseManager.cpp index 17bcd814..b3667e81 100644 --- a/src/game/backend/base/BaseManager.cpp +++ b/src/game/backend/base/BaseManager.cpp @@ -2,7 +2,7 @@ #include "game/backend/Game.h" #include "game/backend/State.h" -#include "game/backend/bindings/Bindings.h" +#include "game/backend/Bindings.h" #include "game/backend/slot/Slots.h" #include "game/backend/event/DefinePop.h" #include "game/backend/event/SpawnBase.h" diff --git a/src/game/backend/bindings/Animations.cpp b/src/game/backend/bindings/Animations.cpp deleted file mode 100644 index 602ff1b1..00000000 --- a/src/game/backend/bindings/Animations.cpp +++ /dev/null @@ -1,100 +0,0 @@ -#include "Binding.h" - -#include "gse/context/Context.h" -#include "gse/callable/Native.h" -#include "gse/Exception.h" -#include "gse/type/Int.h" -#include "gse/type/Float.h" -#include "gse/type/String.h" -#include "gse/type/Object.h" -#include "gse/type/Undefined.h" -#include "game/backend/Game.h" -#include "game/backend/animation/AnimationManager.h" -#include "Bindings.h" -#include "game/backend/event/DefineAnimation.h" -#include "game/backend/animation/FramesRow.h" -#include "engine/Engine.h" -#include "loader/sound/SoundLoader.h" - -namespace game { -namespace backend { -namespace bindings { - -BINDING_IMPL( animations ) { - const gse::type::object_properties_t properties = { - { - "define", - NATIVE_CALL( this ) { - N_EXPECT_ARGS( 2 ); - N_GETVALUE( id, 0, String ); - N_GETVALUE( animation_def, 1, Object ); - N_GETPROP( type, animation_def, "type", String ); - - if ( type == "frames_row" ) { - N_GETPROP( file, animation_def, "file", String ); - N_GETPROP( row_x, animation_def, "row_x", Int ); - N_GETPROP( row_y, animation_def, "row_y", Int ); - N_GETPROP( frame_width, animation_def, "frame_width", Int ); - N_GETPROP( frame_height, animation_def, "frame_height", Int ); - N_GETPROP_OPT( int64_t, frame_center_x, animation_def, "frame_center_x", Int, frame_width / 2 ); - N_GETPROP_OPT( int64_t, frame_center_y, animation_def, "frame_center_y", Int, frame_height / 2 ); - N_GETPROP( frame_padding, animation_def, "frame_padding", Int ); - N_GETPROP( frames_count, animation_def, "frames_count", Int ); - N_GETPROP_OPT( int64_t, frames_per_row, animation_def, "frames_per_row", Int, frames_count ); - N_GETPROP_OPT( float, scale_x, animation_def, "scale_x", Float, 1.0f ); - N_GETPROP_OPT( float, scale_y, animation_def, "scale_y", Float, 1.0f ); - N_GETPROP( duration_ms, animation_def, "duration_ms", Int ); - N_GETPROP( sound, animation_def, "sound", String ); - if ( !g_engine->GetSoundLoader()->LoadCustomSound( sound ) ) { - GSE_ERROR( gse::EC.GAME_ERROR, "Failed to load animation sound '" + sound + "'" ); - } - auto* def = new animation::FramesRow( - id, - file, - row_x, - row_y, - frame_width, - frame_height, - frame_center_x, - frame_center_y, - frame_padding, - frames_count, - frames_per_row, - scale_x, - scale_y, - duration_ms, - sound - ); - auto* game = GAME; - return game->AddEvent( new event::DefineAnimation( game->GetSlotNum(), def ) ); - } - else { - GSE_ERROR( gse::EC.GAME_ERROR, "Unsupported animation type: " + type ); - } - }) - }, - { - "show_on_tile", - NATIVE_CALL( this ) { - N_EXPECT_ARGS( 3 ); - N_GETVALUE( id, 0, String ); - N_GETVALUE_UNWRAP( tile, 1, map::tile::Tile ); - N_PERSIST_CALLABLE( on_complete, 2 ); - const auto* errmsg = GAME->GetAM()->ShowAnimation( id, tile, [ on_complete, ctx, call_si ]() { - on_complete->Run( ctx, call_si, {} ); - N_UNPERSIST_CALLABLE( on_complete ); - }); - if ( errmsg ) { - GSE_ERROR( gse::EC.GAME_ERROR, *errmsg ); - delete errmsg; - } - return VALUE( gse::type::Undefined ); - } ) - } - }; - return VALUE( gse::type::Object, properties ); -} - -} -} -} diff --git a/src/game/backend/bindings/Bases.cpp b/src/game/backend/bindings/Bases.cpp deleted file mode 100644 index fd4caf0c..00000000 --- a/src/game/backend/bindings/Bases.cpp +++ /dev/null @@ -1,116 +0,0 @@ -#include "Binding.h" - -#include "Bindings.h" - -#include "gse/callable/Native.h" -#include "gse/Exception.h" -#include "gse/type/Undefined.h" -#include "gse/type/Object.h" -#include "gse/type/Int.h" -#include "gse/type/String.h" -#include "gse/type/Array.h" -#include "gse/type/Bool.h" -#include "gse/context/Context.h" -#include "game/backend/Game.h" -#include "game/backend/slot/Slot.h" -#include "game/backend/map/tile/Tile.h" -#include "game/backend/base/PopDef.h" -#include "game/backend/event/DefinePop.h" -#include "game/backend/event/SpawnBase.h" - -namespace game { -namespace backend { -namespace bindings { - -BINDING_IMPL( bases ) { - const gse::type::object_properties_t properties = { - { - "define_pop", - NATIVE_CALL( this ) { - N_EXPECT_ARGS( 2 ); - N_GETVALUE( id, 0, String ); - N_GETVALUE( def, 1, Object ); - - N_GETPROP( name, def, "name", String ); - - base::pop_render_infos_t rh = {}; - base::pop_render_infos_t rp = {}; - const auto& f_read_renders = [ &def, &arg, &call_si, &ctx, &getprop_val, &obj_it ]( const std::string& key, base::pop_render_infos_t& out ) { - N_GETPROP( renders, def, key, Array ); - out.reserve( renders.size() ); - for ( const auto& v : renders ) { - if ( v.Get()->type != gse::type::Type::T_OBJECT ) { - GSE_ERROR( gse::EC.INVALID_CALL, "Pop render elements must be objects" ); - } - const auto* obj = (gse::type::Object*)v.Get(); - const auto& ov = obj->value; - N_GETPROP( type, ov, "type", String ); - if ( type == "sprite" ) { - N_GETPROP( file, ov, "file", String ); - N_GETPROP( x, ov, "x", Int ); - N_GETPROP( y, ov, "y", Int ); - N_GETPROP( w, ov, "w", Int ); - N_GETPROP( h, ov, "h", Int ); - out.push_back( - base::pop_render_info_t{ - file, - (uint16_t)x, - (uint16_t)y, - (uint16_t)w, - (uint16_t)h - } - ); - } - else { - GSE_ERROR( gse::EC.INVALID_CALL, "Only sprite pops are supported for now" ); - } - - } - }; - f_read_renders( "renders_human", rh ); - f_read_renders( "renders_progenitor", rp ); - - base::PopDef::pop_flags_t flags = base::PopDef::PF_NONE; - N_GETPROP_OPT( bool, can_work_tiles, def, "tile_worker", Bool, false ); - if ( can_work_tiles ) { - flags |= base::PopDef::PF_TILE_WORKER; - } - - auto* game = GAME; - return game->AddEvent( new event::DefinePop( - game->GetSlotNum(), - new base::PopDef( id, name, rh, rp, flags ) - ) ); - } ) - }, - { - "spawn", - NATIVE_CALL( this ) { - N_EXPECT_ARGS_MIN_MAX( 3, 4 ); - N_GETVALUE_UNWRAP( owner, 0, slot::Slot ); - N_GETVALUE_UNWRAP( tile, 1, map::tile::Tile ); - - N_GETVALUE( info, 2, Object ); - N_GETPROP_OPT( std::string, name, info, "name", String, "" ); - - if ( arguments.size() > 3 ) { - N_PERSIST_CALLABLE( on_spawn, 3 ); - } - - auto* game = GAME; - return game->AddEvent( new event::SpawnBase( - game->GetSlotNum(), - owner->GetIndex(), - tile->coord.x, - tile->coord.y, - name - ) ); - }) - }, - }; - return VALUE( gse::type::Object, properties ); -} - -} -} -} diff --git a/src/game/backend/bindings/Binding.cpp b/src/game/backend/bindings/Binding.cpp deleted file mode 100644 index 34b8daf7..00000000 --- a/src/game/backend/bindings/Binding.cpp +++ /dev/null @@ -1,21 +0,0 @@ -#include "Binding.h" - -#include "game/backend/State.h" - -namespace game { -namespace backend { -namespace bindings { - -Binding::Binding( const std::string& name, Bindings* bindings ) - : m_name( name ) - , m_bindings( bindings ) { - // -} - -void Binding::Add( gse::type::object_properties_t& methods ) { - methods.insert_or_assign( m_name, Get() ); -} - -} -} -} diff --git a/src/game/backend/bindings/Binding.h b/src/game/backend/bindings/Binding.h deleted file mode 100644 index 92c6f267..00000000 --- a/src/game/backend/bindings/Binding.h +++ /dev/null @@ -1,86 +0,0 @@ -#pragma once - -#include "common/Common.h" - -#include "gse/type/Types.h" - -#include "gse/Value.h" - -namespace game { -namespace backend { - -class Game; - -class State; -namespace bindings { - -class Bindings; - -class Binding { -public: - Binding( const std::string& name, Bindings* bindings ); - virtual ~Binding() = default; - - void Add( gse::type::object_properties_t& methods ); - -protected: - - virtual gse::Value Get() = 0; - - const std::string m_name; - Bindings* m_bindings; - -}; - -} -} -} - -#define BINDING( _name ) Binding_##_name - -#define BINDING_DEF( _name ) \ - class BINDING( _name ) : public Binding { \ - public: \ - BINDING( _name )( Bindings* bindings ) \ - : Binding( #_name, bindings ) {} \ - protected: \ - gse::Value Get() override; \ - }; - -#define BINDING_IMPL( _name ) \ - gse::Value BINDING( _name )::Get() - -#define GAME m_bindings->GetGame( ctx, call_si ) - -namespace game { -namespace backend { -namespace bindings { - -BINDING_DEF( message ) - -BINDING_DEF( exit ) - -BINDING_DEF( random ) - -BINDING_DEF( on ) - -BINDING_DEF( players ) - -BINDING_DEF( factions ) - -BINDING_DEF( tiles ) - -BINDING_DEF( units ) - -BINDING_DEF( bases ) - -BINDING_DEF( animations ) - -BINDING_DEF( map ) - -BINDING_DEF( resources ) - -} -} -} - diff --git a/src/game/backend/bindings/CMakeLists.txt b/src/game/backend/bindings/CMakeLists.txt deleted file mode 100644 index 51987b0b..00000000 --- a/src/game/backend/bindings/CMakeLists.txt +++ /dev/null @@ -1,18 +0,0 @@ -SET( SRC ${SRC} - - ${PWD}/Bindings.cpp - ${PWD}/Binding.cpp - ${PWD}/Message.cpp - ${PWD}/Exit.cpp - ${PWD}/Random.cpp - ${PWD}/On.cpp - ${PWD}/Players.cpp - ${PWD}/Factions.cpp - ${PWD}/Tiles.cpp - ${PWD}/Units.cpp - ${PWD}/Bases.cpp - ${PWD}/Animations.cpp - ${PWD}/Map.cpp - ${PWD}/Resources.cpp - - PARENT_SCOPE ) diff --git a/src/game/backend/bindings/Exit.cpp b/src/game/backend/bindings/Exit.cpp deleted file mode 100644 index 21829da0..00000000 --- a/src/game/backend/bindings/Exit.cpp +++ /dev/null @@ -1,31 +0,0 @@ -#include "Binding.h" - -#include "game/backend/Game.h" -#include "Bindings.h" - -#include "gse/type/String.h" -#include "gse/callable/Native.h" -#include "gse/Exception.h" -#include "gse/type/Undefined.h" - -namespace game { -namespace backend { -namespace bindings { - -BINDING_IMPL( exit ) { - return NATIVE_CALL( this ) { - N_EXPECT_ARGS_MIN_MAX( 0, 1 ); - if ( arguments.size() == 1 ) { - N_GETVALUE( reason, 0, String ); - GAME->Quit( reason ); - } - else { - GAME->Quit( "Script exited" ); - } - return VALUE( gse::type::Undefined ); - }); -} - -} -} -} diff --git a/src/game/backend/bindings/Factions.cpp b/src/game/backend/bindings/Factions.cpp deleted file mode 100644 index 3efbf419..00000000 --- a/src/game/backend/bindings/Factions.cpp +++ /dev/null @@ -1,77 +0,0 @@ -#include "Binding.h" - -#include "gse/callable/Native.h" -#include "gse/Exception.h" -#include "gse/type/Object.h" -#include "gse/type/Array.h" -#include "gse/type/String.h" -#include "gse/type/Int.h" -#include "gse/type/Float.h" -#include "gse/type/Undefined.h" -#include "gse/type/Bool.h" -#include "Bindings.h" -#include "game/backend/State.h" -#include "engine/Engine.h" -#include "loader/txt/TXTLoaders.h" -#include "loader/txt/FactionTXTLoader.h" -#include "loader/texture/TextureLoader.h" -#include "types/texture/Texture.h" -#include "game/backend/faction/Faction.h" -#include "game/backend/faction/FactionManager.h" - -#include "types/Color.h" - -namespace game { -namespace backend { -namespace bindings { - -BINDING_IMPL( factions ) { - const gse::type::object_properties_t properties = { - { - "import_base_names", - NATIVE_CALL() { - N_EXPECT_ARGS( 1 ); - N_GETVALUE( filename, 0, String ); - const auto& data = g_engine->GetTXTLoaders()->factions->GetFactionData( filename ); - std::vector< gse::Value > land_names = {}; - land_names.reserve( data.bases_names.land.size() ); - for ( const auto& name : data.bases_names.land ) { - land_names.push_back( VALUE( gse::type::String, name ) ); - } - std::vector< gse::Value > water_names = {}; - water_names.reserve( data.bases_names.water.size() ); - for ( const auto& name : data.bases_names.water ) { - water_names.push_back( VALUE( gse::type::String, name ) ); - } - const auto properties = gse::type::object_properties_t{ - { "land", VALUE( gse::type::Array, land_names ) }, - { "water", VALUE( gse::type::Array, water_names ) }, - }; - return VALUE( gse::type::Object, properties ); - } ) - }, - { - "import_colors", - NATIVE_CALL() { - N_EXPECT_ARGS( 1 ); - N_GETVALUE( filename, 0, String ); - const auto* texture = g_engine->GetTextureLoader()->LoadCustomTexture( filename ); - const auto properties = gse::type::object_properties_t{ - { "faction", types::Color::FromRGBA( texture->GetPixel( 4, 739 ) ).Wrap() }, - { "faction_shadow", types::Color::FromRGBA( texture->GetPixel( 4, 747 ) ).Wrap() }, - { "text", types::Color::FromRGBA( texture->GetPixel( 4, 755 ) ).Wrap() }, - { "text_shadow", types::Color::FromRGBA( texture->GetPixel( 4, 763 ) ).Wrap() }, - { "border", types::Color::FromRGBA( texture->GetPixel( 161, 749 ) ).Wrap() }, - { "border_alpha", types::Color::FromRGBA( texture->GetPixel( 161, 757 ) ).Wrap() }, - { "vehicle", types::Color::FromRGBA( texture->GetPixel( 435, 744 ) ).Wrap() }, - }; - return VALUE( gse::type::Object, properties ); - } ) - }, - }; - return VALUE( gse::type::Object, properties ); -} - -} -} -} diff --git a/src/game/backend/bindings/Map.cpp b/src/game/backend/bindings/Map.cpp deleted file mode 100644 index 654fbb92..00000000 --- a/src/game/backend/bindings/Map.cpp +++ /dev/null @@ -1,66 +0,0 @@ -#include "Binding.h" - -#include "gse/callable/Native.h" -#include "gse/Exception.h" -#include "gse/type/Object.h" -#include "gse/type/Int.h" -#include "gse/type/Undefined.h" -#include "game/backend/Game.h" -#include "Bindings.h" -#include "game/backend/map/Map.h" - -namespace game { -namespace backend { -namespace bindings { - -BINDING_IMPL( map ) { - const gse::type::object_properties_t properties = { - { - "get_width", - NATIVE_CALL( this ) { - const auto* m = GAME->GetMap(); - return VALUE( gse::type::Int, m->GetWidth() ); - }) - }, - { - "get_height", - NATIVE_CALL( this ) { - const auto* m = GAME->GetMap(); - return VALUE( gse::type::Int, m->GetHeight() ); - }) - }, - { - "get_tile", - NATIVE_CALL( this ) { - N_EXPECT_ARGS( 2 ); - N_GETVALUE( x, 0, Int ); - N_GETVALUE( y, 1, Int ); - const auto* m = GAME->GetMap(); - const auto w = m->GetWidth(); - const auto h = m->GetHeight(); - if ( x >= w ) { - GSE_ERROR( gse::EC.INVALID_CALL, "X coordinate exceeds map width ( " + std::to_string( x ) + " >= " + std::to_string( w ) + " )" ); - } - if ( y >= h ) { - GSE_ERROR( gse::EC.INVALID_CALL, "Y coordinate exceeds map height ( " + std::to_string( y ) + " >= " + std::to_string( h ) + " )" ); - } - if ( x < 0 ) { - GSE_ERROR( gse::EC.INVALID_CALL, "X coordinate can't be negative ( " + std::to_string( x ) + " < 0" ); - } - if ( y < 0 ) { - GSE_ERROR( gse::EC.INVALID_CALL, "Y coordinate can't be negative ( " + std::to_string( y ) + " < 0" ); - } - if ( x % 2 != y % 2 ) { - GSE_ERROR( gse::EC.INVALID_CALL, "X and Y oddity differs ( " + std::to_string( x ) + " % 2 != " + std::to_string( y ) + " % 2 )" ); - } - return m->GetTile( x, y )->Wrap(); - } ) - } - }; - - return VALUE( gse::type::Object, properties ); -} - -} -} -} diff --git a/src/game/backend/bindings/Message.cpp b/src/game/backend/bindings/Message.cpp deleted file mode 100644 index af53eb15..00000000 --- a/src/game/backend/bindings/Message.cpp +++ /dev/null @@ -1,25 +0,0 @@ -#include "Binding.h" - -#include "Bindings.h" -#include "game/backend/Game.h" -#include "gse/type/String.h" -#include "gse/callable/Native.h" -#include "gse/Exception.h" -#include "gse/type/Undefined.h" - -namespace game { -namespace backend { -namespace bindings { - -BINDING_IMPL( message ) { - return NATIVE_CALL( this ) { - N_EXPECT_ARGS( 1 ); - N_GETVALUE( text, 0, String ); - GAME->Message( text ); - return VALUE( gse::type::Undefined ); - }); -} - -} -} -} diff --git a/src/game/backend/bindings/On.cpp b/src/game/backend/bindings/On.cpp deleted file mode 100644 index 0049dac4..00000000 --- a/src/game/backend/bindings/On.cpp +++ /dev/null @@ -1,24 +0,0 @@ -#include "Binding.h" - -#include "Bindings.h" -#include "gse/callable/Native.h" -#include "gse/Exception.h" -#include "gse/type/Object.h" -#include "gse/type/Undefined.h" - -namespace game { -namespace backend { -namespace bindings { - -BINDING_IMPL( on ) { - -#define ON( _property, _cbtype ) { _property, CALLBACK( Bindings::_cbtype ) } - const gse::type::object_properties_t properties = { - }; -#undef ON - return VALUE( gse::type::Object, properties ); -} - -} -} -} diff --git a/src/game/backend/bindings/Players.cpp b/src/game/backend/bindings/Players.cpp deleted file mode 100644 index e8358c69..00000000 --- a/src/game/backend/bindings/Players.cpp +++ /dev/null @@ -1,41 +0,0 @@ -#include "Binding.h" - -#include "game/backend/State.h" -#include "game/backend/slot/Slot.h" -#include "game/backend/slot/Slots.h" -#include "Bindings.h" -#include "gse/callable/Native.h" -#include "gse/type/Array.h" -#include "gse/type/String.h" -#include "gse/type/Undefined.h" - -namespace game { -namespace backend { -namespace bindings { - -BINDING_IMPL( players ) { - const gse::type::object_properties_t properties = { - { - "get_all", - NATIVE_CALL( this ) { - N_EXPECT_ARGS( 0 ); - - auto& slots = m_bindings->GetState()->m_slots->GetSlots(); - gse::type::array_elements_t elements = {}; - for ( auto& slot : slots ) { - const auto state = slot.GetState(); - if ( state == slot::Slot::SS_OPEN || state == slot::Slot::SS_CLOSED ) { - continue; // skip - } - elements.push_back( slot.Wrap() ); - } - return VALUE( gse::type::Array, elements ); - }) - }, - }; - return VALUE( gse::type::Object, properties ); -} - -} -} -} diff --git a/src/game/backend/bindings/Random.cpp b/src/game/backend/bindings/Random.cpp deleted file mode 100644 index 04410d14..00000000 --- a/src/game/backend/bindings/Random.cpp +++ /dev/null @@ -1,55 +0,0 @@ -#include "game/backend/Game.h" -#include "game/backend/State.h" -#include "Bindings.h" -#include "gse/callable/Native.h" -#include "gse/type/Object.h" -#include "gse/type/Int.h" -#include "gse/type/Float.h" -#include "gse/type/Undefined.h" -#include "game/backend/Random.h" - -#include "Binding.h" - -namespace game { -namespace backend { -namespace bindings { - -BINDING_IMPL( random ) { - const gse::type::object_properties_t properties = { - { - "get_int", - NATIVE_CALL( this ) { - N_EXPECT_ARGS( 2 ); - N_GETVALUE( min, 0, Int ); - N_GETVALUE( max, 1, Int ); - if ( max < min ) { - GSE_ERROR( gse::EC.INVALID_CALL, "Maximum value is smaller than minimum ( " + std::to_string( max ) + " < " + std::to_string( min ) + " )" ); - } - if ( !GAME->GetState()->IsMaster() ) { - GSE_ERROR( gse::EC.INVALID_CALL, "Only master is allowed to generate random values" ); - } - return VALUE( gse::type::Int, GAME->GetRandom()->GetInt64( min, max ) ); - }) - }, - { - "get_float", - NATIVE_CALL( this ) { - N_EXPECT_ARGS( 2 ); - N_GETVALUE( min, 0, Float ); - N_GETVALUE( max, 1, Float ); - if ( max < min ) { - GSE_ERROR( gse::EC.INVALID_CALL, "Maximum value is smaller than minimum ( " + std::to_string( max ) + " < " + std::to_string( min ) + " )" ); - } - if ( !GAME->GetState()->IsMaster() ) { - GSE_ERROR( gse::EC.INVALID_CALL, "Only master is allowed to generate random values" ); - } - return VALUE( gse::type::Float, GAME->GetRandom()->GetFloat( min, max ) ); - }) - }, - }; - return VALUE( gse::type::Object, properties ); -} - -} -} -} diff --git a/src/game/backend/bindings/Resources.cpp b/src/game/backend/bindings/Resources.cpp deleted file mode 100644 index 5805a606..00000000 --- a/src/game/backend/bindings/Resources.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include "Binding.h" - -#include "game/backend/Game.h" -#include "Bindings.h" - -#include "gse/type/String.h" -#include "gse/type/Int.h" -#include "gse/callable/Native.h" -#include "gse/Exception.h" -#include "gse/type/Undefined.h" - -#include "game/backend/resource/Resource.h" -#include "game/backend/event/DefineResource.h" - -namespace game { -namespace backend { -namespace bindings { - -BINDING_IMPL( resources ) { - const gse::type::object_properties_t properties = { - { - "define", - NATIVE_CALL( this ) { - N_EXPECT_ARGS( 2 ); - N_GETVALUE( id, 0, String ); - N_GETVALUE( def, 1, Object ); - N_GETPROP( name, def, "name", String ); - N_GETPROP( render, def, "render", Object ); - N_GETPROP( type, render, "type", String ); - - if ( type == "sprite_map" ) { - N_GETPROP( file, render, "file", String ); - - N_GETPROP( yields, render, "yields", Object ); - N_GETPROP( grid_x, yields, "grid_x", Int ); - N_GETPROP( grid_y, yields, "grid_y", Int ); - N_GETPROP( grid_margin, yields, "grid_margin", Int ); - N_GETPROP( cell_width, yields, "cell_width", Int ); - N_GETPROP( cell_height, yields, "cell_height", Int ); - N_GETPROP( cells_count, yields, "cells_count", Int ); - -#define X( _n ) \ - N_GETPROP( _n, render, "plus", Object ); \ - N_GETPROP( _n ## _x, _n, "x", Int ); \ - N_GETPROP( _n ## _y, _n, "y", Int ); \ - N_GETPROP( _n ## _width, _n, "width", Int ); \ - N_GETPROP( _n ## _height, _n, "height", Int ); - X( plus ) - X( minus ) - - auto* resource = new resource::Resource( - id, - name, - { - file, - { - (uint16_t)grid_x, (uint16_t)grid_y, (uint8_t)grid_margin, - (uint16_t)cell_width, (uint16_t)cell_height, (uint8_t)cells_count, - }, - { - (uint16_t)plus_x, (uint16_t)plus_y, - (uint16_t)plus_width, (uint16_t)plus_height, - }, - { - (uint16_t)minus_x, (uint16_t)minus_y, - (uint16_t)minus_width, (uint16_t)minus_height, - }, - } - ); - auto* game = GAME; - return game->AddEvent( new event::DefineResource( game->GetSlotNum(), resource ) ); - } - else { - GSE_ERROR( gse::EC.GAME_ERROR, "Unsupported resource type: " + type ); - } - - return VALUE( gse::type::Undefined ); - } ) - }, - }; - return VALUE( gse::type::Object, properties ); - -} - -} -} -} - -#undef X \ No newline at end of file diff --git a/src/game/backend/bindings/Tiles.cpp b/src/game/backend/bindings/Tiles.cpp deleted file mode 100644 index 463205ad..00000000 --- a/src/game/backend/bindings/Tiles.cpp +++ /dev/null @@ -1,56 +0,0 @@ -#include "Binding.h" - -#include "gse/context/Context.h" -#include "gse/callable/Native.h" -#include "gse/Exception.h" -#include "gse/type/Array.h" -#include "gse/type/Object.h" -#include "gse/type/Undefined.h" -#include "game/backend/Game.h" -#include "game/backend/map/tile/TileManager.h" -#include "game/backend/event/RequestTileLocks.h" -#include "game/backend/map/tile/Tile.h" -#include "Bindings.h" - -namespace game { -namespace backend { -namespace bindings { - -BINDING_IMPL( tiles ) { - const gse::type::object_properties_t properties = { - { - "lock", - NATIVE_CALL( this ) { - N_EXPECT_ARGS( 2 ); - N_GETVALUE( tiles, 0, Array ); - N_PERSIST_CALLABLE( on_complete, 1 ); - map::tile::positions_t tile_positions = {}; - tile_positions.reserve( tiles.size() ); - for ( const auto& tileobj : tiles ) { - N_UNWRAP( tile, tileobj, map::tile::Tile ); - tile_positions.push_back( tile->coord ); - } - - GAME->GetTM()->SendTileLockRequest( tile_positions, [ this, on_complete, tile_positions, ctx, call_si ]() { - on_complete->Run( ctx, call_si, { - VALUE( gse::callable::Native, [ this, tile_positions ]( - gse::context::Context* ctx, - const gse::si_t& call_si, - const gse::type::function_arguments_t& arguments - ) -> gse::Value { - GAME->GetTM()->SendTileUnlockRequest( tile_positions ); - return VALUE( gse::type::Undefined ); - } ) - }); - N_UNPERSIST_CALLABLE( on_complete ); - }); - return VALUE( gse::type::Undefined ); - }) - }, - }; - return VALUE( gse::type::Object, properties ); -} - -} -} -} diff --git a/src/game/backend/bindings/Units.cpp b/src/game/backend/bindings/Units.cpp deleted file mode 100644 index 06dc50b5..00000000 --- a/src/game/backend/bindings/Units.cpp +++ /dev/null @@ -1,178 +0,0 @@ -#include "Binding.h" - -#include "gse/callable/Native.h" -#include "gse/Exception.h" -#include "gse/type/Object.h" -#include "gse/type/Array.h" -#include "gse/type/String.h" -#include "gse/type/Int.h" -#include "gse/type/Float.h" -#include "gse/type/Undefined.h" -#include "game/backend/Game.h" -#include "game/backend/slot/Slot.h" -#include "game/backend/unit/UnitManager.h" -#include "game/backend/unit/MoraleSet.h" -#include "game/backend/unit/StaticDef.h" -#include "game/backend/unit/SpriteRender.h" -#include "game/backend/unit/Unit.h" -#include "game/backend/event/DefineMorales.h" -#include "game/backend/event/DefineUnit.h" -#include "game/backend/event/SpawnUnit.h" -#include "game/backend/event/DespawnUnit.h" -#include "Bindings.h" - -namespace game { -namespace backend { -namespace bindings { - -const unit::morale_t GetMorale( GSE_CALLABLE, const int64_t& morale ) { - if ( morale < unit::MORALE_MIN || morale > unit::MORALE_MAX ) { - GSE_ERROR( gse::EC.INVALID_CALL, "Invalid morale value: " + std::to_string( morale ) + " (should be between " + std::to_string( unit::MORALE_MIN ) + " and " + std::to_string( unit::MORALE_MAX ) + ", inclusive)" ); - } - return (unit::morale_t)morale; -} - -const unit::health_t GetHealth( GSE_CALLABLE, const float health ) { - if ( health < unit::Unit::MINIMUM_HEALTH_TO_KEEP || health > unit::StaticDef::HEALTH_MAX ) { - GSE_ERROR( gse::EC.INVALID_CALL, "Invalid health value: " + std::to_string( health ) + " (should be between " + std::to_string( unit::Unit::MINIMUM_HEALTH_TO_KEEP ) + " and " + std::to_string( unit::StaticDef::HEALTH_MAX ) + ", inclusive)" ); - } - if ( health == 0 ) { - GSE_ERROR( gse::EC.INVALID_CALL, "Invalid health value: " + std::to_string( health ) + " (you can't spawn a dead unit)" ); - } - return (unit::health_t)health; -} - -BINDING_IMPL( units ) { - const gse::type::object_properties_t properties = { - { - "define_morales", - NATIVE_CALL( this ) { - N_EXPECT_ARGS( 2 ); - N_GETVALUE( id, 0, String ); - N_GETVALUE( arr, 1, Array ); - const uint8_t expected_count = unit::MORALE_MAX - unit::MORALE_MIN + 1; - if ( arr.size() != expected_count ) { - GSE_ERROR( gse::EC.INVALID_CALL, "Morale set must have exactly " + std::to_string( expected_count ) + " values (found " + std::to_string( arr.size() ) + ")"); - } - unit::MoraleSet::morale_values_t values = {}; - for ( const auto& v : arr ) { - if ( v.Get()->type != gse::type::Type::T_OBJECT ) { - GSE_ERROR( gse::EC.INVALID_CALL, "Morale set elements must be objects"); - } - const auto* obj = (gse::type::Object*)v.Get(); - N_GETPROP( name, obj->value, "name", String ); - values.push_back( unit::Morale{ name } ); - } - auto* game = GAME; - return game->AddEvent( new event::DefineMorales( game->GetSlotNum(), new unit::MoraleSet( id, values ) ) ); - } ) - }, - { - "define", - NATIVE_CALL( this ) { - N_EXPECT_ARGS( 2 ); - N_GETVALUE( id, 0, String ); - N_GETVALUE( unit_def, 1, Object ); - N_GETPROP( name, unit_def, "name", String ); - N_GETPROP( morale, unit_def, "morale", String ); - N_GETPROP( unit_type, unit_def, "type", String ); - if ( unit_type == "static" ) { - N_GETPROP( movement_type_str, unit_def, "movement_type", String ); - unit::movement_type_t movement_type; - if ( movement_type_str == "land" ) { - movement_type = unit::MT_LAND; - } - else if ( movement_type_str == "water" ) { - movement_type = unit::MT_WATER; - } - else if ( movement_type_str == "air" ) { - movement_type = unit::MT_AIR; - } - else if ( movement_type_str == "immovable" ) { - movement_type = unit::MT_IMMOVABLE; - } - else { - GSE_ERROR( gse::EC.INVALID_CALL, "Invalid movement type: " + movement_type_str + ". Specify one of: land water air immovable"); - } - N_GETPROP( movement_per_turn, unit_def, "movement_per_turn", Int ); - N_GETPROP( render_def, unit_def, "render", Object ); - N_GETPROP( render_type, render_def, "type", String ); - if ( render_type == "sprite" ) { - N_GETPROP( sprite_file, render_def, "file", String ); - N_GETPROP( sprite_x, render_def, "x", Int ); - N_GETPROP( sprite_y, render_def, "y", Int ); - N_GETPROP( sprite_w, render_def, "w", Int ); - N_GETPROP( sprite_h, render_def, "h", Int ); - N_GETPROP( sprite_cx, render_def, "cx", Int ); - N_GETPROP( sprite_cy, render_def, "cy", Int ); - N_GETPROP_OPT_INT( sprite_morale_based_xshift, render_def, "morale_based_xshift" ); - auto* game = GAME; - const auto* moraleset = game->GetUM()->GetMoraleSet( morale ); - if ( !moraleset ) { - GSE_ERROR( gse::EC.INVALID_CALL, "Morale type '" + morale + "' is not defined"); - } - auto* def = new unit::StaticDef( - id, - moraleset, - name, - movement_type, - movement_per_turn, - new unit::SpriteRender( - sprite_file, - sprite_x, - sprite_y, - sprite_w, - sprite_h, - sprite_cx, - sprite_cy, - sprite_morale_based_xshift - ) - ); - return game->AddEvent( new event::DefineUnit( game->GetSlotNum(), def ) ); - } - else { - GSE_ERROR( gse::EC.GAME_ERROR, "Unsupported render type: " + render_type ); - } - } - else { - GSE_ERROR( gse::EC.GAME_ERROR, "Unsupported unit type: " + unit_type ); - } - }) - }, - { - "spawn", - NATIVE_CALL( this ) { - N_EXPECT_ARGS( 5 ); - N_GETVALUE( def_name, 0, String ); - N_GETVALUE_UNWRAP( owner, 1, slot::Slot ); - N_GETVALUE_UNWRAP( tile, 2, map::tile::Tile ); - N_GETVALUE( morale, 3, Int ); - N_GETVALUE( health, 4, Float ); - auto* game = GAME; - return game->AddEvent( new event::SpawnUnit( - game->GetSlotNum(), - def_name, - owner->GetIndex(), - tile->coord.x, - tile->coord.y, - GetMorale( ctx, call_si, morale ), - GetHealth( ctx, call_si, health ) - ) ); - }) - }, - { - "despawn", - NATIVE_CALL( this ) { - N_EXPECT_ARGS( 1 ); - N_GETVALUE_UNWRAP( unit, 0, unit::Unit ); - auto* game = GAME; - return game->AddEvent( new event::DespawnUnit( game->GetSlotNum(), unit->m_id ) ); - }) - }, - }; - return VALUE( gse::type::Object, properties ); -} - -} -} -} diff --git a/src/game/backend/resource/ResourceManager.cpp b/src/game/backend/resource/ResourceManager.cpp index 2caff2e0..c266a856 100644 --- a/src/game/backend/resource/ResourceManager.cpp +++ b/src/game/backend/resource/ResourceManager.cpp @@ -6,7 +6,7 @@ #include "game/backend/Game.h" #include "game/backend/State.h" #include "game/backend/slot/Slot.h" -#include "game/backend/bindings/Bindings.h" +#include "game/backend/Bindings.h" #include "game/backend/event/DefineResource.h" namespace game { diff --git a/src/game/backend/unit/UnitManager.cpp b/src/game/backend/unit/UnitManager.cpp index af71600f..6ffc748d 100644 --- a/src/game/backend/unit/UnitManager.cpp +++ b/src/game/backend/unit/UnitManager.cpp @@ -8,7 +8,7 @@ #include "game/backend/Game.h" #include "game/backend/animation/AnimationManager.h" #include "game/backend/State.h" -#include "game/backend/bindings/Bindings.h" +#include "game/backend/Bindings.h" #include "game/backend/slot/Slots.h" #include "game/backend/event/DefineMorales.h" #include "game/backend/event/DefineUnit.h" From 42373f8366907ee3cc0970bfe4f565e31b44d981 Mon Sep 17 00:00:00 2001 From: afwbkbc Date: Sat, 16 Nov 2024 17:33:09 +0200 Subject: [PATCH 11/13] added GLSMAC_data/mods --- GLSMAC_data/mods/.gitkeep | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 GLSMAC_data/mods/.gitkeep diff --git a/GLSMAC_data/mods/.gitkeep b/GLSMAC_data/mods/.gitkeep new file mode 100644 index 00000000..e69de29b From 4e55a1cbe5466a871502adf53b8e8709d77a3ed8 Mon Sep 17 00:00:00 2001 From: afwbkbc Date: Sat, 16 Nov 2024 17:33:39 +0200 Subject: [PATCH 12/13] gitignored GLSMAC_data/mods --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index b7bc96dc..3fc0e012 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,4 @@ _deps /.cmake /cmake-build-debug /GLSMAC.cbp +/GLSMAC_data/mods From 1f8a58830a8150ed23ffabeb2d74cabc796d238b Mon Sep 17 00:00:00 2001 From: afwbkbc Date: Sat, 16 Nov 2024 21:12:22 +0200 Subject: [PATCH 13/13] --mods support --- src/config/Config.cpp | 31 +++++++++++++++++++++ src/config/Config.h | 5 ++++ src/game/backend/Bindings.cpp | 5 +++- src/game/backend/Game.cpp | 4 +++ src/game/backend/faction/FactionManager.cpp | 12 ++++++++ src/gse/GSE.cpp | 2 +- src/gse/GSE.h | 2 +- src/gse/builtins/Include.cpp | 2 +- src/gse/type/Object.cpp | 30 +++++++++++++++++++- src/gse/type/Object.h | 1 - src/resource/ResourceManager.cpp | 16 +++++++++-- 11 files changed, 102 insertions(+), 8 deletions(-) diff --git a/src/config/Config.cpp b/src/config/Config.cpp index f9686d27..56207a81 100644 --- a/src/config/Config.cpp +++ b/src/config/Config.cpp @@ -1,5 +1,6 @@ #include #include +#include #include "Config.h" @@ -242,6 +243,32 @@ Config::Config( const int argc, const char* argv[] ) m_launch_flags |= LF_QUICKSTART_FACTION; } ); + m_parser->AddRule( + "mods", "MODS", "Comma-separated list of mods to load", AH( this ) { + std::stringstream ss( value ); + while ( ss.good() ) { + std::string mod_name; + getline( ss, mod_name, ',' ); + if ( !mod_name.empty() ) { + const auto mod_path = util::FS::GeneratePath( + { + m_data_path, + "mods", + mod_name + } + ); + if ( !util::FS::DirectoryExists( mod_path ) ) { + Error( "Mod path does not exist or is not a directory: " + mod_path ); + } + m_mod_paths.push_back( mod_path ); + } + } + if ( m_mod_paths.empty() ) { + Error( "No mod paths were defined" ); + } + m_launch_flags |= LF_MODS; + } + ); #ifdef DEBUG m_parser->AddRule( @@ -405,6 +432,10 @@ const std::string& Config::GetQuickstartFaction() const { return m_quickstart_faction; } +const std::vector< std::string >& Config::GetModPaths() const { + return m_mod_paths; +} + #ifdef DEBUG const bool Config::HasDebugFlag( const debug_flag_t flag ) const { diff --git a/src/config/Config.h b/src/config/Config.h index 6536d57f..49078888 100644 --- a/src/config/Config.h +++ b/src/config/Config.h @@ -39,6 +39,7 @@ CLASS( Config, common::Module ) LF_QUICKSTART_MAP_LIFEFORMS = 1 << 12, LF_QUICKSTART_MAP_CLOUDS = 1 << 13, LF_QUICKSTART_FACTION = 1 << 14, + LF_MODS = 1 << 15, }; #ifdef DEBUG @@ -81,6 +82,8 @@ CLASS( Config, common::Module ) const game::backend::settings::map_config_value_t GetQuickstartMapClouds() const; const std::string& GetQuickstartFaction() const; + const std::vector< std::string >& GetModPaths() const; + #ifdef DEBUG const bool HasDebugFlag( const debug_flag_t flag ) const; @@ -121,6 +124,8 @@ CLASS( Config, common::Module ) game::backend::settings::map_config_value_t m_quickstart_map_clouds = game::backend::settings::MAP_CONFIG_CLOUDS_AVERAGE; std::string m_quickstart_faction = ""; + std::vector< std::string > m_mod_paths = {}; + #ifdef DEBUG uint16_t m_debug_flags = DF_NONE; diff --git a/src/game/backend/Bindings.cpp b/src/game/backend/Bindings.cpp index 14160932..765fd477 100644 --- a/src/game/backend/Bindings.cpp +++ b/src/game/backend/Bindings.cpp @@ -53,7 +53,10 @@ void Bindings::AddToContext( gse::context::Context* ctx ) { } void Bindings::RunMainScript() { - m_gse->GetInclude( m_gse_context, m_si_internal, m_entry_script ); + m_gse->RunScript( m_gse_context, m_si_internal, m_entry_script ); + for ( const auto& mod_path : g_engine->GetConfig()->GetModPaths() ) { + m_gse->RunScript( m_gse_context, m_si_internal, util::FS::GeneratePath({ mod_path, "main" })); + } } void Bindings::RunMain() { diff --git a/src/game/backend/Game.cpp b/src/game/backend/Game.cpp index 7402b539..66c78ead 100644 --- a/src/game/backend/Game.cpp +++ b/src/game/backend/Game.cpp @@ -472,6 +472,10 @@ const size_t Game::GetSlotNum() const { WRAPIMPL_BEGIN( Game, CLASS_GAME ) WRAPIMPL_PROPS + { + "year", + VALUE( gse::type::Int, 2100/*tmp*/ + m_current_turn.GetId() ) + }, { "random", m_random->Wrap() diff --git a/src/game/backend/faction/FactionManager.cpp b/src/game/backend/faction/FactionManager.cpp index b2db7ada..a861936a 100644 --- a/src/game/backend/faction/FactionManager.cpp +++ b/src/game/backend/faction/FactionManager.cpp @@ -192,6 +192,18 @@ WRAPIMPL_BEGIN( FactionManager, CLASS_FM ) return faction->Wrap(); } ) }, + { + "remove", + NATIVE_CALL( this ) { + N_EXPECT_ARGS( 1 ); + N_GETVALUE( id, 0, String ); + if ( !Get( id ) ) { + GSE_ERROR( gse::EC.GAME_ERROR, "Unknown faction: " + id ); + } + Remove( id ); + return VALUE( gse::type::Undefined ); + } ) + }, }; WRAPIMPL_END_PTR( FactionManager ) diff --git a/src/gse/GSE.cpp b/src/gse/GSE.cpp index 951323e5..8c944efc 100644 --- a/src/gse/GSE.cpp +++ b/src/gse/GSE.cpp @@ -80,7 +80,7 @@ void GSE::Run() { Log( "GSE finished" ); } -const Value GSE::GetInclude( context::Context* ctx, const si_t& si, const std::string& path ) { +const Value GSE::RunScript( context::Context* ctx, const si_t& si, const std::string& path ) { const auto& it = m_include_cache.find( path ); if ( it != m_include_cache.end() ) { return it->second.result; diff --git a/src/gse/GSE.h b/src/gse/GSE.h index 7035c2c9..b4ea6829 100644 --- a/src/gse/GSE.h +++ b/src/gse/GSE.h @@ -52,7 +52,7 @@ CLASS( GSE, common::Class ) void AddModule( const std::string& path, type::Callable* module ); void Run(); - const Value GetInclude( context::Context* ctx, const si_t& si, const std::string& path ); + const Value RunScript( context::Context* ctx, const si_t& si, const std::string& path ); void SetGlobal( const std::string& identifier, Value variable ); const Value& GetGlobal( const std::string& identifier ); diff --git a/src/gse/builtins/Include.cpp b/src/gse/builtins/Include.cpp index dffd884a..8c19e9ff 100644 --- a/src/gse/builtins/Include.cpp +++ b/src/gse/builtins/Include.cpp @@ -18,7 +18,7 @@ void Include::AddToContext( context::Context* ctx ) { N_EXPECT_ARGS( 1 ); N_GETVALUE( path, 0, String ); const auto full_path = ctx->GetScriptInfo().directory + GSE::PATH_SEPARATOR + path; - return ctx->GetGSE()->GetInclude( ctx, call_si, full_path ); + return ctx->GetGSE()->RunScript( ctx, call_si, full_path ); } ) ); } diff --git a/src/gse/type/Object.cpp b/src/gse/type/Object.cpp index d60be887..68a54548 100644 --- a/src/gse/type/Object.cpp +++ b/src/gse/type/Object.cpp @@ -23,6 +23,10 @@ static const std::unordered_map< Object::object_class_t, std::string > s_object_ Object::CLASS_EXCEPTION, "#exception" }, + { + Object::CLASS_RANDOM, + "#random" + }, { Object::CLASS_COLOR, "#color" @@ -39,6 +43,14 @@ static const std::unordered_map< Object::object_class_t, std::string > s_object_ Object::CLASS_GAME, "#game" }, + { + Object::CLASS_RM, + "#rm" + }, + { + Object::CLASS_TM, + "#tm" + }, { Object::CLASS_TILE, "#tile" @@ -53,7 +65,11 @@ static const std::unordered_map< Object::object_class_t, std::string > s_object_ }, { Object::CLASS_FM, - "#factions" + "#fm" + }, + { + Object::CLASS_UM, + "#um" }, { Object::CLASS_UNITDEF, @@ -63,10 +79,22 @@ static const std::unordered_map< Object::object_class_t, std::string > s_object_ Object::CLASS_UNIT, "#unit" }, + { + Object::CLASS_BM, + "#bm" + }, { Object::CLASS_BASE, "#base" }, + { + Object::CLASS_BASE_POP, + "#basepop" + }, + { + Object::CLASS_AM, + "#am" + } }; const std::string& Object::GetClassString( const object_class_t object_class ) { const auto& it = s_object_class_str.find( object_class ); diff --git a/src/gse/type/Object.h b/src/gse/type/Object.h index 9739aa45..ae6b727a 100644 --- a/src/gse/type/Object.h +++ b/src/gse/type/Object.h @@ -32,7 +32,6 @@ class Object : public Type { CLASS_SYSTEM, CLASS_STATE, CLASS_GAME, - CLASS_MAP, CLASS_RM, CLASS_TM, CLASS_TILE, diff --git a/src/resource/ResourceManager.cpp b/src/resource/ResourceManager.cpp index d5f2c14a..6b9baabb 100644 --- a/src/resource/ResourceManager.cpp +++ b/src/resource/ResourceManager.cpp @@ -5,6 +5,9 @@ #include "util/FS.h" +#include "engine/Engine.h" +#include "config/Config.h" + namespace resource { ResourceManager::ResourceManager() @@ -369,8 +372,17 @@ const std::string& ResourceManager::GetCustomPath( const std::string& path ) { key.resize( path.length() ); std::transform( path.begin(), path.end(), key.begin(), ::tolower ); - // look in datadir - auto resolved_file = util::FS::GetExistingCaseSensitivePath( m_data_path, path ); + std::string resolved_file = ""; + + // look in mod dirs + for ( const auto& mod_path : g_engine->GetConfig()->GetModPaths() ) { + resolved_file = util::FS::GetExistingCaseSensitivePath( mod_path, path ); + } + + if ( resolved_file.empty() ) { + // look in datadir + resolved_file = util::FS::GetExistingCaseSensitivePath( m_data_path, path ); + } if ( resolved_file.empty() ) {