From eb01950ecf60fde01ea4c9714208b4be48e714f2 Mon Sep 17 00:00:00 2001 From: afwbkbc Date: Fri, 14 Jun 2024 21:57:21 +0300 Subject: [PATCH 01/18] wip --- gse/default/main.gls.js | 4 +- src/loader/Loader.cpp | 4 ++ src/loader/Loader.h | 1 + src/loader/font/FontLoader.cpp | 2 +- src/loader/font/FontLoader.h | 2 +- src/loader/font/FreeType.cpp | 13 ++-- src/loader/font/FreeType.h | 2 +- src/loader/font/Null.h | 2 +- src/loader/sound/SoundLoader.cpp | 2 +- src/loader/texture/TextureLoader.cpp | 4 +- src/resource/ResourceManager.cpp | 5 ++ src/resource/ResourceManager.h | 1 + src/task/game/Animation.cpp | 2 +- src/task/game/AnimationDef.cpp | 5 +- src/task/game/AnimationDef.h | 10 +-- src/task/game/CMakeLists.txt | 5 +- src/task/game/Game.cpp | 63 ++++++++++++++----- src/task/game/Game.h | 17 +++-- src/task/game/InstancedSprite.cpp | 7 --- src/task/game/Sprite.cpp | 7 --- src/task/game/Types.h | 32 +++++++++- src/task/game/base/Base.cpp | 10 +-- src/task/game/base/Base.h | 7 ++- src/task/game/base/BaseManager.h | 5 +- src/task/game/faction/Faction.cpp | 8 +-- src/task/game/faction/Faction.h | 14 +++-- src/task/game/sprite/CMakeLists.txt | 5 ++ src/task/game/{ => sprite}/InstancedSprite.h | 2 + .../{ => sprite}/InstancedSpriteManager.cpp | 24 +------ .../{ => sprite}/InstancedSpriteManager.h | 10 +-- src/task/game/{ => sprite}/Sprite.h | 2 + src/task/game/text/CMakeLists.txt | 7 +++ src/task/game/text/InstancedFont.cpp | 32 ++++++++++ src/task/game/text/InstancedFont.h | 31 +++++++++ src/task/game/text/InstancedText.cpp | 19 ++++++ src/task/game/text/InstancedText.h | 32 ++++++++++ src/task/game/text/InstancedTextManager.cpp | 63 +++++++++++++++++++ src/task/game/text/InstancedTextManager.h | 50 +++++++++++++++ src/task/game/ui/bottom_bar/TilePreview.cpp | 4 +- src/task/game/unit/BadgeDefs.cpp | 18 +++--- src/task/game/unit/BadgeDefs.h | 20 +++--- src/task/game/unit/SlotBadges.cpp | 16 ++--- src/task/game/unit/SlotBadges.h | 16 ++--- src/task/game/unit/Unit.cpp | 10 +-- src/task/game/unit/Unit.h | 16 ++--- src/task/game/unit/UnitDef.cpp | 10 +-- src/task/game/unit/UnitDef.h | 14 +++-- src/task/game/unit/UnitManager.h | 5 +- src/types/Font.cpp | 7 ++- src/types/Font.h | 5 +- 50 files changed, 485 insertions(+), 167 deletions(-) delete mode 100644 src/task/game/InstancedSprite.cpp delete mode 100644 src/task/game/Sprite.cpp create mode 100644 src/task/game/sprite/CMakeLists.txt rename src/task/game/{ => sprite}/InstancedSprite.h (94%) rename src/task/game/{ => sprite}/InstancedSpriteManager.cpp (92%) rename src/task/game/{ => sprite}/InstancedSpriteManager.h (95%) rename src/task/game/{ => sprite}/Sprite.h (90%) create mode 100644 src/task/game/text/CMakeLists.txt create mode 100644 src/task/game/text/InstancedFont.cpp create mode 100644 src/task/game/text/InstancedFont.h create mode 100644 src/task/game/text/InstancedText.cpp create mode 100644 src/task/game/text/InstancedText.h create mode 100644 src/task/game/text/InstancedTextManager.cpp create mode 100644 src/task/game/text/InstancedTextManager.h diff --git a/gse/default/main.gls.js b/gse/default/main.gls.js index 4fe5117e..4f47b997 100644 --- a/gse/default/main.gls.js +++ b/gse/default/main.gls.js @@ -105,8 +105,8 @@ units.init(); let base_x = 0; let base_y = 1; while (base_x % 2 != base_y % 2) { - base_x = #game.random.get_int(0, w - 1); - base_y = #game.random.get_int(0, h - 1); + base_x = #game.random.get_int(1, w - 2); + base_y = #game.random.get_int(1, h - 2); } #game.bases.spawn( players[i], diff --git a/src/loader/Loader.cpp b/src/loader/Loader.cpp index bb7884ab..724a4239 100644 --- a/src/loader/Loader.cpp +++ b/src/loader/Loader.cpp @@ -6,6 +6,10 @@ namespace loader { const std::string& Loader::GetFilename( const resource::resource_t res ) const { + return g_engine->GetResourceManager()->GetFilename( res ); +} + +const std::string& Loader::GetPath( const resource::resource_t res ) const { return g_engine->GetResourceManager()->GetPath( res ); } diff --git a/src/loader/Loader.h b/src/loader/Loader.h index 966d5634..a4e28903 100644 --- a/src/loader/Loader.h +++ b/src/loader/Loader.h @@ -12,6 +12,7 @@ CLASS( Loader, base::Module ) protected: const std::string& GetFilename( const resource::resource_t res ) const; + const std::string& GetPath( const resource::resource_t res ) const; const std::string& GetCustomFilename( const std::string& filename ) const; }; diff --git a/src/loader/font/FontLoader.cpp b/src/loader/font/FontLoader.cpp index 4919c57c..0aaa3e0c 100644 --- a/src/loader/font/FontLoader.cpp +++ b/src/loader/font/FontLoader.cpp @@ -4,7 +4,7 @@ namespace loader { namespace font { types::Font* FontLoader::LoadFont( const resource::resource_t res, const unsigned char size ) { - return LoadFontImpl( GetFilename( res ), size ); + return LoadFontImpl( GetFilename( res ), GetPath( res ), size ); } } diff --git a/src/loader/font/FontLoader.h b/src/loader/font/FontLoader.h index 65cf1cac..7f231d19 100644 --- a/src/loader/font/FontLoader.h +++ b/src/loader/font/FontLoader.h @@ -16,7 +16,7 @@ namespace font { CLASS( FontLoader, Loader ) types::Font* LoadFont( const resource::resource_t res, const unsigned char size ); protected: - virtual types::Font* LoadFontImpl( const std::string& filename, const unsigned char size ) = 0; + virtual types::Font* LoadFontImpl( const std::string& name, const std::string& path, const unsigned char size ) = 0; }; } diff --git a/src/loader/font/FreeType.cpp b/src/loader/font/FreeType.cpp index 0f2d7860..7c7d68d5 100644 --- a/src/loader/font/FreeType.cpp +++ b/src/loader/font/FreeType.cpp @@ -25,9 +25,9 @@ void FreeType::Iterate() { } -types::Font* FreeType::LoadFontImpl( const std::string& filename, const unsigned char size ) { +types::Font* FreeType::LoadFontImpl( const std::string& name, const std::string& path, const unsigned char size ) { - std::string font_key = filename + ":" + std::to_string( size ); + std::string font_key = name + ":" + std::to_string( size ); font_map_t::iterator it = m_fonts.find( font_key ); if ( it != m_fonts.end() ) { @@ -38,12 +38,11 @@ types::Font* FreeType::LoadFontImpl( const std::string& filename, const unsigned Log( "Loading font \"" + font_key + "\"" ); - NEWV( font, types::Font ); - font->m_name = filename; + NEWV( font, types::Font, font_key ); FT_Face ftface; - res = FT_New_Face( m_freetype, filename.c_str(), 0, &ftface ); - ASSERT( !res, "Unable to load font \"" + filename + "\"" ); + res = FT_New_Face( m_freetype, path.c_str(), 0, &ftface ); + ASSERT( !res, "Unable to load font \"" + path + "\"" ); FT_Set_Pixel_Sizes( ftface, 0, size ); @@ -55,7 +54,7 @@ types::Font* FreeType::LoadFontImpl( const std::string& filename, const unsigned for ( int i = 32 ; i < 128 ; i++ ) { // only ascii for now res = FT_Load_Char( ftface, i, FT_LOAD_RENDER ); - ASSERT( !res, "Font \"" + filename + "\" bitmap loading failed" ); + ASSERT( !res, "Font \"" + path + "\" bitmap loading failed" ); bitmap = &font->m_symbols[ i ]; diff --git a/src/loader/font/FreeType.h b/src/loader/font/FreeType.h index b5565d4b..f9c133bb 100644 --- a/src/loader/font/FreeType.h +++ b/src/loader/font/FreeType.h @@ -20,7 +20,7 @@ CLASS( FreeType, FontLoader ) protected: - types::Font* LoadFontImpl( const std::string& filename, const unsigned char size ) override; + types::Font* LoadFontImpl( const std::string& name, const std::string& path, const unsigned char size ) override; private: FT_Library m_freetype; diff --git a/src/loader/font/Null.h b/src/loader/font/Null.h index df8cfd06..0af2b704 100644 --- a/src/loader/font/Null.h +++ b/src/loader/font/Null.h @@ -9,7 +9,7 @@ namespace font { CLASS( Null, FontLoader ) protected: - types::Font* LoadFontImpl( const std::string& filename, const unsigned char size ) override { return nullptr; } + types::Font* LoadFontImpl( const std::string& name, const std::string& path, const unsigned char size ) override { return nullptr; } }; } diff --git a/src/loader/sound/SoundLoader.cpp b/src/loader/sound/SoundLoader.cpp index 694dd43d..f8860375 100644 --- a/src/loader/sound/SoundLoader.cpp +++ b/src/loader/sound/SoundLoader.cpp @@ -4,7 +4,7 @@ namespace loader { namespace sound { types::Sound* SoundLoader::LoadSound( const resource::resource_t res ) { - return LoadSoundImpl( GetFilename( res ) ); + return LoadSoundImpl( GetPath( res ) ); } types::Sound* SoundLoader::LoadCustomSound( const std::string& filename ) { diff --git a/src/loader/texture/TextureLoader.cpp b/src/loader/texture/TextureLoader.cpp index 9204713a..923391c5 100644 --- a/src/loader/texture/TextureLoader.cpp +++ b/src/loader/texture/TextureLoader.cpp @@ -250,7 +250,7 @@ types::texture::Texture* TextureLoader::LoadTexture( const resource::resource_t const bool fix_yellow_shadows_old = m_fix_yellow_shadows; m_transparent_colors = GetTCs( res ); m_fix_yellow_shadows = s_fix_yellow_shadow.find( res ) != s_fix_yellow_shadow.end(); - auto* result = LoadTextureImpl( GetFilename( res ) ); + auto* result = LoadTextureImpl( GetPath( res ) ); m_transparent_colors = colors_old; m_fix_yellow_shadows = fix_yellow_shadows_old; return result; @@ -279,7 +279,7 @@ types::texture::Texture* TextureLoader::LoadCustomTexture( const std::string& fi types::texture::Texture* TextureLoader::LoadTexture( const resource::resource_t res, const size_t x1, const size_t y1, const size_t x2, const size_t y2, const uint8_t flags, const float value ) { const transparent_colors_t colors_old = m_transparent_colors; m_transparent_colors = GetTCs( res ); - types::texture::Texture* result = LoadTextureImpl( GetFilename( res ), x1, y1, x2, y2, flags, value ); + types::texture::Texture* result = LoadTextureImpl( GetPath( res ), x1, y1, x2, y2, flags, value ); m_transparent_colors = colors_old; return result; } diff --git a/src/resource/ResourceManager.cpp b/src/resource/ResourceManager.cpp index 3e37af8b..6f54e0ed 100644 --- a/src/resource/ResourceManager.cpp +++ b/src/resource/ResourceManager.cpp @@ -279,6 +279,11 @@ const resource_t ResourceManager::GetResource( const std::string& filename ) con return NONE; } +const std::string& ResourceManager::GetFilename( const resource_t res ) const { + ASSERT_NOLOG( m_resources_to_filenames.find( res ) != m_resources_to_filenames.end(), "filename for " + std::to_string( res ) + " not found" ); + return m_resources_to_filenames.at( res ); +} + const std::string& ResourceManager::GetPath( const resource_t res ) const { ASSERT_NOLOG( m_resource_paths.find( res ) != m_resource_paths.end(), "resource path for " + std::to_string( res ) + " not found" ); return m_resource_paths.at( res ); diff --git a/src/resource/ResourceManager.h b/src/resource/ResourceManager.h index 9edeafce..25f7873e 100644 --- a/src/resource/ResourceManager.h +++ b/src/resource/ResourceManager.h @@ -16,6 +16,7 @@ CLASS( ResourceManager, base::Module ) void Init( std::vector< std::string > possible_smac_paths ); const resource_t GetResource( const std::string& filename ) const; + const std::string& GetFilename( const resource_t res ) const; const std::string& GetPath( const resource_t res ) const; const std::string& GetCustomPath( const std::string& path ); diff --git a/src/task/game/Animation.cpp b/src/task/game/Animation.cpp index 29df019e..cc31ae86 100644 --- a/src/task/game/Animation.cpp +++ b/src/task/game/Animation.cpp @@ -4,7 +4,7 @@ #include "AnimationDef.h" #include "scene/actor/Sound.h" #include "audio/Audio.h" -#include "InstancedSprite.h" +#include "task/game/sprite/InstancedSprite.h" #include "scene/actor/Instanced.h" namespace task { diff --git a/src/task/game/AnimationDef.cpp b/src/task/game/AnimationDef.cpp index 6dc22c73..87990e74 100644 --- a/src/task/game/AnimationDef.cpp +++ b/src/task/game/AnimationDef.cpp @@ -4,11 +4,12 @@ #include "game/animation/FramesRow.h" #include "loader/sound/SoundLoader.h" #include "loader/texture/TextureLoader.h" +#include "task/game/sprite/InstancedSpriteManager.h" namespace task { namespace game { -AnimationDef::AnimationDef( InstancedSpriteManager* ism, const ::game::animation::Def* def ) +AnimationDef::AnimationDef( sprite::InstancedSpriteManager* ism, const ::game::animation::Def* def ) : m_ism( ism ) , m_id( def->m_id ) , m_type( def->m_type ) @@ -61,7 +62,7 @@ const instanced_sprites_t& AnimationDef::GetSprites() { y + cxy.y }, dst_wh, - InstancedSpriteManager::ZL_ANIMATIONS + ZL_ANIMATIONS ) ); } diff --git a/src/task/game/AnimationDef.h b/src/task/game/AnimationDef.h index fb10edd3..9740921f 100644 --- a/src/task/game/AnimationDef.h +++ b/src/task/game/AnimationDef.h @@ -1,7 +1,5 @@ #pragma once -#include "InstancedSpriteManager.h" - #include "Types.h" #include "game/animation/Types.h" @@ -16,10 +14,14 @@ class Def; namespace task { namespace game { +namespace sprite { +class InstancedSpriteManager; +} + class AnimationDef { public: - AnimationDef( InstancedSpriteManager* ism, const ::game::animation::Def* def ); + AnimationDef( sprite::InstancedSpriteManager* ism, const ::game::animation::Def* def ); ~AnimationDef(); const instanced_sprites_t& GetSprites(); @@ -28,7 +30,7 @@ class AnimationDef { private: - InstancedSpriteManager* const m_ism = nullptr; + sprite::InstancedSpriteManager* const m_ism = nullptr; ::game::animation::sprite_render_info_t m_render = {}; diff --git a/src/task/game/CMakeLists.txt b/src/task/game/CMakeLists.txt index 7b2fd186..e5c3473c 100644 --- a/src/task/game/CMakeLists.txt +++ b/src/task/game/CMakeLists.txt @@ -2,15 +2,14 @@ SUBDIR( ui ) SUBDIR( actor ) SUBDIR( faction ) SUBDIR( tile ) +SUBDIR( sprite ) +SUBDIR( text ) SUBDIR( unit ) SUBDIR( base ) SET( SRC ${SRC} ${PWD}/Game.cpp - ${PWD}/InstancedSpriteManager.cpp - ${PWD}/InstancedSprite.cpp - ${PWD}/Sprite.cpp ${PWD}/Slot.cpp ${PWD}/AnimationDef.cpp ${PWD}/Animation.cpp diff --git a/src/task/game/Game.cpp b/src/task/game/Game.cpp index a7e5459c..184f9e3c 100644 --- a/src/task/game/Game.cpp +++ b/src/task/game/Game.cpp @@ -28,7 +28,8 @@ #include "unit/UnitManager.h" #include "base/BaseManager.h" #include "faction/FactionManager.h" -#include "InstancedSpriteManager.h" +#include "task/game/sprite/InstancedSpriteManager.h" +#include "task/game/text/InstancedTextManager.h" #include "Animation.h" #include "AnimationDef.h" #include "task/game/unit/Unit.h" @@ -36,7 +37,7 @@ #include "Slot.h" #include "task/game/unit/BadgeDefs.h" #include "scene/actor/Instanced.h" -#include "InstancedSprite.h" +#include "task/game/sprite/InstancedSprite.h" #include "loader/texture/TextureLoader.h" #include "actor/Actor.h" #include "actor/TileSelection.h" @@ -49,6 +50,10 @@ #include "ui/style/Theme.h" #include "game/map/Consts.h" +// TMP +#include "loader/font/FontLoader.h" +#include "text/InstancedText.h" + #define INITIAL_CAMERA_ANGLE { -M_PI * 0.5, M_PI * 0.75, 0 } namespace task { @@ -482,11 +487,16 @@ base::BaseManager* Game::GetBM() const { return m_bm; } -InstancedSpriteManager* Game::GetISM() const { +sprite::InstancedSpriteManager* Game::GetISM() const { ASSERT( m_ism, "ism not set" ); return m_ism; } +text::InstancedTextManager* Game::GetITM() const { + ASSERT( m_itm, "itm not set" ); + return m_itm; +} + types::texture::Texture* Game::GetSourceTexture( const resource::resource_t res ) { const auto it = m_textures.source.find( res ); if ( it != m_textures.source.end() ) { @@ -503,7 +513,7 @@ types::texture::Texture* Game::GetSourceTexture( const resource::resource_t res return texture; } -InstancedSprite* Game::GetTerrainInstancedSprite( const ::game::map::sprite_actor_t& actor ) { +sprite::InstancedSprite* Game::GetTerrainInstancedSprite( const ::game::map::sprite_actor_t& actor ) { return m_ism->GetInstancedSprite( "Terrain_" + actor.name, GetSourceTexture( resource::PCX_TER1 ), @@ -517,7 +527,7 @@ InstancedSprite* Game::GetTerrainInstancedSprite( const ::game::map::sprite_acto ::game::map::s_consts.tile.scale.x, ::game::map::s_consts.tile.scale.y * ::game::map::s_consts.sprite.y_scale }, - InstancedSpriteManager::ZL_TERRAIN, + ZL_TERRAIN, actor.z_index ); } @@ -1083,14 +1093,6 @@ void Game::SendBackendRequest( const ::game::BackendRequest* request ) { m_pending_backend_requests.push_back( *request ); } -void Game::ActivateTurn() { - -} - -void Game::DeactivateTurn() { - -} - void Game::UpdateMapData( const types::Vec2< size_t >& map_size ) { m_map_data.width = map_size.x; @@ -1141,7 +1143,8 @@ void Game::Initialize( NEW( m_world_scene, scene::Scene, "Game", scene::SCENE_TYPE_ORTHO ); - NEW( m_ism, InstancedSpriteManager, m_world_scene ); + NEW( m_ism, sprite::InstancedSpriteManager, m_world_scene ); + NEW( m_itm, text::InstancedTextManager, m_world_scene ); NEW( m_fm, faction::FactionManager, this ); NEW( m_tm, tile::TileManager, this ); NEW( m_um, unit::UnitManager, this ); @@ -1604,6 +1607,33 @@ void Game::Initialize( ResetMapState(); + // TEST + + auto* font = m_itm->GetInstancedFont( g_engine->GetFontLoader()->LoadFont( resource::TTF_ARIALN, 18 ) ); + + auto* text = m_itm->CreateInstancedText( + "X", + font, + types::Color{ + 1.0f, + 0.0f, + 0.0f, + 1.0f + }, + ZL_BASES + ); + + + + /* + text->actor->AddInstance( + { + 0.0f, + 0.0f, + 0.0f + } + );*/ + m_is_initialized = true; } @@ -1705,6 +1735,11 @@ void Game::Deinitialize() { m_tm = nullptr; } + if ( m_itm ) { + DELETE( m_itm ); + m_itm = nullptr; + } + if ( m_ism ) { DELETE( m_ism ); m_ism = nullptr; diff --git a/src/task/game/Game.h b/src/task/game/Game.h index dc7e4b24..92b4900e 100644 --- a/src/task/game/Game.h +++ b/src/task/game/Game.h @@ -94,9 +94,16 @@ class BaseManager; class Base; } +namespace sprite { class InstancedSpriteManager; class InstancedSprite; class Sprite; +} + +namespace text { +class InstancedTextManager; +} + class Slot; class AnimationDef; class Animation; @@ -161,10 +168,11 @@ CLASS( Game, ::base::Task ) tile::TileManager* GetTM() const; unit::UnitManager* GetUM() const; base::BaseManager* GetBM() const; - InstancedSpriteManager* GetISM() const; + sprite::InstancedSpriteManager* GetISM() const; + text::InstancedTextManager* GetITM() const; types::texture::Texture* GetSourceTexture( const resource::resource_t res ); - InstancedSprite* GetTerrainInstancedSprite( const ::game::map::sprite_actor_t& actor ); + sprite::InstancedSprite* GetTerrainInstancedSprite( const ::game::map::sprite_actor_t& actor ); void CenterAtCoordinatePercents( const ::types::Vec2< float > position_percents ); @@ -231,14 +239,13 @@ CLASS( Game, ::base::Task ) tile::TileManager* m_tm = nullptr; unit::UnitManager* m_um = nullptr; base::BaseManager* m_bm = nullptr; - InstancedSpriteManager* m_ism = nullptr; + sprite::InstancedSpriteManager* m_ism = nullptr; + text::InstancedTextManager* m_itm = nullptr; size_t m_slot_index = 0; bool m_is_turn_active = false; ::game::turn::turn_status_t m_turn_status = ::game::turn::TS_PLEASE_WAIT; size_t m_turn_id = 0; - void ActivateTurn(); - void DeactivateTurn(); ::game::map_editor::tool_type_t m_editor_tool = ::game::map_editor::TT_NONE; ::game::map_editor::brush_type_t m_editor_brush = ::game::map_editor::BT_NONE; diff --git a/src/task/game/InstancedSprite.cpp b/src/task/game/InstancedSprite.cpp deleted file mode 100644 index a64e1866..00000000 --- a/src/task/game/InstancedSprite.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "InstancedSprite.h" - -namespace task { -namespace game { - -} -} diff --git a/src/task/game/Sprite.cpp b/src/task/game/Sprite.cpp deleted file mode 100644 index a4428a7a..00000000 --- a/src/task/game/Sprite.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "Sprite.h" - -namespace task { -namespace game { - -} -} diff --git a/src/task/game/Types.h b/src/task/game/Types.h index 3171e32c..edefab18 100644 --- a/src/task/game/Types.h +++ b/src/task/game/Types.h @@ -1,13 +1,43 @@ #pragma once #include +#include namespace task { namespace game { +namespace sprite { class InstancedSprite; +} + +typedef std::vector< sprite::InstancedSprite* > instanced_sprites_t; + +enum z_level_t { + ZL_TERRAIN, + ZL_BASES, + ZL_UNITS, + ZL_ANIMATIONS, +}; +static const float MAX_ZINDEX_ADJUSTMENT = 0.05f; -typedef std::vector< InstancedSprite* > instanced_sprites_t; +static const std::unordered_map< z_level_t, float > s_zlevel_map = { + { + ZL_TERRAIN, + 0.4f + }, + { + ZL_BASES, + 0.5f + }, + { + ZL_UNITS, + 0.6f + }, + { + ZL_ANIMATIONS, + 0.7f + }, +}; } } diff --git a/src/task/game/base/Base.cpp b/src/task/game/base/Base.cpp index a242e6af..e7f57a0d 100644 --- a/src/task/game/base/Base.cpp +++ b/src/task/game/base/Base.cpp @@ -6,8 +6,8 @@ #include "util/String.h" #include "game/base/Base.h" #include "task/game/Slot.h" -#include "task/game/Sprite.h" -#include "task/game/InstancedSprite.h" +#include "task/game/sprite/Sprite.h" +#include "task/game/sprite/InstancedSprite.h" #include "scene/actor/Instanced.h" #include "types/mesh/Rectangle.h" #include "scene/actor/Sprite.h" @@ -58,7 +58,7 @@ tile::Tile* Base::GetTile() const { return m_tile; } -Sprite* Base::GetSprite() const { +sprite::Sprite* Base::GetSprite() const { return m_faction->GetBaseSprite( false, 1, 0 ); } @@ -66,7 +66,7 @@ void Base::Show() { if ( !m_render.is_rendered ) { const auto& c = m_render.coords; - Sprite* sprite = GetSprite(); + sprite::Sprite* sprite = GetSprite(); if ( !m_render.instance_id ) { m_render.instance_id = sprite->next_instance_id++; @@ -94,7 +94,7 @@ const Base::render_data_t& Base::GetRenderData() const { return m_render_data; } -Base::meshtex_t Base::GetMeshTex( const InstancedSprite* sprite ) { +Base::meshtex_t Base::GetMeshTex( const sprite::InstancedSprite* sprite ) { auto* texture = sprite->actor->GetSpriteActor()->GetTexture(); NEWV( mesh, types::mesh::Rectangle ); mesh->SetCoords( diff --git a/src/task/game/base/Base.h b/src/task/game/base/Base.h index fd6d2a7f..f5da8916 100644 --- a/src/task/game/base/Base.h +++ b/src/task/game/base/Base.h @@ -22,8 +22,11 @@ namespace game { class Game; class Slot; + +namespace sprite { class Sprite; class InstancedSprite; +} namespace tile { class Tile; @@ -55,7 +58,7 @@ class Base { const bool IsOwned() const; tile::Tile* GetTile() const; - Sprite* GetSprite() const; + sprite::Sprite* GetSprite() const; void Show(); void Hide(); @@ -88,7 +91,7 @@ class Base { render_data_t m_render_data = {}; - meshtex_t GetMeshTex( const InstancedSprite* sprite ); + meshtex_t GetMeshTex( const sprite::InstancedSprite* sprite ); }; } diff --git a/src/task/game/base/BaseManager.h b/src/task/game/base/BaseManager.h index 977f40e4..1a7191ef 100644 --- a/src/task/game/base/BaseManager.h +++ b/src/task/game/base/BaseManager.h @@ -18,7 +18,10 @@ namespace task { namespace game { class Game; + +namespace sprite { class InstancedSpriteManager; +} namespace tile { class Tile; @@ -50,7 +53,7 @@ CLASS( BaseManager, ::base::Base ) private: Game* m_game; - InstancedSpriteManager* m_ism; + sprite::InstancedSpriteManager* m_ism; const size_t m_slot_index; diff --git a/src/task/game/faction/Faction.cpp b/src/task/game/faction/Faction.cpp index 436dce65..07d511a4 100644 --- a/src/task/game/faction/Faction.cpp +++ b/src/task/game/faction/Faction.cpp @@ -1,7 +1,7 @@ #include "Faction.h" #include "game/rules/Faction.h" -#include "task/game/InstancedSpriteManager.h" +#include "task/game/sprite/InstancedSpriteManager.h" #include "engine/Engine.h" #include "loader/texture/TextureLoader.h" @@ -11,7 +11,7 @@ namespace task { namespace game { namespace faction { -Faction::Faction( const ::game::rules::Faction* def, InstancedSpriteManager* ism ) +Faction::Faction( const ::game::rules::Faction* def, sprite::InstancedSpriteManager* ism ) : m_ism( ism ) , m_id( def->m_id ) , m_border_color( def->m_colors.border ) @@ -24,7 +24,7 @@ Faction::Faction( const ::game::rules::Faction* def, InstancedSpriteManager* ism // } -Sprite* Faction::GetBaseSprite( const bool is_water, const uint8_t size, const uint8_t perimeter_level ) { +sprite::Sprite* Faction::GetBaseSprite( const bool is_water, const uint8_t size, const uint8_t perimeter_level ) { ASSERT_NOLOG( size < 4, "base size overflow ( " + std::to_string( size ) + " >= 4 )" ); ASSERT_NOLOG( perimeter_level < 3, "base perimeter level overflow ( " + std::to_string( perimeter_level ) + " >= 3 )" ); const uint8_t index = ( is_water @@ -77,7 +77,7 @@ Sprite* Faction::GetBaseSprite( const bool is_water, const uint8_t size, const u y + cxy.y }, dst_wh, - InstancedSpriteManager::ZL_BASES + ZL_BASES ), 1 } diff --git a/src/task/game/faction/Faction.h b/src/task/game/faction/Faction.h index 5617acf7..37b36165 100644 --- a/src/task/game/faction/Faction.h +++ b/src/task/game/faction/Faction.h @@ -5,7 +5,7 @@ #include "game/rules/Types.h" #include "types/Color.h" -#include "task/game/Sprite.h" +#include "task/game/sprite/Sprite.h" namespace types { namespace texture { @@ -20,16 +20,18 @@ class Faction; namespace task { namespace game { +namespace sprite { class InstancedSpriteManager; class InstancedSprite; +} namespace faction { class Faction { public: - Faction( const ::game::rules::Faction* def, InstancedSpriteManager* ism ); + Faction( const ::game::rules::Faction* def, sprite::InstancedSpriteManager* ism ); - Sprite* GetBaseSprite( const bool is_water, const uint8_t size, const uint8_t perimeter_level ); + sprite::Sprite* GetBaseSprite( const bool is_water, const uint8_t size, const uint8_t perimeter_level ); const std::string m_id; const types::Color m_border_color; @@ -37,7 +39,7 @@ class Faction { private: - InstancedSpriteManager* m_ism = nullptr; + sprite::InstancedSpriteManager* m_ism = nullptr; struct { ::game::rules::bases_render_info_t bases_render; @@ -46,8 +48,8 @@ class Faction { types::texture::Texture* m_base_grid_texture = nullptr; types::texture::Texture* GetBaseGridTexture(); - std::unordered_map< uint8_t, Sprite > m_base_grid_sprites = {}; - Sprite m_base_sprites[6][4] = {}; + std::unordered_map< uint8_t, sprite::Sprite > m_base_grid_sprites = {}; + sprite::Sprite m_base_sprites[6][4] = {}; }; diff --git a/src/task/game/sprite/CMakeLists.txt b/src/task/game/sprite/CMakeLists.txt new file mode 100644 index 00000000..99a8dcdf --- /dev/null +++ b/src/task/game/sprite/CMakeLists.txt @@ -0,0 +1,5 @@ +SET( SRC ${SRC} + + ${PWD}/InstancedSpriteManager.cpp + + PARENT_SCOPE ) diff --git a/src/task/game/InstancedSprite.h b/src/task/game/sprite/InstancedSprite.h similarity index 94% rename from src/task/game/InstancedSprite.h rename to src/task/game/sprite/InstancedSprite.h index 81dd8210..7f9192ba 100644 --- a/src/task/game/InstancedSprite.h +++ b/src/task/game/sprite/InstancedSprite.h @@ -10,6 +10,7 @@ class Instanced; namespace task { namespace game { +namespace sprite { class InstancedSprite { public: @@ -23,3 +24,4 @@ class InstancedSprite { } } +} diff --git a/src/task/game/InstancedSpriteManager.cpp b/src/task/game/sprite/InstancedSpriteManager.cpp similarity index 92% rename from src/task/game/InstancedSpriteManager.cpp rename to src/task/game/sprite/InstancedSpriteManager.cpp index f49c03dd..77130cfb 100644 --- a/src/task/game/InstancedSpriteManager.cpp +++ b/src/task/game/sprite/InstancedSpriteManager.cpp @@ -1,6 +1,6 @@ #include "InstancedSpriteManager.h" -#include "task/game/InstancedSprite.h" +#include "InstancedSprite.h" #include "scene/Scene.h" #include "scene/actor/Instanced.h" #include "scene/actor/Sprite.h" @@ -8,26 +8,7 @@ namespace task { namespace game { - -static const float MAX_ZINDEX_ADJUSTMENT = 0.05f; -static const std::unordered_map< InstancedSpriteManager::z_level_t, float > s_zlevel_map = { - { - InstancedSpriteManager::ZL_TERRAIN, - 0.4f - }, - { - InstancedSpriteManager::ZL_BASES, - 0.5f - }, - { - InstancedSpriteManager::ZL_UNITS, - 0.6f - }, - { - InstancedSpriteManager::ZL_ANIMATIONS, - 0.7f - }, -}; +namespace sprite { InstancedSpriteManager::InstancedSpriteManager( scene::Scene* scene ) : m_scene( scene ) { @@ -199,3 +180,4 @@ types::texture::Texture* InstancedSpriteManager::GetRepaintedSourceTexture( cons } } +} diff --git a/src/task/game/InstancedSpriteManager.h b/src/task/game/sprite/InstancedSpriteManager.h similarity index 95% rename from src/task/game/InstancedSpriteManager.h rename to src/task/game/sprite/InstancedSpriteManager.h index 5537eb38..982b8bba 100644 --- a/src/task/game/InstancedSpriteManager.h +++ b/src/task/game/sprite/InstancedSpriteManager.h @@ -5,6 +5,7 @@ #include "base/Base.h" +#include "task/game/Types.h" #include "game/map/Types.h" #include "types/texture/Types.h" #include "InstancedSprite.h" @@ -19,17 +20,11 @@ class Scene; namespace task { namespace game { +namespace sprite { CLASS( InstancedSpriteManager, ::base::Base ) public: - enum z_level_t { - ZL_TERRAIN, - ZL_BASES, - ZL_UNITS, - ZL_ANIMATIONS, - }; - InstancedSpriteManager( scene::Scene* scene ); ~InstancedSpriteManager(); @@ -62,3 +57,4 @@ CLASS( InstancedSpriteManager, ::base::Base ) } } +} diff --git a/src/task/game/Sprite.h b/src/task/game/sprite/Sprite.h similarity index 90% rename from src/task/game/Sprite.h rename to src/task/game/sprite/Sprite.h index ebaadcc3..5556393d 100644 --- a/src/task/game/Sprite.h +++ b/src/task/game/sprite/Sprite.h @@ -4,6 +4,7 @@ namespace task { namespace game { +namespace sprite { class InstancedSprite; @@ -15,3 +16,4 @@ class Sprite { } } +} diff --git a/src/task/game/text/CMakeLists.txt b/src/task/game/text/CMakeLists.txt new file mode 100644 index 00000000..9047afd0 --- /dev/null +++ b/src/task/game/text/CMakeLists.txt @@ -0,0 +1,7 @@ +SET( SRC ${SRC} + + ${PWD}/InstancedTextManager.cpp + ${PWD}/InstancedFont.cpp + ${PWD}/InstancedText.cpp + + PARENT_SCOPE ) diff --git a/src/task/game/text/InstancedFont.cpp b/src/task/game/text/InstancedFont.cpp new file mode 100644 index 00000000..cbeb5bc0 --- /dev/null +++ b/src/task/game/text/InstancedFont.cpp @@ -0,0 +1,32 @@ +#include "InstancedFont.h" + +#include "types/Font.h" +#include "types/texture/Texture.h" + +#include "task/game/sprite/Sprite.h" + +namespace task { +namespace game { +namespace text { + +InstancedFont::InstancedFont( const types::Font* font ) + : m_name( font->m_name ) { + + // load font into texture + NEW( m_texture, types::texture::Texture, "InstancedFont_" + m_name, font->m_dimensions.width, font->m_dimensions.height ); + m_texture->Fill( 0, 0, m_texture->m_width - 1, m_texture->m_height - 1, types::Color( 1.0f, 0.0f, 0.0f, 1.0f ) ); + +} + +InstancedFont::~InstancedFont() { + + DELETE( m_texture ); +} + +const std::string& InstancedFont::GetFontName() const { + return m_name; +} + +} +} +} diff --git a/src/task/game/text/InstancedFont.h b/src/task/game/text/InstancedFont.h new file mode 100644 index 00000000..dc9d5673 --- /dev/null +++ b/src/task/game/text/InstancedFont.h @@ -0,0 +1,31 @@ +#pragma once + +#include + +namespace types { +class Font; +namespace texture { +class Texture; +} +} + +namespace task { +namespace game { +namespace text { + +class InstancedFont { +public: + InstancedFont( const types::Font* font ); + ~InstancedFont(); + + const std::string& GetFontName() const; + +private: + const std::string m_name = ""; + + types::texture::Texture* m_texture = nullptr; +}; + +} +} +} diff --git a/src/task/game/text/InstancedText.cpp b/src/task/game/text/InstancedText.cpp new file mode 100644 index 00000000..3174b8d9 --- /dev/null +++ b/src/task/game/text/InstancedText.cpp @@ -0,0 +1,19 @@ +#include "InstancedText.h" + +namespace task { +namespace game { +namespace text { + +InstancedText::InstancedText( const std::string& name, InstancedFont* font ) + : m_name( name ) + , m_font( font ) { + // +} + +void InstancedText::ShowAt( const types::Vec3& coords ) { + +} + +} +} +} \ No newline at end of file diff --git a/src/task/game/text/InstancedText.h b/src/task/game/text/InstancedText.h new file mode 100644 index 00000000..afb99420 --- /dev/null +++ b/src/task/game/text/InstancedText.h @@ -0,0 +1,32 @@ +#pragma once + +#include + +#include "types/Vec3.h" + +namespace task { +namespace game { + +namespace sprite { +class InstancedSprite; +} + +namespace text { + +class InstancedFont; + +class InstancedText { +public: + InstancedText( const std::string& name, InstancedFont* font ); + + void ShowAt( const types::Vec3& coords ); + +private: + const std::string m_name = ""; + InstancedFont* m_font = nullptr; + +}; + +} +} +} diff --git a/src/task/game/text/InstancedTextManager.cpp b/src/task/game/text/InstancedTextManager.cpp new file mode 100644 index 00000000..f65dde7e --- /dev/null +++ b/src/task/game/text/InstancedTextManager.cpp @@ -0,0 +1,63 @@ +#include "InstancedTextManager.h" + +#include "InstancedFont.h" +#include "InstancedText.h" +#include "types/Font.h" + +namespace task { +namespace game { +namespace text { + +InstancedTextManager::InstancedTextManager( scene::Scene* scene ) + : m_scene( scene ) { + // +} + +InstancedTextManager::~InstancedTextManager() { + for ( const auto& it : m_instanced_texts ) { + delete it.second; + } + for ( const auto& it : m_instanced_fonts ) { + delete it.second; + } +} + +InstancedFont* InstancedTextManager::GetInstancedFont( const types::Font* font ) { + auto it = m_instanced_fonts.find( font ); + if ( it == m_instanced_fonts.end() ) { + Log( "Initializing instanced font: " + font->m_name ); + it = m_instanced_fonts.insert( + { + font, + new InstancedFont( font ) + } + ).first; + } + return it->second; +} + +InstancedText* InstancedTextManager::CreateInstancedText( + const std::string& text, + InstancedFont* font, + const types::Color& color, + const z_level_t z_level, + const float z_index_adjustment +) { + + ASSERT( s_zlevel_map.find( z_level ) != s_zlevel_map.end(), "unknown z level " + std::to_string( z_level ) ); + ASSERT( z_index_adjustment >= -MAX_ZINDEX_ADJUSTMENT && z_index_adjustment <= MAX_ZINDEX_ADJUSTMENT, "z index adjustment too large" ); + + Log( "Creating instanced text: '" + text + "', " + font->GetFontName() ); + + /* + NEWV( instanced, scene::actor::Instanced, actor ); + instanced->SetZIndex( s_zlevel_map.at( z_level ) + z_index_adjustment ); + m_scene->AddActor( instanced ); + */ + + return new InstancedText( text, font ); +} + +} +} +} diff --git a/src/task/game/text/InstancedTextManager.h b/src/task/game/text/InstancedTextManager.h new file mode 100644 index 00000000..75217fa6 --- /dev/null +++ b/src/task/game/text/InstancedTextManager.h @@ -0,0 +1,50 @@ +#pragma once + +#include "base/Base.h" + +#include "task/game/Types.h" + +#include "types/Color.h" + +namespace types { +class Font; +} + +namespace scene { +class Scene; +} + +namespace task { +namespace game { +namespace text { + +class InstancedFont; +class InstancedText; + +CLASS( InstancedTextManager, ::base::Base ) +public: + + InstancedTextManager( scene::Scene* scene ); + ~InstancedTextManager(); + + InstancedFont* GetInstancedFont( const types::Font* font ); + + InstancedText* CreateInstancedText( + const std::string& text, + InstancedFont* font, + const types::Color& color, + const z_level_t z_level, + const float z_index_adjustment = 0.0f + ); + +private: + + scene::Scene* m_scene = nullptr; + + std::unordered_map< const types::Font*, InstancedFont* > m_instanced_fonts = {}; + +}; + +} +} +} diff --git a/src/task/game/ui/bottom_bar/TilePreview.cpp b/src/task/game/ui/bottom_bar/TilePreview.cpp index aeec4d5d..a77f4e74 100644 --- a/src/task/game/ui/bottom_bar/TilePreview.cpp +++ b/src/task/game/ui/bottom_bar/TilePreview.cpp @@ -7,8 +7,8 @@ #include "types/mesh/Render.h" #include "ui/object/Label.h" #include "ui/object/Mesh.h" -#include "task/game/InstancedSpriteManager.h" -#include "task/game/InstancedSprite.h" +#include "task/game/sprite/InstancedSpriteManager.h" +#include "task/game/sprite/InstancedSprite.h" #include "scene/actor/Instanced.h" #include "scene/actor/Sprite.h" diff --git a/src/task/game/unit/BadgeDefs.cpp b/src/task/game/unit/BadgeDefs.cpp index cd80dded..9455af3f 100644 --- a/src/task/game/unit/BadgeDefs.cpp +++ b/src/task/game/unit/BadgeDefs.cpp @@ -3,9 +3,9 @@ #include "engine/Engine.h" #include "loader/texture/TextureLoader.h" #include "types/texture/Texture.h" -#include "task/game/InstancedSpriteManager.h" +#include "task/game/sprite/InstancedSpriteManager.h" #include "game/map/Consts.h" -#include "task/game/InstancedSprite.h" +#include "task/game/sprite/InstancedSprite.h" namespace task { namespace game { @@ -13,7 +13,7 @@ namespace unit { const BadgeDefs::consts_t BadgeDefs::s_consts = {}; -BadgeDefs::BadgeDefs( InstancedSpriteManager* ism ) +BadgeDefs::BadgeDefs( sprite::InstancedSpriteManager* ism ) : m_ism( ism ) { // } @@ -51,7 +51,7 @@ const types::Vec3 BadgeDefs::GetBadgeHealthbarCoords( const types::Vec3& unit_co }; } -InstancedSprite* BadgeDefs::GetBadgeSprite( const badge_type_t badge_type, const ::game::unit::morale_t morale ) { +sprite::InstancedSprite* BadgeDefs::GetBadgeSprite( const badge_type_t badge_type, const ::game::unit::morale_t morale ) { auto it1 = m_unitbadge_sprites.find( badge_type ); if ( it1 == m_unitbadge_sprites.end() ) { it1 = m_unitbadge_sprites.insert( @@ -102,7 +102,7 @@ InstancedSprite* BadgeDefs::GetBadgeSprite( const badge_type_t badge_type, const ::game::map::s_consts.tile.scale.x * s_consts.scale.x, ::game::map::s_consts.tile.scale.y * s_consts.scale.y * ::game::map::s_consts.sprite.y_scale }, - InstancedSpriteManager::ZL_UNITS, + ZL_UNITS, 0.008f ) } @@ -111,7 +111,7 @@ InstancedSprite* BadgeDefs::GetBadgeSprite( const badge_type_t badge_type, const return it2->second; } -InstancedSprite* BadgeDefs::GetFakeBadgeSprite() { +sprite::InstancedSprite* BadgeDefs::GetFakeBadgeSprite() { if ( !m_fake_badge ) { m_fake_badge = m_ism->GetInstancedSprite( "Badge_FAKE", @@ -132,14 +132,14 @@ InstancedSprite* BadgeDefs::GetFakeBadgeSprite() { ::game::map::s_consts.tile.scale.x * s_consts.fake_badges.scale.x, ::game::map::s_consts.tile.scale.y * s_consts.fake_badges.scale.y * ::game::map::s_consts.sprite.y_scale }, - InstancedSpriteManager::ZL_UNITS, + ZL_UNITS, 0.002f ); } return m_fake_badge; } -Sprite* BadgeDefs::GetBadgeHealthbarSprite( const float health ) { +sprite::Sprite* BadgeDefs::GetBadgeHealthbarSprite( const float health ) { const uint8_t step = round( health * ( s_consts.healthbars.resolution - 1 ) ); auto it = m_healthbar_sprites.find( step ); if ( it == m_healthbar_sprites.end() ) { @@ -195,7 +195,7 @@ Sprite* BadgeDefs::GetBadgeHealthbarSprite( const float health ) { ::game::map::s_consts.tile.scale.x * s_consts.healthbars.scale.x, ::game::map::s_consts.tile.scale.y * s_consts.healthbars.scale.y * ::game::map::s_consts.sprite.y_scale }, - InstancedSpriteManager::ZL_UNITS, + ZL_UNITS, 0.005f ), 1 diff --git a/src/task/game/unit/BadgeDefs.h b/src/task/game/unit/BadgeDefs.h index 626e1682..46927607 100644 --- a/src/task/game/unit/BadgeDefs.h +++ b/src/task/game/unit/BadgeDefs.h @@ -7,7 +7,7 @@ #include "game/unit/Types.h" -#include "task/game/Sprite.h" +#include "task/game/sprite/Sprite.h" namespace types::texture { class Texture; @@ -16,8 +16,10 @@ class Texture; namespace task { namespace game { +namespace sprite { class InstancedSprite; class InstancedSpriteManager; +} namespace unit { @@ -27,7 +29,7 @@ class BadgeDefs { static const types::Vec3 GetBadgeCoords( const types::Vec3& unit_coords ); static const types::Vec3 GetBadgeHealthbarCoords( const types::Vec3& unit_coords ); - BadgeDefs( InstancedSpriteManager* ism ); + BadgeDefs( sprite::InstancedSpriteManager* ism ); ~BadgeDefs(); typedef uint8_t badge_type_t; @@ -36,9 +38,9 @@ class BadgeDefs { static const badge_type_t BT_DEFAULT{ 1 << 1 }; static const badge_type_t BT_PROGENITOR{ 0 << 1 }; - InstancedSprite* GetBadgeSprite( const badge_type_t badge_type, const ::game::unit::morale_t morale ); - InstancedSprite* GetFakeBadgeSprite(); - Sprite* GetBadgeHealthbarSprite( const float health ); + sprite::InstancedSprite* GetBadgeSprite( const badge_type_t badge_type, const ::game::unit::morale_t morale ); + sprite::InstancedSprite* GetFakeBadgeSprite(); + sprite::Sprite* GetBadgeHealthbarSprite( const float health ); const size_t GetBadgeBlinkInterval() const; const types::Vec3 GetFakeBadgeCoords( const types::Vec3& coords, const uint8_t offset ) const; @@ -87,19 +89,19 @@ class BadgeDefs { } blink; } s_consts; - InstancedSpriteManager* const m_ism = nullptr; + sprite::InstancedSpriteManager* const m_ism = nullptr; - typedef std::unordered_map< ::game::unit::morale_t, InstancedSprite* > unitbadge_spritemap_t; + typedef std::unordered_map< ::game::unit::morale_t, sprite::InstancedSprite* > unitbadge_spritemap_t; typedef std::unordered_map< badge_type_t, unitbadge_spritemap_t > unitbadge_spritemaps_t; unitbadge_spritemaps_t m_unitbadge_sprites = {}; - InstancedSprite* m_fake_badge = nullptr; + sprite::InstancedSprite* m_fake_badge = nullptr; types::texture::Texture* m_badges_texture = nullptr; types::texture::Texture* GetBadgesTexture(); std::unordered_map< uint8_t, types::texture::Texture* > m_healthbar_textures = {}; - std::unordered_map< uint8_t, Sprite > m_healthbar_sprites = {}; + std::unordered_map< uint8_t, sprite::Sprite > m_healthbar_sprites = {}; }; diff --git a/src/task/game/unit/SlotBadges.cpp b/src/task/game/unit/SlotBadges.cpp index d95c6fd9..c93b864d 100644 --- a/src/task/game/unit/SlotBadges.cpp +++ b/src/task/game/unit/SlotBadges.cpp @@ -1,8 +1,8 @@ #include "SlotBadges.h" #include "BadgeDefs.h" -#include "task/game/InstancedSpriteManager.h" -#include "task/game/InstancedSprite.h" +#include "task/game/sprite/InstancedSpriteManager.h" +#include "task/game/sprite/InstancedSprite.h" #include "task/game/faction/Faction.h" #include "scene/actor/Instanced.h" @@ -12,7 +12,7 @@ namespace unit { SlotBadges::SlotBadges( BadgeDefs* badge_defs, - InstancedSpriteManager* ism, + sprite::InstancedSpriteManager* ism, const size_t slot_index, const faction::Faction* faction ) @@ -58,7 +58,7 @@ SlotBadges::~SlotBadges() { } } -Sprite* SlotBadges::GetUnitBadgeSprite( const ::game::unit::morale_t morale, const bool is_active ) { +sprite::Sprite* SlotBadges::GetUnitBadgeSprite( const ::game::unit::morale_t morale, const bool is_active ) { auto it = m_per_morale_sprites.find( morale ); if ( it == m_per_morale_sprites.end() ) { it = m_per_morale_sprites.insert( @@ -71,11 +71,11 @@ Sprite* SlotBadges::GetUnitBadgeSprite( const ::game::unit::morale_t morale, con } ).first; } - Sprite** sprite_ptr = is_active + sprite::Sprite** sprite_ptr = is_active ? &it->second.normal : &it->second.greyedout; if ( !*sprite_ptr ) { - NEW( *sprite_ptr, Sprite, { + NEW( *sprite_ptr, sprite::Sprite, { m_ism->GetRepaintedInstancedSprite( m_badges_key + "_" + std::to_string( morale ) + ( is_active ? "_NORMAL" @@ -109,9 +109,9 @@ void SlotBadges::HideFakeBadge( const size_t instance_id ) { GetFakeBadge()->instanced_sprite->actor->RemoveInstance( instance_id ); } -Sprite* SlotBadges::GetFakeBadge() { +sprite::Sprite* SlotBadges::GetFakeBadge() { if ( !m_fake_badge ) { - NEW( m_fake_badge, Sprite, { + NEW( m_fake_badge, sprite::Sprite, { m_ism->GetRepaintedInstancedSprite( m_badges_key + "_FAKE", m_badge_defs->GetFakeBadgeSprite(), diff --git a/src/task/game/unit/SlotBadges.h b/src/task/game/unit/SlotBadges.h index 0c42b6ed..fab4e465 100644 --- a/src/task/game/unit/SlotBadges.h +++ b/src/task/game/unit/SlotBadges.h @@ -11,8 +11,10 @@ namespace task { namespace game { +namespace sprite { class Sprite; class InstancedSpriteManager; +} namespace faction { class Faction; @@ -27,13 +29,13 @@ class SlotBadges { SlotBadges( BadgeDefs* badge_defs, - InstancedSpriteManager* ism, + sprite::InstancedSpriteManager* ism, const size_t slot_index, const faction::Faction* faction ); ~SlotBadges(); - Sprite* GetUnitBadgeSprite( const ::game::unit::morale_t morale, const bool is_active ); + sprite::Sprite* GetUnitBadgeSprite( const ::game::unit::morale_t morale, const bool is_active ); const size_t ShowFakeBadge( const types::Vec3& coords, const uint8_t offset ); void HideFakeBadge( const size_t instance_id ); @@ -41,22 +43,22 @@ class SlotBadges { private: BadgeDefs* const m_badge_defs; - InstancedSpriteManager* const m_ism; + sprite::InstancedSpriteManager* const m_ism; const std::string m_badges_key; const faction::Faction* m_faction = nullptr; class slot_sprites_t { public: - Sprite* normal; - Sprite* greyedout; + sprite::Sprite* normal; + sprite::Sprite* greyedout; }; std::unordered_map< ::game::unit::morale_t, slot_sprites_t > m_per_morale_sprites = {}; types::texture::repaint_rules_t m_repaint_rules = {}; types::texture::repaint_rules_t m_fake_badge_repaint_rules = {}; - Sprite* m_fake_badge = nullptr; - Sprite* GetFakeBadge(); + sprite::Sprite* m_fake_badge = nullptr; + sprite::Sprite* GetFakeBadge(); }; } diff --git a/src/task/game/unit/Unit.cpp b/src/task/game/unit/Unit.cpp index b8ff40bf..de573fb7 100644 --- a/src/task/game/unit/Unit.cpp +++ b/src/task/game/unit/Unit.cpp @@ -9,7 +9,7 @@ #include "BadgeDefs.h" #include "SlotBadges.h" #include "UnitManager.h" -#include "task/game/InstancedSprite.h" +#include "task/game/sprite/InstancedSprite.h" #include "scene/actor/Instanced.h" #include "types/mesh/Rectangle.h" #include "scene/actor/Sprite.h" @@ -106,15 +106,15 @@ const size_t Unit::GetSelectionWeight() const { return weight; } -Sprite* Unit::GetSprite() const { +sprite::Sprite* Unit::GetSprite() const { return m_def->GetSprite( m_morale ); } -Sprite* Unit::GetBadgeSprite() const { +sprite::Sprite* Unit::GetBadgeSprite() const { return m_render.badge.def; } -Sprite* Unit::GetBadgeHealthbarSprite() const { +sprite::Sprite* Unit::GetBadgeHealthbarSprite() const { return m_render.badge.healthbar.def; } @@ -350,7 +350,7 @@ const bool Unit::ShouldBeActive() const { return m_is_owned && CanMove(); } -Unit::meshtex_t Unit::GetMeshTex( const InstancedSprite* sprite ) { +Unit::meshtex_t Unit::GetMeshTex( const sprite::InstancedSprite* sprite ) { auto* texture = sprite->actor->GetSpriteActor()->GetTexture(); NEWV( mesh, types::mesh::Rectangle ); mesh->SetCoords( diff --git a/src/task/game/unit/Unit.h b/src/task/game/unit/Unit.h index 72d6cc61..41bbf201 100644 --- a/src/task/game/unit/Unit.h +++ b/src/task/game/unit/Unit.h @@ -22,8 +22,11 @@ namespace game { class Game; class Slot; + +namespace sprite { class Sprite; class InstancedSprite; +} namespace tile { class Tile; @@ -65,9 +68,9 @@ class Unit { const size_t GetSelectionWeight() const; - Sprite* GetSprite() const; - Sprite* GetBadgeSprite() const; - Sprite* GetBadgeHealthbarSprite() const; + sprite::Sprite* GetSprite() const; + sprite::Sprite* GetBadgeSprite() const; + sprite::Sprite* GetBadgeHealthbarSprite() const; const std::string GetNameString() const; const std::string GetStatsString() const; @@ -101,7 +104,6 @@ class Unit { void MoveToTile( tile::Tile* dst_tile ); void UpdateFromTile(); - const bool IsValid() const; const bool IsMoving() const; struct meshtex_t { @@ -130,10 +132,10 @@ class Unit { bool is_rendered = false; size_t instance_id = 0; struct { - Sprite* def = nullptr; + sprite::Sprite* def = nullptr; size_t instance_id = 0; struct { - Sprite* def = nullptr; + sprite::Sprite* def = nullptr; size_t instance_id = 0; } healthbar; struct { @@ -161,7 +163,7 @@ class Unit { util::Scroller< types::Vec3 > m_mover; - Unit::meshtex_t GetMeshTex( const InstancedSprite* sprite ); + Unit::meshtex_t GetMeshTex( const sprite::InstancedSprite* sprite ); void SetRenderCoords( const types::Vec3& coords ); }; diff --git a/src/task/game/unit/UnitDef.cpp b/src/task/game/unit/UnitDef.cpp index 293295d3..bab086b3 100644 --- a/src/task/game/unit/UnitDef.cpp +++ b/src/task/game/unit/UnitDef.cpp @@ -5,14 +5,14 @@ #include "engine/Engine.h" #include "loader/texture/TextureLoader.h" #include "util/String.h" -#include "task/game/InstancedSpriteManager.h" +#include "task/game/sprite/InstancedSpriteManager.h" #include "game/map/Consts.h" namespace task { namespace game { namespace unit { -UnitDef::UnitDef( InstancedSpriteManager* ism, const ::game::unit::Def* unitdef ) +UnitDef::UnitDef( sprite::InstancedSpriteManager* ism, const ::game::unit::Def* unitdef ) : m_ism( ism ) , m_id( unitdef->m_id ) , m_name( unitdef->m_name ) @@ -57,7 +57,7 @@ const bool UnitDef::IsArtillery() const { return m_id != "SporeLauncher"; } -Sprite* UnitDef::GetSprite( const ::game::unit::morale_t morale ) { +sprite::Sprite* UnitDef::GetSprite( const ::game::unit::morale_t morale ) { ASSERT_NOLOG( m_type == ::game::unit::DT_STATIC, "only static units are supported for now" ); ASSERT_NOLOG( static_.render.is_sprite, "only sprite unitdefs are supported for now" ); @@ -89,7 +89,7 @@ Sprite* UnitDef::GetSprite( const ::game::unit::morale_t morale ) { ::game::map::s_consts.tile.scale.x, ::game::map::s_consts.tile.scale.y * ::game::map::s_consts.sprite.y_scale }, - InstancedSpriteManager::ZL_UNITS + ZL_UNITS ), } } @@ -117,7 +117,7 @@ Sprite* UnitDef::GetSprite( const ::game::unit::morale_t morale ) { ::game::map::s_consts.tile.scale.x, ::game::map::s_consts.tile.scale.y * ::game::map::s_consts.sprite.y_scale }, - InstancedSpriteManager::ZL_UNITS + ZL_UNITS ), 1 }; diff --git a/src/task/game/unit/UnitDef.h b/src/task/game/unit/UnitDef.h index 0486b24d..29d7806f 100644 --- a/src/task/game/unit/UnitDef.h +++ b/src/task/game/unit/UnitDef.h @@ -7,7 +7,7 @@ #include "types/Vec3.h" // TODO: remove? -#include "task/game/Sprite.h" +#include "task/game/sprite/Sprite.h" namespace types { namespace texture { @@ -22,18 +22,20 @@ class Def; namespace task { namespace game { +namespace sprite { class InstancedSpriteManager; +} namespace unit { class UnitDef { public: - UnitDef( InstancedSpriteManager* ism, const ::game::unit::Def* unitdef ); + UnitDef( sprite::InstancedSpriteManager* ism, const ::game::unit::Def* unitdef ); ~UnitDef(); const bool IsArtillery() const; - Sprite* GetSprite( const ::game::unit::morale_t morale ); + sprite::Sprite* GetSprite( const ::game::unit::morale_t morale ); const bool IsImmovable() const; @@ -42,7 +44,7 @@ class UnitDef { private: - InstancedSpriteManager* const m_ism; + sprite::InstancedSpriteManager* const m_ism; ::game::unit::sprite_render_info_t m_render = {}; @@ -50,7 +52,7 @@ class UnitDef { std::string m_name; ::game::unit::def_type_t m_type; - typedef std::unordered_map< ::game::unit::morale_t, Sprite > morale_based_sprites_t; + typedef std::unordered_map< ::game::unit::morale_t, sprite::Sprite > morale_based_sprites_t; struct { ::game::unit::movement_type_t movement_type; @@ -59,7 +61,7 @@ class UnitDef { bool is_sprite = false; uint32_t morale_based_xshift = 0; types::texture::Texture* texture = nullptr; - Sprite sprite = {}; + sprite::Sprite sprite = {}; morale_based_sprites_t* morale_based_sprites = nullptr; } render = {}; } static_ = {}; diff --git a/src/task/game/unit/UnitManager.h b/src/task/game/unit/UnitManager.h index 17514349..fb441aab 100644 --- a/src/task/game/unit/UnitManager.h +++ b/src/task/game/unit/UnitManager.h @@ -18,7 +18,10 @@ namespace task { namespace game { class Game; + +namespace sprite { class InstancedSpriteManager; +} namespace tile { class Tile; @@ -79,7 +82,7 @@ CLASS( UnitManager, ::base::Base ) private: Game* m_game; - InstancedSpriteManager* m_ism; + sprite::InstancedSpriteManager* m_ism; const size_t m_slot_index; diff --git a/src/types/Font.cpp b/src/types/Font.cpp index 114e56ce..db36ae9e 100644 --- a/src/types/Font.cpp +++ b/src/types/Font.cpp @@ -1,4 +1,4 @@ -#include +#include #include "Font.h" @@ -6,6 +6,11 @@ namespace types { +Font::Font( const std::string& name ) + : m_name( name ) { + // +} + Font::~Font() { if ( m_graphics_object ) { m_graphics_object->Remove(); diff --git a/src/types/Font.h b/src/types/Font.h index 717f710e..f45258a5 100644 --- a/src/types/Font.h +++ b/src/types/Font.h @@ -11,8 +11,11 @@ class ObjectLink; namespace types { CLASS( Font, base::Base ) + Font( const std::string& name ); virtual ~Font(); + const std::string m_name = ""; + struct dimensions_t { float width; float height; @@ -28,7 +31,7 @@ CLASS( Font, base::Base ) unsigned char* data; }; - std::string m_name = ""; + std::string m_filename = ""; bitmap_t m_symbols[128] = {}; dimensions_t m_dimensions = { 0.0, From d18fa6ed751eb627ffeade40e13734dde0477ceb Mon Sep 17 00:00:00 2001 From: afwbkbc Date: Wed, 19 Jun 2024 22:06:03 +0300 Subject: [PATCH 02/18] wip --- src/task/game/Game.cpp | 10 ++- src/task/game/Types.h | 5 ++ src/task/game/text/InstancedFont.cpp | 80 +++++++++++++++++++-- src/task/game/text/InstancedFont.h | 11 ++- src/task/game/text/InstancedText.cpp | 36 ++++++++-- src/task/game/text/InstancedText.h | 11 ++- src/task/game/text/InstancedTextManager.cpp | 6 +- src/task/game/text/InstancedTextManager.h | 9 ++- 8 files changed, 146 insertions(+), 22 deletions(-) diff --git a/src/task/game/Game.cpp b/src/task/game/Game.cpp index 184f9e3c..2520ed2a 100644 --- a/src/task/game/Game.cpp +++ b/src/task/game/Game.cpp @@ -1144,7 +1144,7 @@ void Game::Initialize( NEW( m_world_scene, scene::Scene, "Game", scene::SCENE_TYPE_ORTHO ); NEW( m_ism, sprite::InstancedSpriteManager, m_world_scene ); - NEW( m_itm, text::InstancedTextManager, m_world_scene ); + NEW( m_itm, text::InstancedTextManager, m_ism ); NEW( m_fm, faction::FactionManager, this ); NEW( m_tm, tile::TileManager, this ); NEW( m_um, unit::UnitManager, this ); @@ -1623,7 +1623,13 @@ void Game::Initialize( ZL_BASES ); - + text->ShowAt( + { + 0.0f, + 0.0f, + 0.0f + } + ); /* text->actor->AddInstance( diff --git a/src/task/game/Types.h b/src/task/game/Types.h index edefab18..8ef328ae 100644 --- a/src/task/game/Types.h +++ b/src/task/game/Types.h @@ -14,6 +14,7 @@ typedef std::vector< sprite::InstancedSprite* > instanced_sprites_t; enum z_level_t { ZL_TERRAIN, + ZL_TERRAIN_TEXT, ZL_BASES, ZL_UNITS, ZL_ANIMATIONS, @@ -25,6 +26,10 @@ static const std::unordered_map< z_level_t, float > s_zlevel_map = { ZL_TERRAIN, 0.4f }, + { + ZL_TERRAIN_TEXT, + 0.5f + }, { ZL_BASES, 0.5f diff --git a/src/task/game/text/InstancedFont.cpp b/src/task/game/text/InstancedFont.cpp index cbeb5bc0..7117da10 100644 --- a/src/task/game/text/InstancedFont.cpp +++ b/src/task/game/text/InstancedFont.cpp @@ -1,20 +1,64 @@ #include "InstancedFont.h" +#include + #include "types/Font.h" #include "types/texture/Texture.h" - -#include "task/game/sprite/Sprite.h" +#include "task/game/sprite/InstancedSpriteManager.h" +#include "engine/Engine.h" +#include "graphics/Graphics.h" namespace task { namespace game { namespace text { -InstancedFont::InstancedFont( const types::Font* font ) - : m_name( font->m_name ) { +InstancedFont::InstancedFont( sprite::InstancedSpriteManager* ism, const types::Font* font ) + : m_ism( ism ) + , m_font( font ) + , m_name( font->m_name ) { // load font into texture - NEW( m_texture, types::texture::Texture, "InstancedFont_" + m_name, font->m_dimensions.width, font->m_dimensions.height ); - m_texture->Fill( 0, 0, m_texture->m_width - 1, m_texture->m_height - 1, types::Color( 1.0f, 0.0f, 0.0f, 1.0f ) ); + + unsigned int w = 0; + unsigned int h = 0; + for ( uint8_t i = 32 ; i < 128 ; i++ ) { + const auto& sym = m_font->m_symbols[ i ]; + w += sym.width; + h = std::max( h, sym.top + sym.height + 1 ); + } + + NEW( m_texture, types::texture::Texture, "InstancedFont_" + m_name, w, h ); + + float cx = 0; + float cy = 0; + unsigned int sym_x = 0; + for ( uint8_t i = 32 ; i < 128 ; i++ ) { + const auto& sym = m_font->m_symbols[ i ]; + const int sym_y = i == 'a' || i == 'b' + ? 1 + cy + sym.top + : 0;//1 + sym.top; + for ( size_t y = 0 ; y < sym.height ; y++ ) { + for ( size_t x = 0 ; x < sym.width ; x++ ) { + m_texture->SetPixel( + sym_x + sym.left + x, + sym_y + y, + types::Color{ + 1.0f, + 1.0f, + 1.0f, + (float)( sym.data[ y * sym.width + x ] ) / 256.0f + } + ); + } + } + cx += sym.ax; + cy += sym.ay; + sym_x += sym.left + sym.width; + } + + //m_texture->SetPixel() + + //m_texture->Fill( 0, 0, m_texture->m_width - 1, m_texture->m_height - 1, types::Color( 1.0f, 0.0f, 0.0f, 1.0f ) ); } @@ -27,6 +71,30 @@ const std::string& InstancedFont::GetFontName() const { return m_name; } +sprite::InstancedSprite* InstancedFont::GetSymbolSprite( const char symbol ) const { + return m_ism->GetInstancedSprite( + "InstancedFont_" + m_name + "_sym_" + std::to_string( symbol ), + m_texture, + { + 0, + 0 + }, + { + (uint32_t)m_texture->m_width, + (uint32_t)m_texture->m_height, + }, + { + 25, + 25 + }, + { + 12.0f, + 1.0f + }, + ZL_TERRAIN_TEXT + ); +} + } } } diff --git a/src/task/game/text/InstancedFont.h b/src/task/game/text/InstancedFont.h index dc9d5673..3af8e926 100644 --- a/src/task/game/text/InstancedFont.h +++ b/src/task/game/text/InstancedFont.h @@ -11,16 +11,25 @@ class Texture; namespace task { namespace game { + +namespace sprite { +class InstancedSpriteManager; +class InstancedSprite; +} + namespace text { class InstancedFont { public: - InstancedFont( const types::Font* font ); + InstancedFont( sprite::InstancedSpriteManager* ism, const types::Font* font ); ~InstancedFont(); const std::string& GetFontName() const; + sprite::InstancedSprite* GetSymbolSprite( const char symbol ) const; private: + sprite::InstancedSpriteManager* m_ism = nullptr; + const types::Font* m_font = nullptr; const std::string m_name = ""; types::texture::Texture* m_texture = nullptr; diff --git a/src/task/game/text/InstancedText.cpp b/src/task/game/text/InstancedText.cpp index 3174b8d9..6510dda5 100644 --- a/src/task/game/text/InstancedText.cpp +++ b/src/task/game/text/InstancedText.cpp @@ -1,17 +1,43 @@ #include "InstancedText.h" +#include "InstancedFont.h" +#include "task/game/sprite/InstancedSprite.h" +#include "scene/actor/Instanced.h" + namespace task { namespace game { namespace text { -InstancedText::InstancedText( const std::string& name, InstancedFont* font ) - : m_name( name ) - , m_font( font ) { - // +InstancedText::InstancedText( const std::string& text, InstancedFont* font ) + : m_text( text ) { + // create instance for every letter + for ( const auto c : text ) { + m_text_sprites.push_back( font->GetSymbolSprite( c ) ); + } + m_instance_ids.reserve( m_text_sprites.size() ); +} + +InstancedText::~InstancedText() { + Hide(); } void InstancedText::ShowAt( const types::Vec3& coords ) { - + Hide(); + for ( size_t i = 0 ; i < m_text_sprites.size() ; i++ ) { + m_instance_ids.push_back( m_text_sprites.at( i )->actor->AddInstance( coords ) ); + break; // TMP + } +} + +void InstancedText::Hide() { + if ( !m_instance_ids.empty() ) { + ASSERT_NOLOG( m_instance_ids.size() == m_text_sprites.size(), "instance ids size mismatch" ); + for ( size_t i = 0 ; i < m_instance_ids.size() ; i++ ) { + m_text_sprites.at( i )->actor->RemoveInstance( m_instance_ids.at( i ) ); + break; // TMP + } + m_instance_ids.clear(); + } } } diff --git a/src/task/game/text/InstancedText.h b/src/task/game/text/InstancedText.h index afb99420..546a878e 100644 --- a/src/task/game/text/InstancedText.h +++ b/src/task/game/text/InstancedText.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include "types/Vec3.h" @@ -9,6 +10,7 @@ namespace game { namespace sprite { class InstancedSprite; +class InstancedSpriteManager; } namespace text { @@ -17,13 +19,16 @@ class InstancedFont; class InstancedText { public: - InstancedText( const std::string& name, InstancedFont* font ); + InstancedText( const std::string& text, InstancedFont* font ); + ~InstancedText(); void ShowAt( const types::Vec3& coords ); + void Hide(); private: - const std::string m_name = ""; - InstancedFont* m_font = nullptr; + const std::string m_text = ""; + std::vector< sprite::InstancedSprite* > m_text_sprites = {}; + std::vector< size_t > m_instance_ids = {}; }; diff --git a/src/task/game/text/InstancedTextManager.cpp b/src/task/game/text/InstancedTextManager.cpp index f65dde7e..f93aeb73 100644 --- a/src/task/game/text/InstancedTextManager.cpp +++ b/src/task/game/text/InstancedTextManager.cpp @@ -8,8 +8,8 @@ namespace task { namespace game { namespace text { -InstancedTextManager::InstancedTextManager( scene::Scene* scene ) - : m_scene( scene ) { +InstancedTextManager::InstancedTextManager( sprite::InstancedSpriteManager* ism ) + : m_ism( ism ) { // } @@ -29,7 +29,7 @@ InstancedFont* InstancedTextManager::GetInstancedFont( const types::Font* font ) it = m_instanced_fonts.insert( { font, - new InstancedFont( font ) + new InstancedFont( m_ism, font ) } ).first; } diff --git a/src/task/game/text/InstancedTextManager.h b/src/task/game/text/InstancedTextManager.h index 4c6cadb7..9ebf9820 100644 --- a/src/task/game/text/InstancedTextManager.h +++ b/src/task/game/text/InstancedTextManager.h @@ -16,6 +16,11 @@ class Scene; namespace task { namespace game { + +namespace sprite { +class InstancedSpriteManager; +} + namespace text { class InstancedFont; @@ -24,7 +29,7 @@ class InstancedText; CLASS( InstancedTextManager, common::Class ) public: - InstancedTextManager( scene::Scene* scene ); + InstancedTextManager( sprite::InstancedSpriteManager* ism ); ~InstancedTextManager(); InstancedFont* GetInstancedFont( const types::Font* font ); @@ -39,7 +44,7 @@ CLASS( InstancedTextManager, common::Class ) private: - scene::Scene* m_scene = nullptr; + sprite::InstancedSpriteManager* m_ism = nullptr; std::unordered_map< const types::Font*, InstancedFont* > m_instanced_fonts = {}; std::unordered_map< const types::Font*, InstancedText* > m_instanced_texts = {}; From bfe080e965fbdeca8a57ecddd127c6786d991c06 Mon Sep 17 00:00:00 2001 From: afwbkbc Date: Fri, 21 Jun 2024 21:53:19 +0300 Subject: [PATCH 03/18] wip --- src/task/game/Game.cpp | 35 +------- src/task/game/base/BaseManager.cpp | 29 +++++++ src/task/game/text/InstancedFont.cpp | 122 +++++++++++++++++++-------- src/task/game/text/InstancedFont.h | 22 ++++- src/task/game/text/InstancedText.cpp | 16 +++- src/task/game/text/InstancedText.h | 1 + 6 files changed, 151 insertions(+), 74 deletions(-) diff --git a/src/task/game/Game.cpp b/src/task/game/Game.cpp index 2520ed2a..68083c11 100644 --- a/src/task/game/Game.cpp +++ b/src/task/game/Game.cpp @@ -1606,40 +1606,7 @@ void Game::Initialize( m_is_resize_handler_set = true; ResetMapState(); - - // TEST - - auto* font = m_itm->GetInstancedFont( g_engine->GetFontLoader()->LoadFont( resource::TTF_ARIALN, 18 ) ); - - auto* text = m_itm->CreateInstancedText( - "X", - font, - types::Color{ - 1.0f, - 0.0f, - 0.0f, - 1.0f - }, - ZL_BASES - ); - - text->ShowAt( - { - 0.0f, - 0.0f, - 0.0f - } - ); - - /* - text->actor->AddInstance( - { - 0.0f, - 0.0f, - 0.0f - } - );*/ - + m_is_initialized = true; } diff --git a/src/task/game/base/BaseManager.cpp b/src/task/game/base/BaseManager.cpp index bce1ff7f..05ee5368 100644 --- a/src/task/game/base/BaseManager.cpp +++ b/src/task/game/base/BaseManager.cpp @@ -6,7 +6,11 @@ #include "task/game/Game.h" #include "task/game/unit/UnitManager.h" #include "task/game/tile/TileManager.h" +#include "task/game/text/InstancedTextManager.h" +#include "task/game/text/InstancedText.h" #include "types/mesh/Rectangle.h" +#include "engine/Engine.h" +#include "loader/font/FontLoader.h" namespace task { namespace game { @@ -65,6 +69,31 @@ void BaseManager::SpawnBase( m_game->RenderTile( tile, m_game->GetUM()->GetSelectedUnit() ); + // TEST + + auto* itm = m_game->GetITM(); + auto* font = itm->GetInstancedFont( g_engine->GetFontLoader()->LoadFont( resource::TTF_ARIALN, 24 ) ); + + auto* text = itm->CreateInstancedText( + "abcdefghijklmnopqrstuvwxyz", + font, + types::Color{ + 1.0f, + 0.0f, + 0.0f, + 1.0f + }, + ZL_BASES + ); + + text->ShowAt( + { + render_coords.x, + render_coords.y - 0.25f, + render_coords.z - 0.25f + } + ); + } /* TODO void BaseManager::DespawnBase( const size_t base_id ) { diff --git a/src/task/game/text/InstancedFont.cpp b/src/task/game/text/InstancedFont.cpp index 7117da10..3fe60d7b 100644 --- a/src/task/game/text/InstancedFont.cpp +++ b/src/task/game/text/InstancedFont.cpp @@ -5,8 +5,6 @@ #include "types/Font.h" #include "types/texture/Texture.h" #include "task/game/sprite/InstancedSpriteManager.h" -#include "engine/Engine.h" -#include "graphics/Graphics.h" namespace task { namespace game { @@ -19,29 +17,26 @@ InstancedFont::InstancedFont( sprite::InstancedSpriteManager* ism, const types:: // load font into texture - unsigned int w = 0; + uint32_t sym_offset = 1; // to keep antialiasing working + + unsigned int w = sym_offset; unsigned int h = 0; for ( uint8_t i = 32 ; i < 128 ; i++ ) { const auto& sym = m_font->m_symbols[ i ]; - w += sym.width; + w += sym.width + sym_offset; h = std::max( h, sym.top + sym.height + 1 ); } - NEW( m_texture, types::texture::Texture, "InstancedFont_" + m_name, w, h ); + NEW( m_texture, types::texture::Texture, "InstancedFont_" + m_name, w, sym_offset + h + sym_offset ); - float cx = 0; - float cy = 0; - unsigned int sym_x = 0; + unsigned int sym_x = sym_offset; for ( uint8_t i = 32 ; i < 128 ; i++ ) { const auto& sym = m_font->m_symbols[ i ]; - const int sym_y = i == 'a' || i == 'b' - ? 1 + cy + sym.top - : 0;//1 + sym.top; for ( size_t y = 0 ; y < sym.height ; y++ ) { for ( size_t x = 0 ; x < sym.width ; x++ ) { m_texture->SetPixel( - sym_x + sym.left + x, - sym_y + y, + sym_x + x, + sym_offset + y, types::Color{ 1.0f, 1.0f, @@ -51,15 +46,44 @@ InstancedFont::InstancedFont( sprite::InstancedSpriteManager* ism, const types:: ); } } - cx += sym.ax; - cy += sym.ay; - sym_x += sym.left + sym.width; + if ( i == 'i' || i == 'j' ) { + int a = 5; + a++; + } + m_symbol_positions.insert( + { + i, + { + { + { + sym_x, + sym_offset + }, + { + sym.width, + sym.height + }, + { + sym_x + sym.width / 2, + sym_offset + sym.height / 2 + } + }, + { + { + sym.left - (int)sym.width / 2, + sym.top - (int)sym.height / 2 + }, + { + sym.ax, + sym.ay + } + } + } + } + ); + sym_x += sym.width + sym_offset; } - //m_texture->SetPixel() - - //m_texture->Fill( 0, 0, m_texture->m_width - 1, m_texture->m_height - 1, types::Color( 1.0f, 0.0f, 0.0f, 1.0f ) ); - } InstancedFont::~InstancedFont() { @@ -71,30 +95,58 @@ const std::string& InstancedFont::GetFontName() const { return m_name; } -sprite::InstancedSprite* InstancedFont::GetSymbolSprite( const char symbol ) const { +sprite::InstancedSprite* InstancedFont::GetSymbolSprite( const unsigned char symbol ) const { + ASSERT_NOLOG( m_symbol_positions.find( symbol ) != m_symbol_positions.end(), "invalid/unsupported symbol: " + std::to_string( symbol ) ); + const auto& pos = m_symbol_positions.find( symbol )->second; return m_ism->GetInstancedSprite( "InstancedFont_" + m_name + "_sym_" + std::to_string( symbol ), m_texture, + pos.src.top_left, + pos.src.width_height, + pos.src.center, { - 0, - 0 - }, - { - (uint32_t)m_texture->m_width, - (uint32_t)m_texture->m_height, - }, - { - 25, - 25 - }, - { - 12.0f, - 1.0f + (float)pos.src.width_height.x * 0.01f, + (float)pos.src.width_height.y * 0.01f }, ZL_TERRAIN_TEXT ); } +const std::vector< types::Vec2< float > > InstancedFont::GetSymbolOffsets( const std::string& text ) const { + std::vector< types::Vec2< float > > offsets = {}; + float total_offset = 0.0f; + float x_center = 0.0f; + const size_t middle = text.size() / 2 - 1; + for ( size_t i = 0 ; i < text.size() ; i++ ) { + const auto symbol = text.at( i ); + ASSERT_NOLOG( m_symbol_positions.find( symbol ) != m_symbol_positions.end(), "invalid/unsupported symbol: " + std::to_string( symbol ) ); + const auto& pos = m_symbol_positions.find( symbol )->second; + offsets.push_back( + { + total_offset + ( pos.dst.top_left.x + pos.dst.advance.x ) * 0.01f, + ( pos.dst.top_left.y ) * 0.01f + } + ); + const float offset = pos.dst.advance.x * 0.01f; + total_offset += offset; + if ( i < middle ) { + x_center += offset; + } + else if ( i == middle ) { + x_center += offset / 2.0f; + } + } + x_center = total_offset / 2.0f; + for ( auto& offset : offsets ) { + offset.x -= x_center; + } + return offsets; + +/* ASSERT_NOLOG( m_symbol_positions.find( symbol ) != m_symbol_positions.end(), "invalid/unsupported symbol: " + std::to_string( symbol ) ); + const auto& pos = m_symbol_positions.find( symbol )->second; + return pos.top_left.x;*/ +} + } } } diff --git a/src/task/game/text/InstancedFont.h b/src/task/game/text/InstancedFont.h index 3af8e926..edf2c03b 100644 --- a/src/task/game/text/InstancedFont.h +++ b/src/task/game/text/InstancedFont.h @@ -1,6 +1,11 @@ #pragma once #include +#include +#include +#include + +#include "game/map/Types.h" namespace types { class Font; @@ -25,9 +30,24 @@ class InstancedFont { ~InstancedFont(); const std::string& GetFontName() const; - sprite::InstancedSprite* GetSymbolSprite( const char symbol ) const; + sprite::InstancedSprite* GetSymbolSprite( const unsigned char symbol ) const; + const std::vector< types::Vec2< float > > GetSymbolOffsets( const std::string& text ) const; private: + + struct symbol_pos_t { + struct { + ::game::map::pcx_texture_coordinates_t top_left; + ::game::map::pcx_texture_coordinates_t width_height; + ::game::map::pcx_texture_coordinates_t center; + } src; + struct { + types::Vec2< int > top_left; + types::Vec2< int > advance; + } dst; + }; + std::unordered_map< uint8_t, symbol_pos_t > m_symbol_positions = {}; + sprite::InstancedSpriteManager* m_ism = nullptr; const types::Font* m_font = nullptr; const std::string m_name = ""; diff --git a/src/task/game/text/InstancedText.cpp b/src/task/game/text/InstancedText.cpp index 6510dda5..d428eaf7 100644 --- a/src/task/game/text/InstancedText.cpp +++ b/src/task/game/text/InstancedText.cpp @@ -11,10 +11,11 @@ namespace text { InstancedText::InstancedText( const std::string& text, InstancedFont* font ) : m_text( text ) { // create instance for every letter - for ( const auto c : text ) { + for ( const auto c : m_text ) { m_text_sprites.push_back( font->GetSymbolSprite( c ) ); } m_instance_ids.reserve( m_text_sprites.size() ); + m_offsets = font->GetSymbolOffsets( m_text ); } InstancedText::~InstancedText() { @@ -24,8 +25,16 @@ InstancedText::~InstancedText() { void InstancedText::ShowAt( const types::Vec3& coords ) { Hide(); for ( size_t i = 0 ; i < m_text_sprites.size() ; i++ ) { - m_instance_ids.push_back( m_text_sprites.at( i )->actor->AddInstance( coords ) ); - break; // TMP + const auto& offsets = m_offsets.at( i ); + m_instance_ids.push_back( + m_text_sprites.at( i )->actor->AddInstance( + { + coords.x + offsets.x, + coords.y + offsets.y, + coords.z + } + ) + ); } } @@ -34,7 +43,6 @@ void InstancedText::Hide() { ASSERT_NOLOG( m_instance_ids.size() == m_text_sprites.size(), "instance ids size mismatch" ); for ( size_t i = 0 ; i < m_instance_ids.size() ; i++ ) { m_text_sprites.at( i )->actor->RemoveInstance( m_instance_ids.at( i ) ); - break; // TMP } m_instance_ids.clear(); } diff --git a/src/task/game/text/InstancedText.h b/src/task/game/text/InstancedText.h index 546a878e..0ac360c3 100644 --- a/src/task/game/text/InstancedText.h +++ b/src/task/game/text/InstancedText.h @@ -29,6 +29,7 @@ class InstancedText { const std::string m_text = ""; std::vector< sprite::InstancedSprite* > m_text_sprites = {}; std::vector< size_t > m_instance_ids = {}; + std::vector< types::Vec2< float > > m_offsets = {}; }; From 349004c8fb3860a1b3d58c4cfc120dc67ac82010 Mon Sep 17 00:00:00 2001 From: afwbkbc Date: Sat, 22 Jun 2024 13:52:50 +0300 Subject: [PATCH 04/18] instanced texts mostly working (need to add/fix shadows) --- GLSMAC_data/default/main.gls.js | 17 +- src/task/game/Types.h | 2 +- src/task/game/base/BaseManager.cpp | 21 +-- src/task/game/faction/Faction.cpp | 7 +- src/task/game/faction/Faction.h | 7 +- src/task/game/text/InstancedFont.cpp | 171 ++++++++++++++------ src/task/game/text/InstancedFont.h | 7 +- src/task/game/text/InstancedText.cpp | 9 +- src/task/game/text/InstancedText.h | 3 +- src/task/game/text/InstancedTextManager.cpp | 19 +-- src/task/game/text/InstancedTextManager.h | 4 +- src/task/game/unit/SlotBadges.cpp | 2 +- src/types/Color.cpp | 9 ++ src/types/Color.h | 2 + src/types/texture/Texture.cpp | 19 +++ src/types/texture/Texture.h | 1 + 16 files changed, 202 insertions(+), 98 deletions(-) diff --git a/GLSMAC_data/default/main.gls.js b/GLSMAC_data/default/main.gls.js index 4f47b997..25ed24b0 100644 --- a/GLSMAC_data/default/main.gls.js +++ b/GLSMAC_data/default/main.gls.js @@ -44,6 +44,7 @@ units.init(); // spawn units let units_spawned = 0; + let bases_spawned = 0; let y = 0; let w = #game.map.get_width(); @@ -53,9 +54,9 @@ units.init(); let x = 0; while (x < w) { 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 owner = random_player(); - let tile = #game.map.get_tile(x, y); let units_count = #game.random.get_int(1, 2); let i = 0; while (i++ < units_count) { @@ -91,16 +92,24 @@ units.init(); } } } + if (#game.random.get_int(0, 2) == 0) { + #game.bases.spawn( + owner, + tile + ); + bases_spawned++; + } } x++; } y++; } #game.message('Total units spawned: ' + #to_string(units_spawned)); + #game.message('Total bases spawned: ' + #to_string(bases_spawned)); // spawn bases - let i = 0; + /*let i = 0; while (i < players_sz) { let base_x = 0; let base_y = 1; @@ -113,7 +122,7 @@ units.init(); #game.map.get_tile(base_x, base_y) ); i++; - } + }*/ }); diff --git a/src/task/game/Types.h b/src/task/game/Types.h index 8ef328ae..e320fc8f 100644 --- a/src/task/game/Types.h +++ b/src/task/game/Types.h @@ -28,7 +28,7 @@ static const std::unordered_map< z_level_t, float > s_zlevel_map = { }, { ZL_TERRAIN_TEXT, - 0.5f + 0.8f }, { ZL_BASES, diff --git a/src/task/game/base/BaseManager.cpp b/src/task/game/base/BaseManager.cpp index 05ee5368..60932333 100644 --- a/src/task/game/base/BaseManager.cpp +++ b/src/task/game/base/BaseManager.cpp @@ -4,10 +4,12 @@ #include "Base.h" #include "task/game/Game.h" +#include "task/game/Slot.h" #include "task/game/unit/UnitManager.h" #include "task/game/tile/TileManager.h" #include "task/game/text/InstancedTextManager.h" #include "task/game/text/InstancedText.h" +#include "task/game/faction/Faction.h" #include "types/mesh/Rectangle.h" #include "engine/Engine.h" #include "loader/font/FontLoader.h" @@ -46,7 +48,7 @@ void BaseManager::SpawnBase( auto* slot = m_game->GetSlot( slot_index ); auto* tile = m_game->GetTM()->GetTile( tile_coords ); - auto* base = m_bases.insert( + m_bases.insert( { base_id, new base::Base( @@ -62,28 +64,19 @@ void BaseManager::SpawnBase( slot_index == m_slot_index ) } - ).first->second; - - types::mesh::Rectangle* mesh = nullptr; - types::texture::Texture* texture = nullptr; + ); m_game->RenderTile( tile, m_game->GetUM()->GetSelectedUnit() ); // TEST auto* itm = m_game->GetITM(); - auto* font = itm->GetInstancedFont( g_engine->GetFontLoader()->LoadFont( resource::TTF_ARIALN, 24 ) ); + auto* font = itm->GetInstancedFont( g_engine->GetFontLoader()->LoadFont( resource::TTF_ARIALN, 48 ) ); auto* text = itm->CreateInstancedText( - "abcdefghijklmnopqrstuvwxyz", + "Base_Name", font, - types::Color{ - 1.0f, - 0.0f, - 0.0f, - 1.0f - }, - ZL_BASES + slot->GetFaction()->m_colors.text ); text->ShowAt( diff --git a/src/task/game/faction/Faction.cpp b/src/task/game/faction/Faction.cpp index 07d511a4..39a07a94 100644 --- a/src/task/game/faction/Faction.cpp +++ b/src/task/game/faction/Faction.cpp @@ -14,7 +14,12 @@ namespace faction { Faction::Faction( const ::game::rules::Faction* def, sprite::InstancedSpriteManager* ism ) : m_ism( ism ) , m_id( def->m_id ) - , m_border_color( def->m_colors.border ) + , m_colors( + { + def->m_colors.text, + def->m_colors.border + } + ) , m_is_progenitor( def->m_flags & ::game::rules::Faction::FF_PROGENITOR ) , m_render( { diff --git a/src/task/game/faction/Faction.h b/src/task/game/faction/Faction.h index 37b36165..642e21c7 100644 --- a/src/task/game/faction/Faction.h +++ b/src/task/game/faction/Faction.h @@ -34,7 +34,12 @@ class Faction { sprite::Sprite* GetBaseSprite( const bool is_water, const uint8_t size, const uint8_t perimeter_level ); const std::string m_id; - const types::Color m_border_color; + + struct { + const types::Color text = {}; + const types::Color border = {}; + } m_colors = {}; + const bool m_is_progenitor; private: diff --git a/src/task/game/text/InstancedFont.cpp b/src/task/game/text/InstancedFont.cpp index 3fe60d7b..953f312b 100644 --- a/src/task/game/text/InstancedFont.cpp +++ b/src/task/game/text/InstancedFont.cpp @@ -10,6 +10,21 @@ namespace task { namespace game { namespace text { +static const types::Vec2< float > s_font_scale = { + 0.004f, + 0.005f, +}; + +static const types::Vec2< uint8_t > s_shadow_offsets = { + 0, + 0 +}; + +static const types::Vec2< uint8_t > s_normal_offsets = { + 1, + 1 +}; + InstancedFont::InstancedFont( sprite::InstancedSpriteManager* ism, const types::Font* font ) : m_ism( ism ) , m_font( font ) @@ -17,7 +32,7 @@ InstancedFont::InstancedFont( sprite::InstancedSpriteManager* ism, const types:: // load font into texture - uint32_t sym_offset = 1; // to keep antialiasing working + const uint32_t sym_offset = 1; // to keep antialiasing working unsigned int w = sym_offset; unsigned int h = 0; @@ -27,29 +42,63 @@ InstancedFont::InstancedFont( sprite::InstancedSpriteManager* ism, const types:: h = std::max( h, sym.top + sym.height + 1 ); } - NEW( m_texture, types::texture::Texture, "InstancedFont_" + m_name, w, sym_offset + h + sym_offset ); - - unsigned int sym_x = sym_offset; - for ( uint8_t i = 32 ; i < 128 ; i++ ) { - const auto& sym = m_font->m_symbols[ i ]; - for ( size_t y = 0 ; y < sym.height ; y++ ) { - for ( size_t x = 0 ; x < sym.width ; x++ ) { - m_texture->SetPixel( - sym_x + x, - sym_offset + y, - types::Color{ - 1.0f, - 1.0f, - 1.0f, - (float)( sym.data[ y * sym.width + x ] ) / 256.0f + NEW( + m_base_texture, + types::texture::Texture, + "InstancedFont_" + m_name + "_BASE", + w + std::max( s_shadow_offsets.x, s_normal_offsets.x ), + sym_offset + h + sym_offset + std::max( s_shadow_offsets.y, s_normal_offsets.y ) + ); + const auto f_paint_base_texture = [ this ]( const types::Vec2< uint8_t >& offsets, const types::Color& multiplier ) -> void { + unsigned int sym_x = sym_offset; + for ( uint8_t i = 32 ; i < 128 ; i++ ) { + const auto& sym = m_font->m_symbols[ i ]; + for ( size_t y = 0 ; y < sym.height ; y++ ) { + for ( size_t x = 0 ; x < sym.width ; x++ ) { + const auto alpha = sym.data[ y * sym.width + x ]; + if ( alpha ) { + const auto px = sym_x + x + offsets.x; + const auto py = sym_offset + y + offsets.y; + const auto c = types::Color::FromRGBA( m_base_texture->GetPixel( px, py ) ); + m_base_texture->SetPixel( + px, + py, + types::Color{ + std::fmax( c.value.red, multiplier.value.red ), + std::fmax( c.value.green, multiplier.value.green ), + std::fmax( c.value.blue, multiplier.value.blue ), + std::fmax( c.value.alpha, (float)alpha / 256.0f * multiplier.value.alpha ) + } + ); } - ); + } } + sym_x += sym.width + sym_offset; + } + }; + f_paint_base_texture( + s_shadow_offsets, types::Color{ + 0.0f, + 0.0f, + 0.0f, + 1.0f + } + ); + f_paint_base_texture( + s_normal_offsets, types::Color{ + 1.0f, + 1.0f, + 1.0f, + 1.0f } - if ( i == 'i' || i == 'j' ) { + ); + unsigned int sym_x = sym_offset; + for ( uint8_t i = 32 ; i < 128 ; i++ ) { + if ( i == ' ' ) { int a = 5; a++; } + const auto& sym = m_font->m_symbols[ i ]; m_symbol_positions.insert( { i, @@ -60,18 +109,18 @@ InstancedFont::InstancedFont( sprite::InstancedSpriteManager* ism, const types:: sym_offset }, { - sym.width, - sym.height + sym.width + s_normal_offsets.x, + sym.height + s_normal_offsets.y }, { - sym_x + sym.width / 2, - sym_offset + sym.height / 2 + sym_x,// + sym.width / 2, + sym_offset,// + sym.height / 2 } }, { { - sym.left - (int)sym.width / 2, - sym.top - (int)sym.height / 2 + sym.left, + sym.top }, { sym.ax, @@ -87,29 +136,55 @@ InstancedFont::InstancedFont( sprite::InstancedSpriteManager* ism, const types:: } InstancedFont::~InstancedFont() { - - DELETE( m_texture ); + for ( const auto& it : m_color_textures ) { + DELETE( it.second ); + } + DELETE( m_base_texture ); } const std::string& InstancedFont::GetFontName() const { return m_name; } -sprite::InstancedSprite* InstancedFont::GetSymbolSprite( const unsigned char symbol ) const { - ASSERT_NOLOG( m_symbol_positions.find( symbol ) != m_symbol_positions.end(), "invalid/unsupported symbol: " + std::to_string( symbol ) ); - const auto& pos = m_symbol_positions.find( symbol )->second; - return m_ism->GetInstancedSprite( - "InstancedFont_" + m_name + "_sym_" + std::to_string( symbol ), - m_texture, - pos.src.top_left, - pos.src.width_height, - pos.src.center, - { - (float)pos.src.width_height.x * 0.01f, - (float)pos.src.width_height.y * 0.01f - }, - ZL_TERRAIN_TEXT - ); +const std::vector< sprite::InstancedSprite* > InstancedFont::GetSymbolSprites( const std::string& text, const types::Color& color ) { + std::vector< sprite::InstancedSprite* > sprites = {}; + const auto texture_key = color.GetRGBA(); + auto texture_it = m_color_textures.find( texture_key ); + if ( texture_it == m_color_textures.end() ) { + NEWV( + texture, + types::texture::Texture, + "InstancedFont_" + m_name + "_" + std::to_string( texture_key ), + m_base_texture->m_width, + m_base_texture->m_height + ); + texture->ColorizeFrom( m_base_texture, color ); + texture_it = m_color_textures.insert( + { + texture_key, + texture, + } + ).first; + } + for ( const auto symbol : text ) { + ASSERT_NOLOG( m_symbol_positions.find( symbol ) != m_symbol_positions.end(), "invalid/unsupported symbol: " + std::to_string( symbol ) ); + const auto& pos = m_symbol_positions.find( symbol )->second; + sprites.push_back( + m_ism->GetInstancedSprite( + "InstancedFont_" + m_name + "_sym_" + std::to_string( symbol ), + texture_it->second, + pos.src.top_left, + pos.src.width_height, + pos.src.center, + { + (float)pos.src.width_height.x * s_font_scale.x, + (float)pos.src.width_height.y * s_font_scale.y + }, + ZL_TERRAIN_TEXT + ) + ); + } + return sprites; } const std::vector< types::Vec2< float > > InstancedFont::GetSymbolOffsets( const std::string& text ) const { @@ -121,13 +196,17 @@ const std::vector< types::Vec2< float > > InstancedFont::GetSymbolOffsets( const const auto symbol = text.at( i ); ASSERT_NOLOG( m_symbol_positions.find( symbol ) != m_symbol_positions.end(), "invalid/unsupported symbol: " + std::to_string( symbol ) ); const auto& pos = m_symbol_positions.find( symbol )->second; + + const float ox = (float)pos.src.width_height.x + pos.dst.top_left.x; + const float oy = pos.dst.top_left.y;// - (float)pos.src.width_height.y / 2.0f; + offsets.push_back( { - total_offset + ( pos.dst.top_left.x + pos.dst.advance.x ) * 0.01f, - ( pos.dst.top_left.y ) * 0.01f + total_offset + ox * s_font_scale.x, + oy * s_font_scale.y } ); - const float offset = pos.dst.advance.x * 0.01f; + const float offset = ( pos.dst.advance.x + pos.dst.top_left.x ) * s_font_scale.x; total_offset += offset; if ( i < middle ) { x_center += offset; @@ -141,10 +220,6 @@ const std::vector< types::Vec2< float > > InstancedFont::GetSymbolOffsets( const offset.x -= x_center; } return offsets; - -/* ASSERT_NOLOG( m_symbol_positions.find( symbol ) != m_symbol_positions.end(), "invalid/unsupported symbol: " + std::to_string( symbol ) ); - const auto& pos = m_symbol_positions.find( symbol )->second; - return pos.top_left.x;*/ } } diff --git a/src/task/game/text/InstancedFont.h b/src/task/game/text/InstancedFont.h index edf2c03b..fee8e66e 100644 --- a/src/task/game/text/InstancedFont.h +++ b/src/task/game/text/InstancedFont.h @@ -7,6 +7,8 @@ #include "game/map/Types.h" +#include "types/Color.h" + namespace types { class Font; namespace texture { @@ -30,7 +32,7 @@ class InstancedFont { ~InstancedFont(); const std::string& GetFontName() const; - sprite::InstancedSprite* GetSymbolSprite( const unsigned char symbol ) const; + const std::vector< sprite::InstancedSprite* > GetSymbolSprites( const std::string& text, const types::Color& color ); const std::vector< types::Vec2< float > > GetSymbolOffsets( const std::string& text ) const; private: @@ -52,7 +54,8 @@ class InstancedFont { const types::Font* m_font = nullptr; const std::string m_name = ""; - types::texture::Texture* m_texture = nullptr; + types::texture::Texture* m_base_texture = nullptr; + std::unordered_map< types::Color::rgba_t, types::texture::Texture* > m_color_textures = {}; }; } diff --git a/src/task/game/text/InstancedText.cpp b/src/task/game/text/InstancedText.cpp index d428eaf7..bdc5fef6 100644 --- a/src/task/game/text/InstancedText.cpp +++ b/src/task/game/text/InstancedText.cpp @@ -8,14 +8,11 @@ namespace task { namespace game { namespace text { -InstancedText::InstancedText( const std::string& text, InstancedFont* font ) +InstancedText::InstancedText( const std::string& text, InstancedFont* font, const types::Color& color ) : m_text( text ) { - // create instance for every letter - for ( const auto c : m_text ) { - m_text_sprites.push_back( font->GetSymbolSprite( c ) ); - } - m_instance_ids.reserve( m_text_sprites.size() ); + m_text_sprites = font->GetSymbolSprites( m_text, color ); m_offsets = font->GetSymbolOffsets( m_text ); + m_instance_ids.reserve( m_text_sprites.size() ); } InstancedText::~InstancedText() { diff --git a/src/task/game/text/InstancedText.h b/src/task/game/text/InstancedText.h index 0ac360c3..c373b2d3 100644 --- a/src/task/game/text/InstancedText.h +++ b/src/task/game/text/InstancedText.h @@ -4,6 +4,7 @@ #include #include "types/Vec3.h" +#include "types/Color.h" namespace task { namespace game { @@ -19,7 +20,7 @@ class InstancedFont; class InstancedText { public: - InstancedText( const std::string& text, InstancedFont* font ); + InstancedText( const std::string& text, InstancedFont* font, const types::Color& color ); ~InstancedText(); void ShowAt( const types::Vec3& coords ); diff --git a/src/task/game/text/InstancedTextManager.cpp b/src/task/game/text/InstancedTextManager.cpp index f93aeb73..4ea85203 100644 --- a/src/task/game/text/InstancedTextManager.cpp +++ b/src/task/game/text/InstancedTextManager.cpp @@ -39,23 +39,10 @@ InstancedFont* InstancedTextManager::GetInstancedFont( const types::Font* font ) InstancedText* InstancedTextManager::CreateInstancedText( const std::string& text, InstancedFont* font, - const types::Color& color, - const z_level_t z_level, - const float z_index_adjustment + const types::Color& color ) { - - ASSERT( s_zlevel_map.find( z_level ) != s_zlevel_map.end(), "unknown z level " + std::to_string( z_level ) ); - ASSERT( z_index_adjustment >= -MAX_ZINDEX_ADJUSTMENT && z_index_adjustment <= MAX_ZINDEX_ADJUSTMENT, "z index adjustment too large" ); - - Log( "Creating instanced text: '" + text + "', " + font->GetFontName() ); - - /* - NEWV( instanced, scene::actor::Instanced, actor ); - instanced->SetZIndex( s_zlevel_map.at( z_level ) + z_index_adjustment ); - m_scene->AddActor( instanced ); - */ - - return new InstancedText( text, font ); + Log( "Creating instanced text: '" + text + "', " + font->GetFontName() + ", " + color.ToString() ); + return new InstancedText( text, font, color ); } } diff --git a/src/task/game/text/InstancedTextManager.h b/src/task/game/text/InstancedTextManager.h index 9ebf9820..46d733cf 100644 --- a/src/task/game/text/InstancedTextManager.h +++ b/src/task/game/text/InstancedTextManager.h @@ -37,9 +37,7 @@ CLASS( InstancedTextManager, common::Class ) InstancedText* CreateInstancedText( const std::string& text, InstancedFont* font, - const types::Color& color, - const z_level_t z_level, - const float z_index_adjustment = 0.0f + const types::Color& color ); private: diff --git a/src/task/game/unit/SlotBadges.cpp b/src/task/game/unit/SlotBadges.cpp index c93b864d..53cf0175 100644 --- a/src/task/game/unit/SlotBadges.cpp +++ b/src/task/game/unit/SlotBadges.cpp @@ -21,7 +21,7 @@ SlotBadges::SlotBadges( , m_badges_key( "Badge_" + std::to_string( slot_index ) ) , m_faction( faction ) { - const auto& c = faction->m_border_color.value; + const auto& c = faction->m_colors.border.value; m_repaint_rules = { { // active types::Color::RGB( 252, 0, 0 ), diff --git a/src/types/Color.cpp b/src/types/Color.cpp index 073dcfaa..db4c85ee 100644 --- a/src/types/Color.cpp +++ b/src/types/Color.cpp @@ -73,6 +73,15 @@ const Color Color::operator/( float operand ) const { }; } +const Color Color::operator*( const Color& other ) const { + return { + value.red * other.value.red, + value.green * other.value.green, + value.blue * other.value.blue, + value.alpha * other.value.alpha, + }; +} + const Color::rgba_t Color::GetRGBA() const { return RGBA( value.red * 255, value.green * 255, value.blue * 255, value.alpha * 255 ); }; diff --git a/src/types/Color.h b/src/types/Color.h index b5357532..b624a3ff 100644 --- a/src/types/Color.h +++ b/src/types/Color.h @@ -42,6 +42,8 @@ class Color { const Color operator*( const float operand ) const; const Color operator/( const float operand ) const; + const Color operator*( const Color& other ) const; + const rgba_t GetRGBA() const; static Color FromRGBA( const rgba_t rgba ); static Color FromRGBA( const uint8_t red, const uint8_t green, const uint8_t blue, const uint8_t alpha = 255 ); diff --git a/src/types/texture/Texture.cpp b/src/types/texture/Texture.cpp index 8f6009c0..b1c1f682 100644 --- a/src/types/texture/Texture.cpp +++ b/src/types/texture/Texture.cpp @@ -746,6 +746,25 @@ void Texture::RepaintFrom( const types::texture::Texture* original, const repain } +void Texture::ColorizeFrom( const types::texture::Texture* original, const types::Color& color ) { + ASSERT( m_width == original->m_width, "repaint width mismatch" ); + ASSERT( m_height == original->m_height, "repaint width mismatch" ); + ASSERT( m_bpp == original->m_bpp, "repaint bpp mismatch" ); + ASSERT( m_bitmap, "bitmap not set" ); + ASSERT( original->m_bitmap, "original bitmap not set" ); + + uint32_t rgba; + repaint_rules_t::const_iterator rule_it; + for ( size_t y = 0 ; y < m_height ; y++ ) { + for ( size_t x = 0 ; x < m_width ; x++ ) { + const auto idx = ( y * m_width + x ) * m_bpp; + memcpy( &rgba, ptr( original->m_bitmap, idx, m_bpp ), m_bpp ); + rgba = ( types::Color::FromRGBA( rgba ) * color ).GetRGBA(); + memcpy( ptr( m_bitmap, idx, m_bpp ), &rgba, m_bpp ); + } + } +} + void Texture::Rotate() { unsigned char* new_bitmap = (unsigned char*)malloc( m_bitmap_size ); diff --git a/src/types/texture/Texture.h b/src/types/texture/Texture.h index 7d2cdad0..83de9411 100644 --- a/src/types/texture/Texture.h +++ b/src/types/texture/Texture.h @@ -74,6 +74,7 @@ CLASS( Texture, Serializable ) void Fill( const size_t x1, const size_t y1, const size_t x2, const size_t y2, const types::Color& color ); void RepaintFrom( const types::texture::Texture* original, const repaint_rules_t& rules ); + void ColorizeFrom( const types::texture::Texture* original, const types::Color& color ); void Rotate(); void FlipV(); From 25db858db56180347432d45490dec427a0c782f8 Mon Sep 17 00:00:00 2001 From: afwbkbc Date: Sat, 22 Jun 2024 13:57:37 +0300 Subject: [PATCH 05/18] fixed release build --- src/resource/Types.h | 2 ++ src/types/Font.cpp | 1 + 2 files changed, 3 insertions(+) diff --git a/src/resource/Types.h b/src/resource/Types.h index 418ad50e..b8890f04 100644 --- a/src/resource/Types.h +++ b/src/resource/Types.h @@ -1,5 +1,7 @@ #pragma once +#include + namespace resource { enum resource_t : uint8_t { diff --git a/src/types/Font.cpp b/src/types/Font.cpp index 80658fc0..3e13e971 100644 --- a/src/types/Font.cpp +++ b/src/types/Font.cpp @@ -1,4 +1,5 @@ #include +#include #include "Font.h" From 3033ceb631b880f1389853ddb1b936a66e1aeba9 Mon Sep 17 00:00:00 2001 From: afwbkbc Date: Sun, 23 Jun 2024 13:47:36 +0300 Subject: [PATCH 06/18] refactored factions --- GLSMAC_data/default/factions.gls.js | 50 +++++-------------- GLSMAC_data/default/factions/angels.gls.js | 16 ++++++ GLSMAC_data/default/factions/believers.gls.js | 16 ++++++ .../default/factions/caretakers.gls.js | 17 +++++++ .../default/factions/consciousness.gls.js | 16 ++++++ GLSMAC_data/default/factions/drones.gls.js | 16 ++++++ GLSMAC_data/default/factions/gaians.gls.js | 16 ++++++ GLSMAC_data/default/factions/hive.gls.js | 16 ++++++ .../default/factions/morganites.gls.js | 16 ++++++ .../default/factions/peacekeepers.gls.js | 16 ++++++ GLSMAC_data/default/factions/pirates.gls.js | 17 +++++++ .../default/factions/planetcult.gls.js | 16 ++++++ GLSMAC_data/default/factions/spartans.gls.js | 16 ++++++ .../default/factions/university.gls.js | 16 ++++++ GLSMAC_data/default/factions/usurpers.gls.js | 17 +++++++ GLSMAC_data/default/main.gls.js | 11 ++-- src/game/bindings/Factions.cpp | 4 ++ src/game/rules/Faction.cpp | 3 -- src/game/rules/Faction.h | 5 +- src/gse/builtins/Builtins.cpp | 1 + src/gse/builtins/Builtins.h | 2 + src/gse/builtins/CMakeLists.txt | 1 + src/gse/builtins/String.cpp | 33 ++++++++++++ src/gse/builtins/String.h | 14 ++++++ src/gse/callable/Native.h | 5 ++ 25 files changed, 305 insertions(+), 51 deletions(-) create mode 100644 GLSMAC_data/default/factions/angels.gls.js create mode 100644 GLSMAC_data/default/factions/believers.gls.js create mode 100644 GLSMAC_data/default/factions/caretakers.gls.js create mode 100644 GLSMAC_data/default/factions/consciousness.gls.js create mode 100644 GLSMAC_data/default/factions/drones.gls.js create mode 100644 GLSMAC_data/default/factions/gaians.gls.js create mode 100644 GLSMAC_data/default/factions/hive.gls.js create mode 100644 GLSMAC_data/default/factions/morganites.gls.js create mode 100644 GLSMAC_data/default/factions/peacekeepers.gls.js create mode 100644 GLSMAC_data/default/factions/pirates.gls.js create mode 100644 GLSMAC_data/default/factions/planetcult.gls.js create mode 100644 GLSMAC_data/default/factions/spartans.gls.js create mode 100644 GLSMAC_data/default/factions/university.gls.js create mode 100644 GLSMAC_data/default/factions/usurpers.gls.js create mode 100644 src/gse/builtins/String.cpp create mode 100644 src/gse/builtins/String.h diff --git a/GLSMAC_data/default/factions.gls.js b/GLSMAC_data/default/factions.gls.js index 02fde6c4..1a470597 100644 --- a/GLSMAC_data/default/factions.gls.js +++ b/GLSMAC_data/default/factions.gls.js @@ -1,40 +1,14 @@ -const faction = (id, name, text_color, border_color, pcx, extra) => { - return [id, { - name: name, - colors: { - text: text_color, - border: border_color, - }, - bases: { - render: { - type: 'sprite_grid', - file: pcx, - grid_x: 1, grid_y: 1, - cell_width: 100, cell_height: 75, - //cell_cx: 50, cell_cy: 38, - cell_padding: 1, - /*scale_x: 0.7, - scale_y: 0.7,*/ - } - }, - } + extra]; -}; +return { -const factions = [ - faction('GAIANS', 'Gaians', #to_color(16, 228, 0), #to_color(0, 252, 0), 'gaians.pcx', {}), - faction('HIVE', 'Hive', #to_color(0, 97, 255), #to_color(0, 0, 255), 'hive.pcx', {}), - faction('UNIVERSITY', 'University', #to_color(216, 224, 244), #to_color(255, 255, 255), 'univ.pcx', {}), - faction('MORGANITES', 'Morganites', #to_color(255, 255, 0), #to_color(255, 255, 0), 'morgan.pcx', {}), - faction('SPARTANS', 'Spartans', #to_color(136, 166, 166), #to_color(0, 0, 0), 'spartans.pcx', {}), - faction('BELIEVERS', 'Believers', #to_color(224, 156, 28), #to_color(236, 92, 0), 'believe.pcx', {}), - faction('PEACEKEEPERS', 'Peacekeepers', #to_color(164, 176, 232), #to_color(164, 176, 232), 'peace.pcx', {}), - faction('CONSCIOUSNESS', 'Consciousness', #to_color(44, 128, 104), #to_color(44, 128, 104), 'cyborg.pcx', {}), - faction('PIRATES', 'Pirates', #to_color(0, 255, 255), #to_color(0, 255, 255), 'pirates.pcx', {}), - faction('DRONES', 'Drones', #to_color(173, 196, 192), #to_color(136, 12, 12), 'drone.pcx', {}), - faction('ANGELS', 'Angels', #to_color(103, 91, 181), #to_color(103, 91, 181), 'angels.pcx', {}), - faction('PLANETCULT', 'Planet Cult', #to_color(232, 84, 84), #to_color(232, 84, 84), 'fungboy.pcx', {}), - faction('CARETAKERS', 'Caretakers', #to_color(116, 156, 56), #to_color(116, 156, 56), 'caretake.pcx', {is_progenitor: true}), - faction('USURPERS', 'Usurpers', #to_color(212, 208, 116), #to_color(212, 208, 116), 'usurper.pcx', {is_progenitor: true}), -]; + define: () => { + const factions = ['gaians', 'hive', 'university', 'morganites', 'spartans', 'believers', 'peacekeepers', 'consciousness', 'pirates', 'drones', 'angels', 'planetcult', 'caretakers', 'usurpers']; + let i = 0; + let sz = #size(factions); + while (i < sz) { + #print(#to_uppercase(factions[i])); + #game.factions.define(#to_uppercase(factions[i]), #include('./factions/' + factions[i])); + i++; + } + }, -return factions; +}; diff --git a/GLSMAC_data/default/factions/angels.gls.js b/GLSMAC_data/default/factions/angels.gls.js new file mode 100644 index 00000000..f593f480 --- /dev/null +++ b/GLSMAC_data/default/factions/angels.gls.js @@ -0,0 +1,16 @@ +return { + name: 'Angels', + colors: { + text: #to_color(103, 91, 181), + border: #to_color(103, 91, 181), + }, + bases: { + render: { + type: 'sprite_grid', + file: 'angels.pcx', + grid_x: 1, grid_y: 1, + cell_width: 100, cell_height: 75, + cell_padding: 1, + }, + }, +}; diff --git a/GLSMAC_data/default/factions/believers.gls.js b/GLSMAC_data/default/factions/believers.gls.js new file mode 100644 index 00000000..13686ba9 --- /dev/null +++ b/GLSMAC_data/default/factions/believers.gls.js @@ -0,0 +1,16 @@ +return { + name: 'Believers', + colors: { + text: #to_color(224, 156, 28), + border: #to_color(236, 92, 0), + }, + bases: { + render: { + type: 'sprite_grid', + file: 'believe.pcx', + grid_x: 1, grid_y: 1, + cell_width: 100, cell_height: 75, + cell_padding: 1, + }, + }, +}; diff --git a/GLSMAC_data/default/factions/caretakers.gls.js b/GLSMAC_data/default/factions/caretakers.gls.js new file mode 100644 index 00000000..2a0e034a --- /dev/null +++ b/GLSMAC_data/default/factions/caretakers.gls.js @@ -0,0 +1,17 @@ +return { + name: 'Caretakers', + colors: { + text: #to_color(116, 156, 56), + border: #to_color(116, 156, 56), + }, + bases: { + render: { + type: 'sprite_grid', + file: 'caretake.pcx', + grid_x: 1, grid_y: 1, + cell_width: 100, cell_height: 75, + cell_padding: 1, + }, + }, + is_progenitor: true, +}; diff --git a/GLSMAC_data/default/factions/consciousness.gls.js b/GLSMAC_data/default/factions/consciousness.gls.js new file mode 100644 index 00000000..bfd0a8ed --- /dev/null +++ b/GLSMAC_data/default/factions/consciousness.gls.js @@ -0,0 +1,16 @@ +return { + name: 'Consciousness', + colors: { + text: #to_color(44, 128, 104), + border: #to_color(44, 128, 104), + }, + bases: { + render: { + type: 'sprite_grid', + file: 'cyborg.pcx', + grid_x: 1, grid_y: 1, + cell_width: 100, cell_height: 75, + cell_padding: 1, + }, + }, +}; diff --git a/GLSMAC_data/default/factions/drones.gls.js b/GLSMAC_data/default/factions/drones.gls.js new file mode 100644 index 00000000..c142dbbe --- /dev/null +++ b/GLSMAC_data/default/factions/drones.gls.js @@ -0,0 +1,16 @@ +return { + name: 'Drones', + colors: { + text: #to_color(173, 196, 192), + border: #to_color(136, 12, 12), + }, + bases: { + render: { + type: 'sprite_grid', + file: 'drone.pcx', + grid_x: 1, grid_y: 1, + cell_width: 100, cell_height: 75, + cell_padding: 1, + }, + }, +}; diff --git a/GLSMAC_data/default/factions/gaians.gls.js b/GLSMAC_data/default/factions/gaians.gls.js new file mode 100644 index 00000000..7a0e5784 --- /dev/null +++ b/GLSMAC_data/default/factions/gaians.gls.js @@ -0,0 +1,16 @@ +return { + name: 'Gaians', + colors: { + text: #to_color(16, 228, 0), + border: #to_color(0, 252, 0), + }, + bases: { + render: { + type: 'sprite_grid', + file: 'gaians.pcx', + grid_x: 1, grid_y: 1, + cell_width: 100, cell_height: 75, + cell_padding: 1, + }, + }, +}; diff --git a/GLSMAC_data/default/factions/hive.gls.js b/GLSMAC_data/default/factions/hive.gls.js new file mode 100644 index 00000000..01f8c1f0 --- /dev/null +++ b/GLSMAC_data/default/factions/hive.gls.js @@ -0,0 +1,16 @@ +return { + name: 'Hive', + colors: { + text: #to_color(0, 97, 255), + border: #to_color(0, 0, 255), + }, + bases: { + render: { + type: 'sprite_grid', + file: 'hive.pcx', + grid_x: 1, grid_y: 1, + cell_width: 100, cell_height: 75, + cell_padding: 1, + }, + }, +}; diff --git a/GLSMAC_data/default/factions/morganites.gls.js b/GLSMAC_data/default/factions/morganites.gls.js new file mode 100644 index 00000000..9b895345 --- /dev/null +++ b/GLSMAC_data/default/factions/morganites.gls.js @@ -0,0 +1,16 @@ +return { + name: 'Morganites', + colors: { + text: #to_color(255, 255, 0), + border: #to_color(255, 255, 0), + }, + bases: { + render: { + type: 'sprite_grid', + file: 'morgan.pcx', + grid_x: 1, grid_y: 1, + cell_width: 100, cell_height: 75, + cell_padding: 1, + }, + }, +}; diff --git a/GLSMAC_data/default/factions/peacekeepers.gls.js b/GLSMAC_data/default/factions/peacekeepers.gls.js new file mode 100644 index 00000000..05c40ade --- /dev/null +++ b/GLSMAC_data/default/factions/peacekeepers.gls.js @@ -0,0 +1,16 @@ +return { + name: 'Peacekeepers', + colors: { + text: #to_color(164, 176, 232), + border: #to_color(164, 176, 232), + }, + bases: { + render: { + type: 'sprite_grid', + file: 'peace.pcx', + grid_x: 1, grid_y: 1, + cell_width: 100, cell_height: 75, + cell_padding: 1, + }, + }, +}; diff --git a/GLSMAC_data/default/factions/pirates.gls.js b/GLSMAC_data/default/factions/pirates.gls.js new file mode 100644 index 00000000..47220a7a --- /dev/null +++ b/GLSMAC_data/default/factions/pirates.gls.js @@ -0,0 +1,17 @@ +return { + name: 'Pirates', + colors: { + text: #to_color(0, 255, 255), + border: #to_color(0, 255, 255), + }, + bases: { + render: { + type: 'sprite_grid', + file: 'pirates.pcx', + grid_x: 1, grid_y: 1, + cell_width: 100, cell_height: 75, + cell_padding: 1, + }, + }, + is_naval: true, +}; diff --git a/GLSMAC_data/default/factions/planetcult.gls.js b/GLSMAC_data/default/factions/planetcult.gls.js new file mode 100644 index 00000000..389027b6 --- /dev/null +++ b/GLSMAC_data/default/factions/planetcult.gls.js @@ -0,0 +1,16 @@ +return { + name: 'Planet Cult', + colors: { + text: #to_color(232, 84, 84), + border: #to_color(232, 84, 84), + }, + bases: { + render: { + type: 'sprite_grid', + file: 'fungboy.pcx', + grid_x: 1, grid_y: 1, + cell_width: 100, cell_height: 75, + cell_padding: 1, + }, + }, +}; diff --git a/GLSMAC_data/default/factions/spartans.gls.js b/GLSMAC_data/default/factions/spartans.gls.js new file mode 100644 index 00000000..b0fa1f1e --- /dev/null +++ b/GLSMAC_data/default/factions/spartans.gls.js @@ -0,0 +1,16 @@ +return { + name: 'Spartans', + colors: { + text: #to_color(136, 166, 166), + border: #to_color(0, 0, 0), + }, + bases: { + render: { + type: 'sprite_grid', + file: 'spartans.pcx', + grid_x: 1, grid_y: 1, + cell_width: 100, cell_height: 75, + cell_padding: 1, + }, + }, +}; diff --git a/GLSMAC_data/default/factions/university.gls.js b/GLSMAC_data/default/factions/university.gls.js new file mode 100644 index 00000000..088bf1af --- /dev/null +++ b/GLSMAC_data/default/factions/university.gls.js @@ -0,0 +1,16 @@ +return { + name: 'University', + colors: { + text: #to_color(216, 224, 244), + border: #to_color(255, 255, 255), + }, + bases: { + render: { + type: 'sprite_grid', + file: 'univ.pcx', + grid_x: 1, grid_y: 1, + cell_width: 100, cell_height: 75, + cell_padding: 1, + }, + }, +}; diff --git a/GLSMAC_data/default/factions/usurpers.gls.js b/GLSMAC_data/default/factions/usurpers.gls.js new file mode 100644 index 00000000..71edb66a --- /dev/null +++ b/GLSMAC_data/default/factions/usurpers.gls.js @@ -0,0 +1,17 @@ +return { + name: 'Usurpers', + colors: { + text: #to_color(212, 208, 116), + border: #to_color(212, 208, 116), + }, + bases: { + render: { + type: 'sprite_grid', + file: 'usurper.pcx', + grid_x: 1, grid_y: 1, + cell_width: 100, cell_height: 75, + cell_padding: 1, + }, + }, + is_progenitor: true, +}; diff --git a/GLSMAC_data/default/main.gls.js b/GLSMAC_data/default/main.gls.js index 25ed24b0..079d59dc 100644 --- a/GLSMAC_data/default/main.gls.js +++ b/GLSMAC_data/default/main.gls.js @@ -1,17 +1,12 @@ const units = #include('units'); +const factions = #include('factions'); #game.on.configure((e) => { - const factions = #include('factions'); - let i = 0; - let sz = #size(factions); - while (i < sz) { - #game.factions.define(factions[i][0], factions[i][1]); - i++; - } + factions.define(); - const rules = #include('rules'); // TODO + const rules = #include('rules'); }); diff --git a/src/game/bindings/Factions.cpp b/src/game/bindings/Factions.cpp index d3c364fd..c139923b 100644 --- a/src/game/bindings/Factions.cpp +++ b/src/game/bindings/Factions.cpp @@ -38,6 +38,10 @@ BINDING_IMPL( factions ) { N_GETPROP_UNWRAP( colors_border, colors, "border", types::Color ); faction.m_colors = { colors_text, 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; diff --git a/src/game/rules/Faction.cpp b/src/game/rules/Faction.cpp index 867b8ce8..65a8d45a 100644 --- a/src/game/rules/Faction.cpp +++ b/src/game/rules/Faction.cpp @@ -9,9 +9,6 @@ namespace game { namespace rules { -const Faction::faction_flag_t Faction::FF_NONE = 0; -const Faction::faction_flag_t Faction::FF_PROGENITOR = 1 << 0; - Faction::Faction() { // } diff --git a/src/game/rules/Faction.h b/src/game/rules/Faction.h index d6950c6a..3828030a 100644 --- a/src/game/rules/Faction.h +++ b/src/game/rules/Faction.h @@ -17,8 +17,9 @@ CLASS2( Faction, types::Serializable, gse::Wrappable ) Faction( const std::string& id, const std::string& name ); typedef uint8_t faction_flag_t; - static const faction_flag_t FF_NONE; - static const faction_flag_t FF_PROGENITOR; + 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; std::string m_id = ""; std::string m_name = ""; diff --git a/src/gse/builtins/Builtins.cpp b/src/gse/builtins/Builtins.cpp index 06335ee6..55ac79a2 100644 --- a/src/gse/builtins/Builtins.cpp +++ b/src/gse/builtins/Builtins.cpp @@ -9,6 +9,7 @@ void Builtins::AddToContext( context::Context* ctx ) { m_console.AddToContext( ctx ); m_conversions.AddToContext( ctx ); m_math.AddToContext( ctx ); + m_string.AddToContext( ctx ); } } diff --git a/src/gse/builtins/Builtins.h b/src/gse/builtins/Builtins.h index 6cb78fe5..e087ee4e 100644 --- a/src/gse/builtins/Builtins.h +++ b/src/gse/builtins/Builtins.h @@ -8,6 +8,7 @@ #include "Include.h" #include "Conversions.h" #include "Math.h" +#include "String.h" namespace gse { namespace builtins { @@ -27,6 +28,7 @@ class Builtins : public Bindings { Console m_console = {}; Conversions m_conversions = {}; Math m_math = {}; + String m_string = {}; }; diff --git a/src/gse/builtins/CMakeLists.txt b/src/gse/builtins/CMakeLists.txt index 949ad7a6..e018a4e8 100644 --- a/src/gse/builtins/CMakeLists.txt +++ b/src/gse/builtins/CMakeLists.txt @@ -6,5 +6,6 @@ SET( SRC ${SRC} ${PWD}/Console.cpp ${PWD}/Conversions.cpp ${PWD}/Math.cpp + ${PWD}/String.cpp PARENT_SCOPE ) diff --git a/src/gse/builtins/String.cpp b/src/gse/builtins/String.cpp new file mode 100644 index 00000000..50586ece --- /dev/null +++ b/src/gse/builtins/String.cpp @@ -0,0 +1,33 @@ +#include "String.h" + +#include + +#include "gse/context/Context.h" +#include "gse/callable/Native.h" +#include "gse/Exception.h" +#include "gse/type/String.h" +#include "gse/type/Undefined.h" + +namespace gse { +namespace builtins { + +void String::AddToContext( context::Context* ctx ) { + + ctx->CreateBuiltin( "to_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() { + N_EXPECT_ARGS( 1 ); + N_GETVALUE_NONCONST( text, 0, String ); + std::transform( text.begin(), text.end(), text.begin(), ::tolower ); + return VALUE( type::String, text ); + } ) ); + +} + +} +} diff --git a/src/gse/builtins/String.h b/src/gse/builtins/String.h new file mode 100644 index 00000000..7314dbe1 --- /dev/null +++ b/src/gse/builtins/String.h @@ -0,0 +1,14 @@ +#pragma once + +#include "gse/Bindings.h" + +namespace gse { +namespace builtins { + +class String : public Bindings { +public: + void AddToContext( context::Context* ctx ) override; +}; + +} +} diff --git a/src/gse/callable/Native.h b/src/gse/callable/Native.h index fccab14c..f8905933 100644 --- a/src/gse/callable/Native.h +++ b/src/gse/callable/Native.h @@ -50,6 +50,11 @@ namespace callable { const auto& _var = (gse::type::Callable*)arg; #define N_UNPERSIST_CALLABLE( _callable ) \ ctx->UnpersistValue( _callable ); +#define N_GETVALUE_NONCONST( _var, _index, _type ) \ + ASSERT_NOLOG( _index < arguments.size(), "argument index overflow" ); \ + arg = arguments.at( _index ).Get(); \ + N_CHECKARG( arg, _index, _type ); \ + auto _var = ((gse::type::_type*)arg)->value; #define N_GETVALUE( _var, _index, _type ) \ ASSERT_NOLOG( _index < arguments.size(), "argument index overflow" ); \ arg = arguments.at( _index ).Get(); \ From 852e3a77ab7929b576c7c524095cfc9614585323 Mon Sep 17 00:00:00 2001 From: afwbkbc Date: Sun, 23 Jun 2024 17:09:43 +0300 Subject: [PATCH 07/18] gse: implemented for loops, refactored scripts --- GLSMAC_data/default/factions.gls.js | 24 +++- GLSMAC_data/default/main.gls.js | 11 +- GLSMAC_data/default/units/animations.gls.js | 7 +- GLSMAC_data/default/units/defs.gls.js | 16 +-- GLSMAC_data/default/units/movement.gls.js | 7 +- GLSMAC_data/tests/types.gls.js | 139 ++++++++++---------- src/gse/context/Context.cpp | 9 ++ src/gse/context/Context.h | 1 + src/gse/parser/JS.cpp | 113 ++++++++++++++-- src/gse/parser/JS.h | 4 + src/gse/parser/Parser.h | 6 +- src/gse/program/CMakeLists.txt | 6 + src/gse/program/Condition.cpp | 13 ++ src/gse/program/Condition.h | 25 ++++ src/gse/program/Conditional.h | 1 + src/gse/program/Element.h | 1 + src/gse/program/ElseIf.cpp | 4 +- src/gse/program/ElseIf.h | 6 +- src/gse/program/For.cpp | 31 +++++ src/gse/program/For.h | 24 ++++ src/gse/program/ForCondition.cpp | 11 ++ src/gse/program/ForCondition.h | 26 ++++ src/gse/program/ForConditionExpressions.cpp | 43 ++++++ src/gse/program/ForConditionExpressions.h | 24 ++++ src/gse/program/ForConditionInOf.cpp | 47 +++++++ src/gse/program/ForConditionInOf.h | 31 +++++ src/gse/program/If.cpp | 4 +- src/gse/program/If.h | 6 +- src/gse/program/SimpleCondition.cpp | 37 ++++++ src/gse/program/SimpleCondition.h | 22 ++++ src/gse/program/While.cpp | 4 +- src/gse/program/While.h | 6 +- src/gse/runner/Interpreter.cpp | 94 ++++++++++++- src/gse/tests/Parser.cpp | 32 ++++- src/gse/tests/Tests.cpp | 82 +++++++----- 35 files changed, 744 insertions(+), 173 deletions(-) create mode 100644 src/gse/program/Condition.cpp create mode 100644 src/gse/program/Condition.h create mode 100644 src/gse/program/For.cpp create mode 100644 src/gse/program/For.h create mode 100644 src/gse/program/ForCondition.cpp create mode 100644 src/gse/program/ForCondition.h create mode 100644 src/gse/program/ForConditionExpressions.cpp create mode 100644 src/gse/program/ForConditionExpressions.h create mode 100644 src/gse/program/ForConditionInOf.cpp create mode 100644 src/gse/program/ForConditionInOf.h create mode 100644 src/gse/program/SimpleCondition.cpp create mode 100644 src/gse/program/SimpleCondition.h diff --git a/GLSMAC_data/default/factions.gls.js b/GLSMAC_data/default/factions.gls.js index 1a470597..425d3c04 100644 --- a/GLSMAC_data/default/factions.gls.js +++ b/GLSMAC_data/default/factions.gls.js @@ -1,13 +1,23 @@ return { define: () => { - const factions = ['gaians', 'hive', 'university', 'morganites', 'spartans', 'believers', 'peacekeepers', 'consciousness', 'pirates', 'drones', 'angels', 'planetcult', 'caretakers', 'usurpers']; - let i = 0; - let sz = #size(factions); - while (i < sz) { - #print(#to_uppercase(factions[i])); - #game.factions.define(#to_uppercase(factions[i]), #include('./factions/' + factions[i])); - i++; + for (faction of [ + 'gaians', + 'hive', + 'university', + 'morganites', + 'spartans', + 'believers', + 'peacekeepers', + 'consciousness', + 'pirates', + 'drones', + 'angels', + 'planetcult', + 'caretakers', + 'usurpers' + ]) { + #game.factions.define(#to_uppercase(faction), #include('./factions/' + faction)); } }, diff --git a/GLSMAC_data/default/main.gls.js b/GLSMAC_data/default/main.gls.js index 079d59dc..80401663 100644 --- a/GLSMAC_data/default/main.gls.js +++ b/GLSMAC_data/default/main.gls.js @@ -41,20 +41,17 @@ units.init(); let units_spawned = 0; let bases_spawned = 0; - let y = 0; let w = #game.map.get_width(); let h = #game.map.get_height(); - while (y < h) { - let x = 0; - while (x < w) { + 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); - let i = 0; - while (i++ < units_count) { + 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()); @@ -95,9 +92,7 @@ units.init(); bases_spawned++; } } - x++; } - y++; } #game.message('Total units spawned: ' + #to_string(units_spawned)); #game.message('Total bases spawned: ' + #to_string(bases_spawned)); diff --git a/GLSMAC_data/default/units/animations.gls.js b/GLSMAC_data/default/units/animations.gls.js index 749dedcf..abc15e55 100644 --- a/GLSMAC_data/default/units/animations.gls.js +++ b/GLSMAC_data/default/units/animations.gls.js @@ -38,11 +38,8 @@ const result = { ]; - let sz = #size(animations); - let i = 0; - while (i < sz) { - #game.animations.define(animations[i][0], animations[i][1]); - i++; + for (animation of animations) { + #game.animations.define(animation[0], animation[1]); } }, diff --git a/GLSMAC_data/default/units/defs.gls.js b/GLSMAC_data/default/units/defs.gls.js index c301a735..220e47ec 100644 --- a/GLSMAC_data/default/units/defs.gls.js +++ b/GLSMAC_data/default/units/defs.gls.js @@ -41,20 +41,12 @@ const units = [ const result = { define: () => { - // TODO: for loops - - let i = 0; - let sz = #size(morales); - while (i < sz) { - #game.units.define_morales(morales[i][0], morales[i][1]); - i++; + for (morale of morales) { + #game.units.define_morales(morale[0], morale[1]); } - i = 0; - sz = #size(units); - while (i < sz) { - #game.units.define(units[i][0], units[i][1]); - i++; + for (unit of units) { + #game.units.define(unit[0], unit[1]); } }, diff --git a/GLSMAC_data/default/units/movement.gls.js b/GLSMAC_data/default/units/movement.gls.js index 9232d023..997f6a72 100644 --- a/GLSMAC_data/default/units/movement.gls.js +++ b/GLSMAC_data/default/units/movement.gls.js @@ -57,15 +57,12 @@ const result = { return 'Water units can\'t move to land tile'; } - const units = e.dst_tile.get_units(); - let i = 0; let any_foreign_units_in_tile = false; - while (i < #size(units)) { // TODO: for loop - if (units[i].get_owner() != e.unit.get_owner()) { + for (unit of e.dst_tile.get_units()) { // TODO: for loop + if (unit.get_owner() != e.unit.get_owner()) { any_foreign_units_in_tile = true; // TODO: break } - i++; } if (any_foreign_units_in_tile) { return 'Destination tile contains foreign units (combat not implemented yet)'; diff --git a/GLSMAC_data/tests/types.gls.js b/GLSMAC_data/tests/types.gls.js index ec3afa7d..f96c8397 100644 --- a/GLSMAC_data/tests/types.gls.js +++ b/GLSMAC_data/tests/types.gls.js @@ -43,29 +43,9 @@ testarr1 [] = testarr2; let testarr3 = testarr1; testarr3[1] = 'SECOND'; testarr3[testmethod2(a, b, c) + 61] = 'FIRST'; -testarr3[2 -: -5 -] -= testarr1[0 -: -1 -] -+testarr2[0 -: -1 -] -; -let testarr4 = testarr3[ -: -3 -] -; -testarr4[c + 1 - 100 -: -c - 100 + 2 -] -= ['new first', 'new second']; +testarr3[2:5] = testarr1[0:1] + testarr2[0:1]; +let testarr4 = testarr3[:3]; +testarr4[ c + 1 - 100 : c - 100 + 2 ] = ['new first', 'new second']; let testobj1 = {}; let testobj2 = { @@ -164,39 +144,11 @@ test.assert(testarr4[1] == 'new first'); test.assert(testarr4[2] == 'new second'); test.assert(testarr4[3] == 'second'); -test.assert(testarr1[0 -: -1 -] == -['first', 'second'] -) -; +test.assert(testarr1[0:1] == ['first', 'second']); -test.assert(testarr1[5 -:] == -[testarr1[5], testarr1[6]] -) -; -test.assert(testarr1[ -: -3 -] == -[testarr1[0], testarr1[1]] + testarr1[2 -: -3 -] ) -; -test.assert(testarr1[4 -: -5 -] -+testarr1[2 -: -3 -] == -[testarr1[4], testarr1[5], testarr1[2], testarr1[3]] -) -; +test.assert(testarr1[5:] == [testarr1[5], testarr1[6]]); +test.assert(testarr1[:3] == [testarr1[0], testarr1[1]] + testarr1[2:3] ); +test.assert(testarr1[4:5] + testarr1[2:3] == [testarr1[4], testarr1[5], testarr1[2], testarr1[3]]); test.assert(testobj3.child1.child2.value == 'CHILD VALUE'); test.assert(testobj1.propertyInt == 272 + c); test.assert(testobj1 == {propertyInt: 372}); @@ -248,6 +200,62 @@ while (i++ < 5) { } test.assert(arr == [1, 2, 3, 4, 5]); +const data = ['a', 'b', 'c']; + +arr = []; +for (i in data) { + arr []= #to_string(i) + '_' + data[i]; +} +test.assert(arr == ['0_a', '1_b', '2_c']); +for (i in ['asd', 'qwe', 'zxc']) { + arr []= i; +} +test.assert(arr == ['0_a', '1_b', '2_c', 0, 1, 2]); +for (i in { + key1: 'value1', + key2: 'value2', +}) { + arr []= i; +} +test.assert(arr == ['0_a', '1_b', '2_c', 0, 1, 2, 'key1', 'key2']); + +arr = []; +for (v of data) { + arr []= 'of_' + v; +} +test.assert(arr == ['of_a', 'of_b', 'of_c']); +for (v of ['asd', 'qwe', 'zxc', (5 + 10), {k: 'v'}, ((x) => { return x * 2 })(5)]) { + arr []= v; +} +test.assert(arr == [ 'of_a', 'of_b', 'of_c', 'asd', 'qwe', 'zxc', 15, {k: 'v'}, 10]); +for (i of { + key1: 'value1', + key2: 'value2', +}) { + arr []= i; +} +test.assert(arr == [ 'of_a', 'of_b', 'of_c', 'asd', 'qwe', 'zxc', 15, {k: 'v'}, 10, 'value1', 'value2']); + +arr = []; +for (let ii = 2 ; ii > 0 ; ii--) { + arr []= 'i_' + data[ii]; +} +test.assert(arr == ['i_c', 'i_b']); + +for (i = 5 ; i <= 10 ; i++) { + arr []= i; +} +test.assert(arr == ['i_c', 'i_b', 5, 6, 7, 8, 9, 10 ]); + +const for_func = () => { + for ( i = 0 ; i < 10 ; i++ ) { + if ( i == 6 ) { + return i; + } + } +}; +test.assert(for_func() == 6); + arr = []; try { arr [] = 'BEFORE EXCEPTION'; // should be printed @@ -273,16 +281,16 @@ try { arr [] = 'CAUGHT ' + e.type + ' : ' + e.reason; arr += e.backtrace; } -} -; +}; + test.assert(arr == [ 'BEFORE EXCEPTION', 'failfunc', 'failfunc2', 'CAUGHT TestError : something happened', - '\tat ' + test.get_script_path() + ':257: throw TestError(\'something happened\');', - '\tat ' + test.get_script_path() + ':262: realfailfunc();', - '\tat ' + test.get_script_path() + ':264: failfunc();' + '\tat ' + test.get_script_path() + ':265: throw TestError(\'something happened\');', + '\tat ' + test.get_script_path() + ':270: realfailfunc();', + '\tat ' + test.get_script_path() + ':272: failfunc();' ]); test.assert(#to_string(2 + 3) + ' (five)' == '5 (five)'); @@ -301,16 +309,9 @@ test.assert(#typeof(123.) == 'Float'); test.assert(#typeof(0.123) == 'Float'); test.assert(#typeof('string') == 'String'); test.assert(#typeof([]) == 'Array'); -test.assert(#typeof(2 -: -3 -) == -'Range' -) -; +test.assert(#typeof(2:3) == 'Range'); test.assert(#typeof({}) == 'Object'); -test.assert(#typeof(() => { -}) == 'Callable'); +test.assert(#typeof(() => {}) == 'Callable'); test.assert(15.0 != 15); test.assert(#round(15.0) == 15); diff --git a/src/gse/context/Context.cpp b/src/gse/context/Context.cpp index 50f139b2..aa62bc26 100644 --- a/src/gse/context/Context.cpp +++ b/src/gse/context/Context.cpp @@ -93,6 +93,15 @@ void Context::UpdateVariable( const std::string& name, const Value& value, const throw Exception( EC.REFERENCE_ERROR, "Variable '" + name + "' is not defined", this, *si ); } +void Context::DestroyVariable( const std::string& name, const si_t* si ) { + const auto it = m_variables.find( name ); + if ( it != m_variables.end() ) { + m_variables.erase( it ); + return; + } + throw Exception( EC.REFERENCE_ERROR, "Variable '" + name + "' is not defined", this, *si ); +} + void Context::CreateBuiltin( const std::string& name, const Value& value ) { CreateConst( "#" + name, value, nullptr ); } diff --git a/src/gse/context/Context.h b/src/gse/context/Context.h index e9c88219..a8c60d21 100644 --- a/src/gse/context/Context.h +++ b/src/gse/context/Context.h @@ -45,6 +45,7 @@ class Context { void CreateVariable( const std::string& name, const Value& value, const si_t* si ); void CreateConst( const std::string& name, const Value& value, const si_t* si ); void UpdateVariable( const std::string& name, const Value& value, const si_t* si ); + void DestroyVariable( const std::string& name, const si_t* si ); void CreateBuiltin( const std::string& name, const Value& value ); void PersistValue( const Value& value ); void UnpersistValue( const Value& value ); diff --git a/src/gse/parser/JS.cpp b/src/gse/parser/JS.cpp index 30aa7baa..c2a3ed49 100644 --- a/src/gse/parser/JS.cpp +++ b/src/gse/parser/JS.cpp @@ -23,7 +23,11 @@ #include "gse/program/ElseIf.h" #include "gse/program/Else.h" #include "gse/program/While.h" +#include "gse/program/For.h" #include "gse/program/Try.h" +#include "gse/program/SimpleCondition.h" +#include "gse/program/ForConditionInOf.h" +#include "gse/program/ForConditionExpressions.h" namespace gse { @@ -265,7 +269,7 @@ const program::Conditional* JS::GetConditional( const source_elements_t::const_i ASSERT( ( *begin )->m_type == SourceElement::ET_CONDITIONAL, "conditional expected here" ); Conditional* conditional = (Conditional*)( *begin ); source_elements_t::const_iterator it = begin + 1, it_end; - const program::Expression* condition = nullptr; + const program::Condition* condition = nullptr; if ( conditional->has_condition ) { if ( it == end || @@ -280,20 +284,98 @@ const program::Conditional* JS::GetConditional( const source_elements_t::const_i ); } it_end = GetBracketsEnd( it, end ); - condition = GetExpression( it + 1, it_end ); + if (conditional->m_conditional_type == Conditional::CT_FOR ) { + if (++it == end) { + throw Exception( + EC.PARSE_ERROR, "Expected iteration condition" + ( it == end + ? " here" + : ", got: " + ( *it )->ToString() + ), nullptr, ( *begin )->m_si + ); + } + // check if it's expressions-based loop (and process if it is) + auto it_first = it; + auto it_second = it_end; + while ( it_first != it_end ) { + if ( + (*it_first)->m_type == SourceElement::ET_DELIMITER && + ((Delimiter*)*it_first)->m_delimiter_type == Delimiter::DT_CODE + ) { + // found first ; + it_second = it_first + 1; + while ( it_second != it_end ) { + if ( + (*it_second)->m_type == SourceElement::ET_DELIMITER && + ((Delimiter*)*it_second)->m_delimiter_type == Delimiter::DT_CODE + ) { + // found second ; + break; + } + it_second++; + } + break; + } + it_first++; + } + if ( + it != it_first && + it + 1 != it_first && + it_first != it_end && + it_first + 1 != it_second && + it_second != it_end && + it_second + 1 != it_end + ) { + condition = new program::ForConditionExpressions( + ( *it )->m_si, + GetExpression( it, it_first ), + GetExpression( it_first + 1, it_second ), + GetExpression( it_second + 1, it_end ) ); + } + else { + // in-of loop + const auto* variable = ( Identifier * ) * ( it++ ); + if ( it == end || ( *it )->m_type != SourceElement::ET_IDENTIFIER ) { + throw Exception( EC.PARSE_ERROR, "Expected iteration condition" + ( it == end + ? " here" + : ", got: " + ( *it )->ToString() + ), nullptr, ( *begin )->m_si ); + } + const auto* in_or_of = ( Identifier * ) * ( it++ ); + ForConditionInOf::for_inof_condition_type_t type; + if ( in_or_of->m_name == "in" ) { + type = ForConditionInOf::FIC_IN; + } + else if ( in_or_of->m_name == "of" ) { + type = ForConditionInOf::FIC_OF; + } + else { + throw Exception( EC.PARSE_ERROR, "Expected iteration condition, got: " + in_or_of->ToString(), nullptr, ( *begin )->m_si ); + } + condition = new program::ForConditionInOf( + ( *it )->m_si, + new Variable( variable->m_si, variable->m_name ), + type, + GetExpression( it, it_end ) + ); + } + } + else { + condition = new program::SimpleCondition( ( *it )->m_si, GetExpression( it + 1, it_end ) ); + } ASSERT( it_end != end, "expected {, got EOF" ); it = it_end + 1; } - if ( - it == end || - ( *it )->m_type != SourceElement::ET_BLOCK || - ( ( Block * )( *it ) )->m_block_side != Block::BS_BEGIN || - ( ( Block * )( *it ) )->m_block_type != BLOCK_CURLY_BRACKETS ) { + if ( it == end ) { + throw Exception( + EC.PARSE_ERROR, "Expected { here", nullptr, ( *begin )->m_si + ); + } + else if ( + ( *it )->m_type != SourceElement::ET_BLOCK || + ( ( Block * )( *it ) )->m_block_side != Block::BS_BEGIN || + ( ( Block * )( *it ) )->m_block_type != BLOCK_CURLY_BRACKETS ) { throw Exception( - EC.PARSE_ERROR, "Expected {" + ( it == end - ? " here" - : ", got: " + ( *it )->ToString() - ), nullptr, ( *begin )->m_si + EC.PARSE_ERROR, "Expected {, got: " + ( *it )->ToString(), nullptr, ( *begin )->m_si ); } it_end = GetBracketsEnd( it, end ); @@ -314,16 +396,19 @@ const program::Conditional* JS::GetConditional( const source_elements_t::const_i it++; } if ( conditional->m_conditional_type == Conditional::CT_IF ) { - return new program::If( si, condition, body, els ); + return new program::If( si, (SimpleCondition*)condition, body, els ); } else { - return new program::ElseIf( si, condition, body, els ); + return new program::ElseIf( si, (SimpleCondition*)condition, body, els ); } } case Conditional::CT_ELSE: return new program::Else( GetSI( begin, end ), body ); case Conditional::CT_WHILE: - return new program::While( GetSI( begin, end ), condition, body ); + return new program::While( GetSI( begin, end ), (SimpleCondition*)condition, body ); + case Conditional::CT_FOR: { + return new program::For( GetSI( begin, end ), (ForCondition*)condition, body ); + } case Conditional::CT_TRY: { if ( it == end || diff --git a/src/gse/parser/JS.h b/src/gse/parser/JS.h index 507ae11c..de420038 100644 --- a/src/gse/parser/JS.h +++ b/src/gse/parser/JS.h @@ -119,6 +119,10 @@ CLASS( JS, Parser ) "while", Parser::Conditional::CT_WHILE }, + { + "for", + Parser::Conditional::CT_FOR + }, { "try", Parser::Conditional::CT_TRY diff --git a/src/gse/parser/Parser.h b/src/gse/parser/Parser.h index 4d7cb07a..e5f7588d 100644 --- a/src/gse/parser/Parser.h +++ b/src/gse/parser/Parser.h @@ -123,6 +123,7 @@ CLASS( Parser, common::Class ) CT_ELSEIF, CT_ELSE, CT_WHILE, + CT_FOR, CT_TRY, CT_CATCH, }; @@ -132,7 +133,8 @@ CLASS( Parser, common::Class ) , has_condition( conditional_type == CT_IF || conditional_type == CT_ELSEIF || - conditional_type == CT_WHILE + conditional_type == CT_WHILE || + conditional_type == CT_FOR ) {}; const conditional_type_t m_conditional_type; @@ -148,6 +150,8 @@ CLASS( Parser, common::Class ) return "else"; case CT_WHILE: return "while"; + case CT_FOR: + return "for"; case CT_TRY: return "try"; case CT_CATCH: diff --git a/src/gse/program/CMakeLists.txt b/src/gse/program/CMakeLists.txt index 6972a63a..a8e714df 100644 --- a/src/gse/program/CMakeLists.txt +++ b/src/gse/program/CMakeLists.txt @@ -4,6 +4,11 @@ SET( SRC ${SRC} ${PWD}/Call.cpp ${PWD}/Catch.cpp ${PWD}/Conditional.cpp + ${PWD}/Condition.cpp + ${PWD}/SimpleCondition.cpp + ${PWD}/ForCondition.cpp + ${PWD}/ForConditionInOf.cpp + ${PWD}/ForConditionExpressions.cpp ${PWD}/Control.cpp ${PWD}/Element.cpp ${PWD}/Else.cpp @@ -22,6 +27,7 @@ SET( SRC ${SRC} ${PWD}/Value.cpp ${PWD}/Variable.cpp ${PWD}/While.cpp + ${PWD}/For.cpp ) diff --git a/src/gse/program/Condition.cpp b/src/gse/program/Condition.cpp new file mode 100644 index 00000000..b2fbee44 --- /dev/null +++ b/src/gse/program/Condition.cpp @@ -0,0 +1,13 @@ +#include "Condition.h" + +#include "Expression.h" + +namespace gse { +namespace program { + +Condition::Condition( const si_t& si, const condition_type_t type ) + : Element( si, ET_CONDITION ) + , type( type ) {} + +} +} diff --git a/src/gse/program/Condition.h b/src/gse/program/Condition.h new file mode 100644 index 00000000..60fe98ce --- /dev/null +++ b/src/gse/program/Condition.h @@ -0,0 +1,25 @@ +#pragma once + +#include "Operand.h" + +namespace gse { +namespace program { + +class Expression; + +class Condition : public Element { +public: + enum condition_type_t { + CT_NOTHING, + CT_SIMPLE, + }; + + Condition( const si_t& si, const condition_type_t type ); + virtual ~Condition() = default; + + const condition_type_t type; + +}; + +} +} diff --git a/src/gse/program/Conditional.h b/src/gse/program/Conditional.h index eb97b2bd..3de1d5ba 100644 --- a/src/gse/program/Conditional.h +++ b/src/gse/program/Conditional.h @@ -12,6 +12,7 @@ class Conditional : public Control { CT_ELSEIF, CT_ELSE, CT_WHILE, + CT_FOR, CT_TRY, CT_CATCH, }; diff --git a/src/gse/program/Element.h b/src/gse/program/Element.h index 5195e398..021c411f 100644 --- a/src/gse/program/Element.h +++ b/src/gse/program/Element.h @@ -11,6 +11,7 @@ class Element { ET_OPERAND, ET_OPERATOR, ET_CONDITIONAL, + ET_CONDITION, }; Element( const si_t& si, const element_type_t element_type ); virtual ~Element() = default; diff --git a/src/gse/program/ElseIf.cpp b/src/gse/program/ElseIf.cpp index 0d3d1446..ffe3bbc4 100644 --- a/src/gse/program/ElseIf.cpp +++ b/src/gse/program/ElseIf.cpp @@ -1,12 +1,12 @@ #include "ElseIf.h" -#include "Expression.h" +#include "SimpleCondition.h" #include "Scope.h" namespace gse { namespace program { -ElseIf::ElseIf( const si_t& si, const Expression* condition, const Scope* body, const Conditional* els ) +ElseIf::ElseIf( const si_t& si, const SimpleCondition* condition, const Scope* body, const Conditional* els ) : Conditional( si, CT_ELSEIF ) , condition( condition ) , body( body ) diff --git a/src/gse/program/ElseIf.h b/src/gse/program/ElseIf.h index 4d2427b9..37ef9a14 100644 --- a/src/gse/program/ElseIf.h +++ b/src/gse/program/ElseIf.h @@ -5,16 +5,16 @@ namespace gse { namespace program { -class Expression; +class SimpleCondition; class Scope; class ElseIf : public Conditional { public: - ElseIf( const si_t& si, const Expression* condition, const Scope* body, const Conditional* els = nullptr ); + ElseIf( const si_t& si, const SimpleCondition* condition, const Scope* body, const Conditional* els = nullptr ); ~ElseIf(); - const Expression* condition; + const SimpleCondition* condition; const Scope* body; const Conditional* els; diff --git a/src/gse/program/For.cpp b/src/gse/program/For.cpp new file mode 100644 index 00000000..c294917d --- /dev/null +++ b/src/gse/program/For.cpp @@ -0,0 +1,31 @@ +#include "For.h" + +#include "ForCondition.h" +#include "Scope.h" + +namespace gse { +namespace program { + +For::For( const si_t& si, const ForCondition* condition, const Scope* body ) + : Conditional( si, CT_FOR ) + , condition( condition ) + , body( body ) {} + +For::~For() { + delete condition; + delete body; +} + +const std::string For::Dump( const size_t depth ) const { + return Formatted( "For" + m_si.ToString() + "(", depth ) + + Formatted( "Condition(", depth + 1 ) + + condition->Dump( depth + 2 ) + + Formatted( ")", depth + 1 ) + + Formatted( "Body(", depth + 1 ) + + body->Dump( depth + 2 ) + + Formatted( ")", depth + 1 ) + + Formatted( ")", depth ); +} + +} +} diff --git a/src/gse/program/For.h b/src/gse/program/For.h new file mode 100644 index 00000000..a1840f58 --- /dev/null +++ b/src/gse/program/For.h @@ -0,0 +1,24 @@ +#pragma once + +#include "Conditional.h" + +namespace gse { +namespace program { + +class ForCondition; +class Scope; + +class For : public Conditional { +public: + + For( const si_t& si, const ForCondition* condition, const Scope* body ); + ~For(); + + const ForCondition* condition; + const Scope* body; + + const std::string Dump( const size_t depth = 0 ) const override; +}; + +} +} diff --git a/src/gse/program/ForCondition.cpp b/src/gse/program/ForCondition.cpp new file mode 100644 index 00000000..052ab4d3 --- /dev/null +++ b/src/gse/program/ForCondition.cpp @@ -0,0 +1,11 @@ +#include "ForCondition.h" + +namespace gse { +namespace program { + +ForCondition::ForCondition( const si_t& si, const for_condition_type_t type ) + : Condition( si, CT_SIMPLE ) + , for_type( type ) {} + +} +} diff --git a/src/gse/program/ForCondition.h b/src/gse/program/ForCondition.h new file mode 100644 index 00000000..55151ac9 --- /dev/null +++ b/src/gse/program/ForCondition.h @@ -0,0 +1,26 @@ +#pragma once + +#include "Condition.h" + +namespace gse { +namespace program { + +class ForCondition : public Condition { +public: + enum for_condition_type_t { + FCT_NOTHING, + FCT_IN_OF, + FCT_EXPRESSIONS, + }; + + ForCondition( const si_t& si, const for_condition_type_t type ); + virtual ~ForCondition() = default; + + const for_condition_type_t for_type; + + virtual const std::string ToString() const = 0; + virtual const std::string Dump( const size_t depth = 0 ) const = 0; +}; + +} +} diff --git a/src/gse/program/ForConditionExpressions.cpp b/src/gse/program/ForConditionExpressions.cpp new file mode 100644 index 00000000..0d691b66 --- /dev/null +++ b/src/gse/program/ForConditionExpressions.cpp @@ -0,0 +1,43 @@ +#include "ForConditionExpressions.h" + +#include "Variable.h" +#include "Expression.h" + +namespace gse { +namespace program { + +ForConditionExpressions::ForConditionExpressions( const si_t& si, const Expression* init, const Expression* check, const Expression* iterate ) + : ForCondition( si, FCT_EXPRESSIONS ) + , init( init ) + , check( check ) + , iterate( iterate ) {} + +ForConditionExpressions::~ForConditionExpressions() { + if ( init ) { + delete init; + } + if ( iterate ) { + delete iterate; + } + if ( check ) { + delete check; + } +} + +const std::string ForConditionExpressions::ToString() const { + return init->ToString() + " ; " + iterate->ToString() + " ; " + check->ToString(); +} + +const std::string ForConditionExpressions::Dump( const size_t depth ) const { + return + Formatted( "ForCondition" + m_si.ToString() + "(", depth ) + + init->Dump( depth + 1 ) + + Formatted( ";", depth + 1 ) + + iterate->Dump( depth + 1 ) + + Formatted( ";", depth + 1 ) + + check->Dump( depth + 1 ) + + Formatted( ")", depth ); +} + +} +} diff --git a/src/gse/program/ForConditionExpressions.h b/src/gse/program/ForConditionExpressions.h new file mode 100644 index 00000000..30ddfa8e --- /dev/null +++ b/src/gse/program/ForConditionExpressions.h @@ -0,0 +1,24 @@ +#pragma once + +#include "ForCondition.h" + +namespace gse { +namespace program { + +class Expression; + +class ForConditionExpressions : public ForCondition { +public: + ForConditionExpressions( const si_t& si, const Expression* init, const Expression* check, const Expression* iterate ); + ~ForConditionExpressions(); + + const Expression* init; + const Expression* check; + const Expression* iterate; + + const std::string ToString() const override; + const std::string Dump( const size_t depth = 0 ) const override; +}; + +} +} diff --git a/src/gse/program/ForConditionInOf.cpp b/src/gse/program/ForConditionInOf.cpp new file mode 100644 index 00000000..43564264 --- /dev/null +++ b/src/gse/program/ForConditionInOf.cpp @@ -0,0 +1,47 @@ +#include "ForConditionInOf.h" + +#include "Variable.h" +#include "Expression.h" + +namespace gse { +namespace program { + +ForConditionInOf::ForConditionInOf( const si_t& si, const Variable* variable, const for_inof_condition_type_t type, const Expression* expression ) + : ForCondition( si, FCT_IN_OF ) + , variable( variable ) + , for_inof_type( type ) + , expression( expression ) {} + +ForConditionInOf::~ForConditionInOf() { + if ( variable ) { + delete variable; + } + if ( expression ) { + delete expression; + } +} + +const std::string ForConditionInOf::ToString() const { + return variable->ToString() + ( for_inof_type == FIC_IN + ? " in " + : " of " + ) + expression->ToString(); +} + +const std::string ForConditionInOf::Dump( const size_t depth ) const { + return + Formatted( "ForCondition" + m_si.ToString() + "(", depth ) + + variable->Dump( depth + 1 ) + + Formatted( + for_inof_type == FIC_IN + ? " in " + : " of ", + depth + 1 + ) + + expression->Dump( depth + 1 ) + + + Formatted( ")", depth ); +} + +} +} diff --git a/src/gse/program/ForConditionInOf.h b/src/gse/program/ForConditionInOf.h new file mode 100644 index 00000000..e2ea387f --- /dev/null +++ b/src/gse/program/ForConditionInOf.h @@ -0,0 +1,31 @@ +#pragma once + +#include "ForCondition.h" + +namespace gse { +namespace program { + +class Variable; +class Expression; + +class ForConditionInOf : public ForCondition { +public: + enum for_inof_condition_type_t { + FIC_NOTHING, + FIC_IN, + FIC_OF, + }; + + ForConditionInOf( const si_t& si, const Variable* variable, const for_inof_condition_type_t type, const Expression* expression ); + ~ForConditionInOf(); + + const Variable* variable; + const for_inof_condition_type_t for_inof_type; + const Expression* expression; + + const std::string ToString() const override; + const std::string Dump( const size_t depth = 0 ) const override; +}; + +} +} diff --git a/src/gse/program/If.cpp b/src/gse/program/If.cpp index ceae925e..fd606b51 100644 --- a/src/gse/program/If.cpp +++ b/src/gse/program/If.cpp @@ -1,12 +1,12 @@ #include "If.h" -#include "Expression.h" +#include "SimpleCondition.h" #include "Scope.h" namespace gse { namespace program { -If::If( const si_t& si, const Expression* condition, const Scope* body, const Conditional* els ) +If::If( const si_t& si, const SimpleCondition* condition, const Scope* body, const Conditional* els ) : Conditional( si, CT_IF ) , condition( condition ) , body( body ) diff --git a/src/gse/program/If.h b/src/gse/program/If.h index 4379ccd9..45c50d64 100644 --- a/src/gse/program/If.h +++ b/src/gse/program/If.h @@ -5,16 +5,16 @@ namespace gse { namespace program { -class Expression; +class SimpleCondition; class Scope; class If : public Conditional { public: - If( const si_t& si, const Expression* condition, const Scope* body, const Conditional* els = nullptr ); + If( const si_t& si, const SimpleCondition* condition, const Scope* body, const Conditional* els = nullptr ); ~If(); - const Expression* condition; + const SimpleCondition* condition; const Scope* body; const Conditional* els; diff --git a/src/gse/program/SimpleCondition.cpp b/src/gse/program/SimpleCondition.cpp new file mode 100644 index 00000000..b0fa1d88 --- /dev/null +++ b/src/gse/program/SimpleCondition.cpp @@ -0,0 +1,37 @@ +#include "SimpleCondition.h" + +#include "Expression.h" + +namespace gse { +namespace program { + +SimpleCondition::SimpleCondition( const si_t& si, const Expression* expression ) + : Condition( si, CT_SIMPLE ) + , expression( expression ) {} + +SimpleCondition::~SimpleCondition() { + if ( expression ) { + delete expression; + } +} + +const std::string SimpleCondition::ToString() const { + std::string result = ""; + if ( expression ) { + result += expression->ToString(); + } + return result; +} + +const std::string SimpleCondition::Dump( const size_t depth ) const { + return + Formatted( "SimpleCondition" + m_si.ToString() + "(", depth ) + + ( expression == nullptr + ? "" + : expression->Dump( depth + 1 ) + ) + + Formatted( ")", depth ); +} + +} +} diff --git a/src/gse/program/SimpleCondition.h b/src/gse/program/SimpleCondition.h new file mode 100644 index 00000000..56d71a1a --- /dev/null +++ b/src/gse/program/SimpleCondition.h @@ -0,0 +1,22 @@ +#pragma once + +#include "Condition.h" + +namespace gse { +namespace program { + +class Expression; + +class SimpleCondition : public Condition { +public: + SimpleCondition( const si_t& si, const Expression* expression ); + ~SimpleCondition(); + + const Expression* expression; + + const std::string ToString() const override; + const std::string Dump( const size_t depth = 0 ) const override; +}; + +} +} diff --git a/src/gse/program/While.cpp b/src/gse/program/While.cpp index 00fdefbd..94f601df 100644 --- a/src/gse/program/While.cpp +++ b/src/gse/program/While.cpp @@ -1,12 +1,12 @@ #include "While.h" -#include "Expression.h" +#include "SimpleCondition.h" #include "Scope.h" namespace gse { namespace program { -While::While( const si_t& si, const Expression* condition, const Scope* body ) +While::While( const si_t& si, const SimpleCondition* condition, const Scope* body ) : Conditional( si, CT_WHILE ) , condition( condition ) , body( body ) {} diff --git a/src/gse/program/While.h b/src/gse/program/While.h index 8cd11458..d946b184 100644 --- a/src/gse/program/While.h +++ b/src/gse/program/While.h @@ -5,16 +5,16 @@ namespace gse { namespace program { -class Expression; +class SimpleCondition; class Scope; class While : public Conditional { public: - While( const si_t& si, const Expression* condition, const Scope* body ); + While( const si_t& si, const SimpleCondition* condition, const Scope* body ); ~While(); - const Expression* condition; + const SimpleCondition* condition; const Scope* body; const std::string Dump( const size_t depth = 0 ) const override; diff --git a/src/gse/runner/Interpreter.cpp b/src/gse/runner/Interpreter.cpp index ecab756d..715455e6 100644 --- a/src/gse/runner/Interpreter.cpp +++ b/src/gse/runner/Interpreter.cpp @@ -6,6 +6,11 @@ #include "gse/program/Object.h" #include "gse/program/Value.h" #include "gse/program/Expression.h" +#include "gse/program/Condition.h" +#include "gse/program/SimpleCondition.h" +#include "gse/program/ForCondition.h" +#include "gse/program/ForConditionInOf.h" +#include "gse/program/ForConditionExpressions.h" #include "gse/program/Operator.h" #include "gse/program/Operand.h" #include "gse/program/Variable.h" @@ -17,6 +22,7 @@ #include "gse/program/ElseIf.h" #include "gse/program/Else.h" #include "gse/program/While.h" +#include "gse/program/For.h" #include "gse/program/Try.h" #include "gse/program/Catch.h" #include "gse/type/Type.h" @@ -94,7 +100,7 @@ const gse::Value Interpreter::EvaluateConditional( context::Context* ctx, const switch ( conditional->conditional_type ) { case Conditional::CT_IF: { const auto* c = (If*)conditional; - if ( EvaluateBool( ctx, c->condition ) ) { + if ( EvaluateBool( ctx, c->condition->expression ) ) { return EvaluateScope( ctx, c->body ); } else if ( c->els ) { @@ -110,7 +116,7 @@ const gse::Value Interpreter::EvaluateConditional( context::Context* ctx, const throw gse::Exception( EC.PARSE_ERROR, "Unexpected elseif without if", ctx, conditional->m_si ); } const auto* c = (ElseIf*)conditional; - if ( EvaluateBool( ctx, c->condition ) ) { + if ( EvaluateBool( ctx, c->condition->expression ) ) { return EvaluateScope( ctx, c->body ); } else if ( c->els ) { @@ -131,7 +137,7 @@ const gse::Value Interpreter::EvaluateConditional( context::Context* ctx, const case Conditional::CT_WHILE: { const auto* c = (While*)conditional; gse::Value result = VALUE( Undefined ); - while ( EvaluateBool( ctx, c->condition ) ) { + while ( EvaluateBool( ctx, c->condition->expression ) ) { result = EvaluateScope( ctx, c->body ); if ( result.Get()->type != Type::T_UNDEFINED ) { break; @@ -139,6 +145,88 @@ const gse::Value Interpreter::EvaluateConditional( context::Context* ctx, const } return result; } + case Conditional::CT_FOR: { + const auto* c = (For*)conditional; + gse::Value result = VALUE( Undefined ); + switch ( c->condition->for_type ) { + case ForCondition::FCT_EXPRESSIONS: { + const auto* condition = (ForConditionExpressions*)c->condition; + EvaluateExpression( ctx, condition->init ); + while ( EvaluateBool( ctx, condition->check ) ) { + result = EvaluateScope( ctx, c->body ); + if ( result.Get()->type != Type::T_UNDEFINED ) { + break; + } + EvaluateExpression( ctx, condition->iterate ); + } + break; + } + case ForCondition::FCT_IN_OF: { + const auto* condition = (ForConditionInOf*)c->condition; + const auto target = EvaluateExpression( ctx, condition->expression ); + const auto forctx = ctx->ForkContext( ctx, condition->m_si, false ); + forctx->IncRefs(); + switch ( target.Get()->type ) { + case Type::T_ARRAY: { + const auto* arr = (type::Array*)target.Get(); + switch ( condition->for_inof_type ) { + case ForConditionInOf::FIC_IN: { + for ( size_t i = 0 ; i < arr->value.size() ; i++ ) { + forctx->CreateConst( condition->variable->name, VALUE( Int, i ), &condition->m_si ); + result = EvaluateScope( forctx, c->body ); + forctx->DestroyVariable( condition->variable->name, &condition->m_si ); + if ( result.Get()->type != Type::T_UNDEFINED ) { + break; + } + } + break; + } + case ForConditionInOf::FIC_OF: { + for ( const auto& v : arr->value ) { + forctx->CreateConst( condition->variable->name, v, &condition->m_si ); + result = EvaluateScope( forctx, c->body ); + forctx->DestroyVariable( condition->variable->name, &condition->m_si ); + if ( result.Get()->type != Type::T_UNDEFINED ) { + break; + } + } + break; + } + default: + THROW( "unexpected for in_of condition type: " + std::to_string( condition->for_inof_type ) ); + } + break; + } + case Type::T_OBJECT: { + const auto* obj = (type::Object*)target.Get(); + if ( condition->for_inof_type != ForConditionInOf::FIC_IN && condition->for_inof_type != ForConditionInOf::FIC_OF ) { + THROW( "unexpected for in_of condition type: " + std::to_string( condition->for_inof_type ) ); + } + for ( const auto& v : obj->value ) { + forctx->CreateConst( + condition->variable->name, condition->for_inof_type == ForConditionInOf::FIC_IN + ? VALUE( String, v.first ) + : v.second, &condition->m_si + ); + result = EvaluateScope( forctx, c->body ); + forctx->DestroyVariable( condition->variable->name, &condition->m_si ); + if ( result.Get()->type != Type::T_UNDEFINED ) { + break; + } + } + break; + } + default: + THROW( "unexpected type for iteration: " + target.ToString() ); + } + forctx->DecRefs(); + break; + } + default: + THROW( "unexpected for condition type: " + std::to_string( c->condition->for_type ) ); + } + return result; + } case Conditional::CT_TRY: { const auto* c = (Try*)conditional; try { diff --git a/src/gse/tests/Parser.cpp b/src/gse/tests/Parser.cpp index 6e9b5146..ed777011 100644 --- a/src/gse/tests/Parser.cpp +++ b/src/gse/tests/Parser.cpp @@ -6,6 +6,8 @@ #include "gse/program/Statement.h" #include "gse/program/Conditional.h" #include "gse/program/Expression.h" +#include "gse/program/Condition.h" +#include "gse/program/SimpleCondition.h" #include "gse/program/Operator.h" #include "gse/program/Value.h" #include "gse/program/Variable.h" @@ -199,6 +201,25 @@ void AddParserTests( task::gsetests::GSETests* task ) { VALIDATE( operand, a->b, b->b ); GT_OK(); }; + const auto simple_condition = VALIDATOR( SimpleCondition, &errmsg, &expression, &si ) { + VALIDATE( si, a->m_si, b->m_si ); + VALIDATE( expression, a->expression, b->expression ); + GT_OK(); + }; + const auto condition = VALIDATOR( Condition, &errmsg, &simple_condition, &si ) { + GT_ASSERT( a->type == b->type, "conditions have different types ( " + a->ToString() + " != " + b->ToString() + " )" ); + VALIDATE( si, a->m_si, b->m_si ); + switch ( a->type ) { + case Condition::CT_SIMPLE: { + VALIDATE( simple_condition, (SimpleCondition*)a, (SimpleCondition*)b ); + break; + } + default: { + GT_FAIL( "unknown condition type: " + std::to_string( a->type ) ); + } + } + GT_OK(); + }; const auto statement = VALIDATOR( Statement, &errmsg, &expression, &si ) { VALIDATE( si, a->m_si, b->m_si ); VALIDATE( expression, a->body, b->body ); @@ -212,7 +233,7 @@ void AddParserTests( task::gsetests::GSETests* task ) { > conditional = VALIDATOR( Conditional, &errmsg, - &expression, + &condition, &scope, &conditional, &object, @@ -222,12 +243,12 @@ void AddParserTests( task::gsetests::GSETests* task ) { GT_ASSERT( a->conditional_type == b->conditional_type, "conditionals have different types ( " + a->Dump() + " != " + b->Dump() + " )" ); switch ( a->conditional_type ) { case Conditional::CT_IF: { - VALIDATE( expression, ( (If*)a )->condition, ( (If*)b )->condition ); + VALIDATE( condition, ( (If*)a )->condition, ( (If*)b )->condition ); VALIDATE( scope, ( (If*)a )->body, ( (If*)b )->body ); break; } case Conditional::CT_ELSEIF: { - VALIDATE( expression, ( (ElseIf*)a )->condition, ( (ElseIf*)b )->condition ); + VALIDATE( condition, ( (ElseIf*)a )->condition, ( (ElseIf*)b )->condition ); VALIDATE( scope, ( (ElseIf*)a )->body, ( (ElseIf*)b )->body ); break; } @@ -236,10 +257,13 @@ void AddParserTests( task::gsetests::GSETests* task ) { break; } case Conditional::CT_WHILE: { - VALIDATE( expression, ( (While*)a )->condition, ( (While*)b )->condition ); + VALIDATE( condition, ( (While*)a )->condition, ( (While*)b )->condition ); VALIDATE( scope, ( (While*)a )->body, ( (While*)b )->body ); break; } + case Conditional::CT_FOR: { + THROW( "TODO: PARSER FOR" ); + } case Conditional::CT_TRY: { VALIDATE( scope, ( (Try*)a )->body, ( (Try*)b )->body ); VALIDATE( conditional, ( (Try*)a )->handlers, ( (Try*)b )->handlers ); diff --git a/src/gse/tests/Tests.cpp b/src/gse/tests/Tests.cpp index 0a434962..f481b837 100644 --- a/src/gse/tests/Tests.cpp +++ b/src/gse/tests/Tests.cpp @@ -25,6 +25,7 @@ #include "gse/program/Expression.h" #include "gse/program/Operator.h" #include "gse/program/Object.h" +#include "gse/program/SimpleCondition.h" #include "gse/type/Bool.h" #include "gse/type/Int.h" #include "gse/type/String.h" @@ -1463,11 +1464,14 @@ const Program* GetTestProgram() { ), new If( SI( 84, 1, 86, 2 ), - new Expression( - SI( 84, 6, 84, 11 ), - new Variable( SI( 84, 6, 84, 7 ), "a" ), - new Operator( SI( 84, 8, 84, 9 ), OT_GT ), - new Variable( SI( 84, 10, 84, 11 ), "b" ) + new SimpleCondition( + SI( 84, 4, 84, 5 ), + new Expression( + SI( 84, 6, 84, 11 ), + new Variable( SI( 84, 6, 84, 7 ), "a" ), + new Operator( SI( 84, 8, 84, 9 ), OT_GT ), + new Variable( SI( 84, 10, 84, 11 ), "b" ) + ) ), new Scope( SI( 85, 8, 85, 24 ), @@ -1503,11 +1507,14 @@ const Program* GetTestProgram() { ), new If( SI( 90, 1, 92, 2 ), - new Expression( - SI( 90, 6, 90, 11 ), - new Variable( SI( 90, 6, 90, 7 ), "b" ), - new Operator( SI( 90, 8, 90, 9 ), OT_GT ), - new Variable( SI( 90, 10, 90, 11 ), "a" ) + new SimpleCondition( + SI( 90, 4, 90, 5 ), + new Expression( + SI( 90, 6, 90, 11 ), + new Variable( SI( 90, 6, 90, 7 ), "b" ), + new Operator( SI( 90, 8, 90, 9 ), OT_GT ), + new Variable( SI( 90, 10, 90, 11 ), "a" ) + ) ), new Scope( SI( 91, 8, 91, 24 ), @@ -1543,9 +1550,12 @@ const Program* GetTestProgram() { ), new If( SI( 96, 1, 96, 41 ), - new Expression( - SI( 96, 6, 96, 11 ), - new program::Value( SI( 96, 6, 96, 11 ), VALUE( type::Bool, false ) ) + new SimpleCondition( + SI( 96, 4, 96, 5 ), + new Expression( + SI( 96, 6, 96, 11 ), + new program::Value( SI( 96, 6, 96, 11 ), VALUE( type::Bool, false ) ) + ) ), new Scope( SI( 96, 21, 96, 39 ), @@ -1564,9 +1574,12 @@ const Program* GetTestProgram() { ), new If( SI( 97, 1, 99, 2 ), - new Expression( - SI( 97, 6, 97, 11 ), - new program::Value( SI( 97, 6, 97, 11 ), VALUE( type::Bool, false ) ) + new SimpleCondition( + SI( 97, 4, 97, 5 ), + new Expression( + SI( 97, 6, 97, 11 ), + new program::Value( SI( 97, 6, 97, 11 ), VALUE( type::Bool, false ) ) + ) ), new Scope( SI( 98, 8, 98, 23 ), @@ -1584,9 +1597,12 @@ const Program* GetTestProgram() { ), new ElseIf( SI( 99, 3, 101, 2 ), - new Expression( - SI( 99, 12, 99, 17 ), - new program::Value( SI( 99, 12, 99, 17 ), VALUE( type::Bool, false ) ) + new SimpleCondition( + SI( 99, 3, 101, 2 ), + new Expression( + SI( 99, 12, 99, 17 ), + new program::Value( SI( 99, 12, 99, 17 ), VALUE( type::Bool, false ) ) + ) ), new Scope( SI( 100, 3, 100, 25 ), @@ -1604,9 +1620,12 @@ const Program* GetTestProgram() { ), new ElseIf( SI( 101, 3, 103, 2 ), - new Expression( - SI( 101, 12, 101, 16 ), - new program::Value( SI( 101, 12, 101, 16 ), VALUE( type::Bool, true ) ) + new SimpleCondition( + SI( 101, 3, 103, 2 ), + new Expression( + SI( 101, 12, 101, 16 ), + new program::Value( SI( 101, 12, 101, 16 ), VALUE( type::Bool, true ) ) + ) ), new Scope( SI( 102, 3, 102, 23 ), @@ -1653,15 +1672,18 @@ const Program* GetTestProgram() { ), new While( SI( 108, 1, 110, 2 ), - new Expression( - SI( 108, 9, 108, 16 ), + new SimpleCondition( + SI( 108, 7, 108, 8 ), new Expression( - SI( 108, 9, 108, 12 ), - new Variable( SI( 108, 9, 108, 10 ), "i" ), - new Operator( SI( 108, 10, 108, 12 ), OT_INC ) - ), - new Operator( SI( 108, 13, 108, 14 ), OT_LT ), - new program::Value( SI( 108, 15, 108, 16 ), VALUE( type::Int, 5 ) ) + SI( 108, 9, 108, 16 ), + new Expression( + SI( 108, 9, 108, 12 ), + new Variable( SI( 108, 9, 108, 10 ), "i" ), + new Operator( SI( 108, 10, 108, 12 ), OT_INC ) + ), + new Operator( SI( 108, 13, 108, 14 ), OT_LT ), + new program::Value( SI( 108, 15, 108, 16 ), VALUE( type::Int, 5 ) ) + ) ), new Scope( SI( 109, 8, 109, 18 ), From 2107fd1404e116bd2dfb8133fbd8193d8a17b72a Mon Sep 17 00:00:00 2001 From: afwbkbc Date: Wed, 26 Jun 2024 18:36:56 +0300 Subject: [PATCH 08/18] import base names from faction txt files --- GLSMAC_data/default/factions/angels.gls.js | 1 + GLSMAC_data/default/factions/believers.gls.js | 1 + .../default/factions/caretakers.gls.js | 1 + .../default/factions/consciousness.gls.js | 1 + GLSMAC_data/default/factions/drones.gls.js | 1 + GLSMAC_data/default/factions/gaians.gls.js | 7 ++- GLSMAC_data/default/factions/hive.gls.js | 1 + .../default/factions/morganites.gls.js | 1 + .../default/factions/peacekeepers.gls.js | 1 + GLSMAC_data/default/factions/pirates.gls.js | 1 + .../default/factions/planetcult.gls.js | 1 + GLSMAC_data/default/factions/spartans.gls.js | 1 + .../default/factions/university.gls.js | 1 + GLSMAC_data/default/factions/usurpers.gls.js | 1 + src/engine/Engine.cpp | 3 + src/engine/Engine.h | 6 ++ src/game/bindings/Factions.cpp | 29 +++++++++- src/loader/CMakeLists.txt | 1 + src/loader/txt/CMakeLists.txt | 7 +++ src/loader/txt/FactionTXTLoader.cpp | 45 +++++++++++++++ src/loader/txt/FactionTXTLoader.h | 33 +++++++++++ src/loader/txt/TXTLoader.cpp | 55 +++++++++++++++++++ src/loader/txt/TXTLoader.h | 29 ++++++++++ src/loader/txt/TXTLoaders.cpp | 17 ++++++ src/loader/txt/TXTLoaders.h | 17 ++++++ src/main.cpp | 5 +- src/resource/Types.h | 2 +- src/task/game/text/InstancedFont.cpp | 4 -- src/util/String.cpp | 3 + 29 files changed, 268 insertions(+), 8 deletions(-) create mode 100644 src/loader/txt/CMakeLists.txt create mode 100644 src/loader/txt/FactionTXTLoader.cpp create mode 100644 src/loader/txt/FactionTXTLoader.h create mode 100644 src/loader/txt/TXTLoader.cpp create mode 100644 src/loader/txt/TXTLoader.h create mode 100644 src/loader/txt/TXTLoaders.cpp create mode 100644 src/loader/txt/TXTLoaders.h diff --git a/GLSMAC_data/default/factions/angels.gls.js b/GLSMAC_data/default/factions/angels.gls.js index f593f480..1f739f4a 100644 --- a/GLSMAC_data/default/factions/angels.gls.js +++ b/GLSMAC_data/default/factions/angels.gls.js @@ -12,5 +12,6 @@ return { 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 index 13686ba9..64f76da5 100644 --- a/GLSMAC_data/default/factions/believers.gls.js +++ b/GLSMAC_data/default/factions/believers.gls.js @@ -12,5 +12,6 @@ return { 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 index 2a0e034a..ac73b3c8 100644 --- a/GLSMAC_data/default/factions/caretakers.gls.js +++ b/GLSMAC_data/default/factions/caretakers.gls.js @@ -12,6 +12,7 @@ return { 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 index bfd0a8ed..725786f1 100644 --- a/GLSMAC_data/default/factions/consciousness.gls.js +++ b/GLSMAC_data/default/factions/consciousness.gls.js @@ -12,5 +12,6 @@ return { 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 index c142dbbe..be31bedc 100644 --- a/GLSMAC_data/default/factions/drones.gls.js +++ b/GLSMAC_data/default/factions/drones.gls.js @@ -12,5 +12,6 @@ return { 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 index 7a0e5784..78ebb535 100644 --- a/GLSMAC_data/default/factions/gaians.gls.js +++ b/GLSMAC_data/default/factions/gaians.gls.js @@ -1,4 +1,4 @@ -return { +const faction = { name: 'Gaians', colors: { text: #to_color(16, 228, 0), @@ -12,5 +12,10 @@ return { cell_width: 100, cell_height: 75, cell_padding: 1, }, + names: #game.factions.import_base_names('gaians.txt'), }, }; + +#print('FACTION', faction); + +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 index 01f8c1f0..3d4f2444 100644 --- a/GLSMAC_data/default/factions/hive.gls.js +++ b/GLSMAC_data/default/factions/hive.gls.js @@ -12,5 +12,6 @@ return { 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 index 9b895345..6cb4a371 100644 --- a/GLSMAC_data/default/factions/morganites.gls.js +++ b/GLSMAC_data/default/factions/morganites.gls.js @@ -12,5 +12,6 @@ return { 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 index 05c40ade..78a8ab9c 100644 --- a/GLSMAC_data/default/factions/peacekeepers.gls.js +++ b/GLSMAC_data/default/factions/peacekeepers.gls.js @@ -12,5 +12,6 @@ return { 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 index 47220a7a..e08450cd 100644 --- a/GLSMAC_data/default/factions/pirates.gls.js +++ b/GLSMAC_data/default/factions/pirates.gls.js @@ -12,6 +12,7 @@ return { 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 index 389027b6..2eeb5d5e 100644 --- a/GLSMAC_data/default/factions/planetcult.gls.js +++ b/GLSMAC_data/default/factions/planetcult.gls.js @@ -12,5 +12,6 @@ return { 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 index b0fa1f1e..53902f69 100644 --- a/GLSMAC_data/default/factions/spartans.gls.js +++ b/GLSMAC_data/default/factions/spartans.gls.js @@ -12,5 +12,6 @@ return { 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 index 088bf1af..98217cd8 100644 --- a/GLSMAC_data/default/factions/university.gls.js +++ b/GLSMAC_data/default/factions/university.gls.js @@ -12,5 +12,6 @@ return { 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 index 71edb66a..56dd55e2 100644 --- a/GLSMAC_data/default/factions/usurpers.gls.js +++ b/GLSMAC_data/default/factions/usurpers.gls.js @@ -12,6 +12,7 @@ return { cell_width: 100, cell_height: 75, cell_padding: 1, }, + names: #game.factions.import_base_names('usurper.txt'), }, is_progenitor: true, }; diff --git a/src/engine/Engine.cpp b/src/engine/Engine.cpp index 667e2413..cbb11075 100644 --- a/src/engine/Engine.cpp +++ b/src/engine/Engine.cpp @@ -10,6 +10,7 @@ #include "loader/font/FontLoader.h" #include "loader/texture/TextureLoader.h" #include "loader/sound/SoundLoader.h" +#include "loader/txt/TXTLoaders.h" #include "scheduler/Scheduler.h" #include "input/Input.h" #include "graphics/Graphics.h" @@ -33,6 +34,7 @@ Engine::Engine( loader::font::FontLoader* font_loader, loader::texture::TextureLoader* texture_loader, loader::sound::SoundLoader* sound_loader, + loader::txt::TXTLoaders* txt_loaders, scheduler::Scheduler* scheduler, input::Input* input, graphics::Graphics* graphics, @@ -49,6 +51,7 @@ Engine::Engine( , m_font_loader( font_loader ) , m_texture_loader( texture_loader ) , m_sound_loader( sound_loader ) + , m_txt_loaders( txt_loaders ) , m_scheduler( scheduler ) , m_input( input ) , m_graphics( graphics ) diff --git a/src/engine/Engine.h b/src/engine/Engine.h index 1416e355..ae44c68d 100644 --- a/src/engine/Engine.h +++ b/src/engine/Engine.h @@ -39,6 +39,9 @@ class TextureLoader; namespace sound { class SoundLoader; } +namespace txt { +class TXTLoaders; +} } namespace input { @@ -81,6 +84,7 @@ CLASS( Engine, common::Class ); loader::font::FontLoader* font_loader, loader::texture::TextureLoader* texture_loader, loader::sound::SoundLoader* sound_loader, + loader::txt::TXTLoaders* txt_loaders, scheduler::Scheduler* scheduler, input::Input* input, graphics::Graphics* graphics, @@ -100,6 +104,7 @@ CLASS( Engine, common::Class ); loader::font::FontLoader* GetFontLoader() const { return m_font_loader; } loader::texture::TextureLoader* GetTextureLoader() const { return m_texture_loader; } loader::sound::SoundLoader* GetSoundLoader() const { return m_sound_loader; } + loader::txt::TXTLoaders* GetTXTLoaders() const { return m_txt_loaders; } input::Input* GetInput() const { return m_input; } graphics::Graphics* GetGraphics() const { return m_graphics; } audio::Audio* GetAudio() const { return m_audio; } @@ -121,6 +126,7 @@ CLASS( Engine, common::Class ); loader::font::FontLoader* m_font_loader = nullptr; loader::texture::TextureLoader* m_texture_loader = nullptr; loader::sound::SoundLoader* m_sound_loader = nullptr; + loader::txt::TXTLoaders* m_txt_loaders = nullptr; scheduler::Scheduler* m_scheduler = nullptr; input::Input* m_input = nullptr; graphics::Graphics* m_graphics = nullptr; diff --git a/src/game/bindings/Factions.cpp b/src/game/bindings/Factions.cpp index c139923b..229fb78a 100644 --- a/src/game/bindings/Factions.cpp +++ b/src/game/bindings/Factions.cpp @@ -3,6 +3,7 @@ #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" @@ -10,6 +11,9 @@ #include "gse/type/Bool.h" #include "game/bindings/Bindings.h" #include "game/State.h" +#include "engine/Engine.h" +#include "loader/txt/TXTLoaders.h" +#include "loader/txt/FactionTXTLoader.h" #include "types/Color.h" @@ -20,9 +24,32 @@ 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; 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 ); + } ) + }, { "define", - NATIVE_CALL( this, &factions, &factions_order ) { + NATIVE_CALL( &factions, &factions_order ) { N_EXPECT_ARGS( 2 ); N_GETVALUE( id, 0, String ); if ( factions.find( id ) != factions.end() ) { diff --git a/src/loader/CMakeLists.txt b/src/loader/CMakeLists.txt index 85d02293..8c8793fc 100644 --- a/src/loader/CMakeLists.txt +++ b/src/loader/CMakeLists.txt @@ -1,6 +1,7 @@ SUBDIR( font ) SUBDIR( texture ) SUBDIR( sound ) +SUBDIR( txt ) SET( SRC ${SRC} diff --git a/src/loader/txt/CMakeLists.txt b/src/loader/txt/CMakeLists.txt new file mode 100644 index 00000000..fb3fc14f --- /dev/null +++ b/src/loader/txt/CMakeLists.txt @@ -0,0 +1,7 @@ +SET( SRC ${SRC} + + ${PWD}/TXTLoaders.cpp + ${PWD}/TXTLoader.cpp + ${PWD}/FactionTXTLoader.cpp + + PARENT_SCOPE ) diff --git a/src/loader/txt/FactionTXTLoader.cpp b/src/loader/txt/FactionTXTLoader.cpp new file mode 100644 index 00000000..26fc5e1b --- /dev/null +++ b/src/loader/txt/FactionTXTLoader.cpp @@ -0,0 +1,45 @@ +#include "FactionTXTLoader.h" + +#include "engine/Engine.h" +#include "resource/ResourceManager.h" + +namespace loader { +namespace txt { + +const FactionTXTLoader::faction_data_t& FactionTXTLoader::GetFactionData( resource::resource_t res ) { + return GetFactionDataImpl( g_engine->GetResourceManager()->GetPath( res ) ); +} + +const FactionTXTLoader::faction_data_t& FactionTXTLoader::GetFactionData( const std::string& filename ) { + return GetFactionDataImpl( g_engine->GetResourceManager()->GetCustomPath( filename ) ); +} + +const FactionTXTLoader::faction_data_t& FactionTXTLoader::GetFactionDataImpl( const std::string& path ) { + auto it = m_faction_data.find( path ); + if ( it == m_faction_data.end() ) { + const auto data = GetTXTData( path ); + it = m_faction_data.insert( + { + path, + faction_data_t{ + { + GetSection( data, "BASES" ), + GetSection( data, "WATERBASES" ), + } + } + } + ).first; + } + return it->second; +} + +const std::vector< std::string >& FactionTXTLoader::GetSection( const txt_data_t& data, const std::string& name ) const { + const auto bases_it = data.sections.find( name ); + if ( bases_it == data.sections.end() ) { + THROW( "file does not contain section #" + name ); + } + return bases_it->second; +} + +} +} diff --git a/src/loader/txt/FactionTXTLoader.h b/src/loader/txt/FactionTXTLoader.h new file mode 100644 index 00000000..b809f8ca --- /dev/null +++ b/src/loader/txt/FactionTXTLoader.h @@ -0,0 +1,33 @@ +#pragma once + +#include + +#include "TXTLoader.h" + +namespace loader { +namespace txt { + +CLASS( FactionTXTLoader, TXTLoader ) + + struct faction_data_t { + struct { + std::vector< std::string > land; + std::vector< std::string > water; + } bases_names; + }; + const faction_data_t& GetFactionData( resource::resource_t res ); + const faction_data_t& GetFactionData( const std::string& filename ); + +protected: + +private: + + std::unordered_map< std::string, faction_data_t > m_faction_data = {}; + + const faction_data_t& GetFactionDataImpl( const std::string& path ); + const std::vector< std::string >& GetSection( const txt_data_t& data, const std::string& name ) const; + +}; + +} +} diff --git a/src/loader/txt/TXTLoader.cpp b/src/loader/txt/TXTLoader.cpp new file mode 100644 index 00000000..d4bada90 --- /dev/null +++ b/src/loader/txt/TXTLoader.cpp @@ -0,0 +1,55 @@ +#include "TXTLoader.h" + +#include "util/FS.h" +#include "util/String.h" + +namespace loader { +namespace txt { + +const TXTLoader::txt_data_t& TXTLoader::GetTXTData( const std::string& path ) { + auto it = m_txt_data.find( path ); + if ( it == m_txt_data.end() ) { + it = m_txt_data.insert( + { + path, + txt_data_t{ util::FS::ReadFile( path ) } + } + ).first; + } + return it->second; +} + +TXTLoader::txt_data_t::txt_data_t( const std::string& source ) { + + // raw lines + lines = util::String::SplitToLines( source ); + + // sections + sections_t::iterator section_it = sections.end(); + for ( const auto& line : lines ) { + if ( !line.empty() && line[ 0 ] == '#' && line.find( ' ' ) == std::string::npos ) { + if ( line.substr( 0, 4 ) == "#END" ) { + section_it = sections.end(); + } + else { + const auto section_name = line.substr( 1 ); + ASSERT_NOLOG( sections.find( section_name ) == sections.end(), "section '" + section_name + "' already exists" ); + section_it = sections.insert( + { + section_name, + {} + } + ).first; + } + } + else { + if ( section_it != sections.end() ) { + section_it->second.push_back( line ); + } + } + } + +} + +} +} diff --git a/src/loader/txt/TXTLoader.h b/src/loader/txt/TXTLoader.h new file mode 100644 index 00000000..2c701c51 --- /dev/null +++ b/src/loader/txt/TXTLoader.h @@ -0,0 +1,29 @@ +#pragma once + +#include +#include + +#include "loader/Loader.h" + +namespace loader { +namespace txt { + +CLASS( TXTLoader, Loader ) + +protected: + + struct txt_data_t { + typedef std::vector< std::string > lines_t; + typedef std::unordered_map< std::string, std::vector< std::string > > sections_t; + txt_data_t( const std::string& source ); + lines_t lines = {}; + sections_t sections = {}; + }; + + std::unordered_map< std::string, txt_data_t > m_txt_data = {}; + + const txt_data_t& GetTXTData( const std::string& path ); +}; + +} +} diff --git a/src/loader/txt/TXTLoaders.cpp b/src/loader/txt/TXTLoaders.cpp new file mode 100644 index 00000000..ac4287a2 --- /dev/null +++ b/src/loader/txt/TXTLoaders.cpp @@ -0,0 +1,17 @@ +#include "TXTLoaders.h" + +#include "FactionTXTLoader.h" + +namespace loader { +namespace txt { + +TXTLoaders::TXTLoaders() + : factions( new FactionTXTLoader() ) { + // +} +TXTLoaders::~TXTLoaders() { + delete factions; +} + +} +} \ No newline at end of file diff --git a/src/loader/txt/TXTLoaders.h b/src/loader/txt/TXTLoaders.h new file mode 100644 index 00000000..15e0cf37 --- /dev/null +++ b/src/loader/txt/TXTLoaders.h @@ -0,0 +1,17 @@ +#pragma once + +namespace loader { +namespace txt { + +class FactionTXTLoader; + +class TXTLoaders { +public: + TXTLoaders(); + ~TXTLoaders(); + + FactionTXTLoader* const factions; +}; + +} +} diff --git a/src/main.cpp b/src/main.cpp index 637dd5a7..f8c555f9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -37,6 +37,7 @@ #include "loader/font/FreeType.h" #include "loader/texture/SDL2.h" #include "loader/sound/SDL2.h" +#include "loader/txt/TXTLoaders.h" #include "input/sdl2/SDL2.h" #include "graphics/opengl/OpenGL.h" #include "audio/sdl2/SDL2.h" @@ -98,7 +99,6 @@ int main( const int argc, const char* argv[] ) { config.Init(); - #ifdef DEBUG if ( config.HasDebugFlag( config::Config::DF_GDB ) ) { #ifdef __linux__ @@ -200,6 +200,7 @@ int main( const int argc, const char* argv[] ) { &font_loader, &texture_loader, &sound_loader, + nullptr, &scheduler, &input, &graphics, @@ -221,6 +222,7 @@ int main( const int argc, const char* argv[] ) { loader::font::FreeType font_loader; loader::texture::SDL2 texture_loader; loader::sound::SDL2 sound_loader; + loader::txt::TXTLoaders txt_loaders; input::sdl2::SDL2 input; bool vsync = VSYNC; @@ -265,6 +267,7 @@ int main( const int argc, const char* argv[] ) { &font_loader, &texture_loader, &sound_loader, + &txt_loaders, &scheduler, &input, &graphics, diff --git a/src/resource/Types.h b/src/resource/Types.h index b8890f04..3aee662a 100644 --- a/src/resource/Types.h +++ b/src/resource/Types.h @@ -58,7 +58,7 @@ enum resource_t : uint8_t { PCX_FUNGBOY, PCX_CARETAKE, PCX_USURPER, - + TTF_ARIALN, TTF_ARIALNB, diff --git a/src/task/game/text/InstancedFont.cpp b/src/task/game/text/InstancedFont.cpp index 953f312b..34a9930e 100644 --- a/src/task/game/text/InstancedFont.cpp +++ b/src/task/game/text/InstancedFont.cpp @@ -94,10 +94,6 @@ InstancedFont::InstancedFont( sprite::InstancedSpriteManager* ism, const types:: ); unsigned int sym_x = sym_offset; for ( uint8_t i = 32 ; i < 128 ; i++ ) { - if ( i == ' ' ) { - int a = 5; - a++; - } const auto& sym = m_font->m_symbols[ i ]; m_symbol_positions.insert( { diff --git a/src/util/String.cpp b/src/util/String.cpp index 8265de57..486ce0b5 100644 --- a/src/util/String.cpp +++ b/src/util/String.cpp @@ -10,6 +10,9 @@ const std::vector< std::string > String::SplitToLines( const std::string& input std::vector< std::string > lines = {}; auto ss = std::stringstream{ input }; for ( std::string line ; std::getline( ss, line, '\n' ) ; ) { + if ( line.back() == '\r' ) { + line.erase( line.end() - 1 ); + } lines.push_back( line ); } return lines; From 2b079054d77930876c48c59ac8134a24f74e84ae Mon Sep 17 00:00:00 2001 From: afwbkbc Date: Thu, 27 Jun 2024 21:00:28 +0300 Subject: [PATCH 09/18] keep, display and generate base names, some fixes --- GLSMAC_data/default/main.gls.js | 5 +++- src/game/FrontendRequest.cpp | 8 ++++++ src/game/FrontendRequest.h | 3 +++ src/game/Game.cpp | 38 ++++++++++++++++++++++++--- src/game/Game.h | 5 ++++ src/game/base/Base.cpp | 21 ++++++++------- src/game/base/Base.h | 6 +++-- src/game/bindings/Bases.cpp | 10 +++++-- src/game/bindings/Factions.cpp | 15 +++++++++++ src/game/event/SpawnBase.cpp | 14 +++++++--- src/game/event/SpawnBase.h | 6 ++++- src/game/rules/Faction.h | 6 +++++ src/gse/callable/Native.h | 8 +++--- src/loader/txt/TXTLoader.cpp | 2 +- src/task/game/CMakeLists.txt | 1 + src/task/game/Game.cpp | 5 ++-- src/task/game/TileObject.cpp | 19 ++++++++++++++ src/task/game/TileObject.h | 29 +++++++++++++++++++++ src/task/game/base/Base.cpp | 25 +++++++++++++++--- src/task/game/base/Base.h | 20 +++++++++----- src/task/game/base/BaseManager.cpp | 42 +++++++++--------------------- src/task/game/base/BaseManager.h | 17 +++++++++--- src/task/game/faction/Faction.cpp | 6 +++++ src/task/game/faction/Faction.h | 7 +++++ src/task/game/tile/Tile.cpp | 3 +++ src/task/game/unit/Unit.cpp | 9 ++----- src/task/game/unit/Unit.h | 11 ++++---- 27 files changed, 256 insertions(+), 85 deletions(-) create mode 100644 src/task/game/TileObject.cpp create mode 100644 src/task/game/TileObject.h diff --git a/GLSMAC_data/default/main.gls.js b/GLSMAC_data/default/main.gls.js index 80401663..fbd57160 100644 --- a/GLSMAC_data/default/main.gls.js +++ b/GLSMAC_data/default/main.gls.js @@ -87,7 +87,10 @@ units.init(); if (#game.random.get_int(0, 2) == 0) { #game.bases.spawn( owner, - tile + tile, + { + // name: 'base name', + } ); bases_spawned++; } diff --git a/src/game/FrontendRequest.cpp b/src/game/FrontendRequest.cpp index 26d26ff2..28adba44 100644 --- a/src/game/FrontendRequest.cpp +++ b/src/game/FrontendRequest.cpp @@ -59,6 +59,10 @@ FrontendRequest::FrontendRequest( const FrontendRequest& other ) NEW( data.unit_spawn.morale_string, std::string, *other.data.unit_spawn.morale_string ); break; } + case FR_BASE_SPAWN: { + NEW( data.base_spawn.base_info.name, std::string, *other.data.base_spawn.base_info.name ); + break; + } default: { // @@ -110,6 +114,10 @@ FrontendRequest::~FrontendRequest() { DELETE( data.unit_spawn.morale_string ); break; } + case FR_BASE_SPAWN: { + DELETE( data.base_spawn.base_info.name ); + break; + } default: { // } diff --git a/src/game/FrontendRequest.h b/src/game/FrontendRequest.h index 12bcb17b..bee27f01 100644 --- a/src/game/FrontendRequest.h +++ b/src/game/FrontendRequest.h @@ -151,6 +151,9 @@ class FrontendRequest { float y; float z; } render_coords; + struct { + const std::string* name; + } base_info; } base_spawn; } data; }; diff --git a/src/game/Game.cpp b/src/game/Game.cpp index c5a94af7..9e1e8abd 100644 --- a/src/game/Game.cpp +++ b/src/game/Game.cpp @@ -925,13 +925,42 @@ void Game::SpawnBase( base::Base* base ) { return; } - Log( "Spawning base #" + std::to_string( base->m_id ) + " at " + base->GetTile()->ToString() ); + 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 ); - auto* tile = base->GetTile(); - QueueBaseUpdate( base, BUO_SPAWN ); if ( m_state->IsMaster() ) { @@ -944,6 +973,8 @@ void Game::SpawnBase( base::Base* base ) { } ); } + + RefreshBase( base ); } const std::string* Game::MoveUnitValidate( unit::Unit* unit, map::tile::Tile* dst_tile ) { @@ -2098,6 +2129,7 @@ void Game::PushBaseUpdates() { c.y, c.z }; + NEW( fr.data.base_spawn.base_info.name, std::string, base->m_name ); AddFrontendRequest( fr ); } if ( bu.ops & BUO_REFRESH ) { diff --git a/src/game/Game.h b/src/game/Game.h index 93828872..22331350 100644 --- a/src/game/Game.h +++ b/src/game/Game.h @@ -1,6 +1,8 @@ #pragma once #include +#include +#include #include #include @@ -331,6 +333,7 @@ CLASS( Game, MTModule ) void SpawnUnit( unit::Unit* unit ); void SkipUnitTurn( const size_t unit_id ); void DespawnUnit( const size_t unit_id ); + std::string RegisterBaseName( const std::string& requested_name ); 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 ); @@ -468,6 +471,8 @@ CLASS( Game, MTModule ) 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: friend class bindings::Bindings; void PushUnitUpdates(); diff --git a/src/game/base/Base.cpp b/src/game/base/Base.cpp index c91a640a..29f8d227 100644 --- a/src/game/base/Base.cpp +++ b/src/game/base/Base.cpp @@ -25,27 +25,29 @@ Base::Base( Game* game, const size_t id, slot::Slot* owner, - map::tile::Tile* tile + map::tile::Tile* tile, + const std::string& name ) : MapObject( game->GetMap(), tile ) , m_game( game ) , m_id( id ) - , m_owner( owner ) { + , m_owner( owner ) + , m_name( name ) { if ( next_id <= id ) { next_id = id + 1; } ASSERT_NOLOG( !tile->base, "tile already has base" ); tile->base = this; m_tile = tile; - m_game->RefreshBase( this ); } -const types::Buffer Base::Serialize( const Base* unit ) { +const types::Buffer Base::Serialize( const Base* base ) { types::Buffer buf; - buf.WriteInt( unit->m_id ); - buf.WriteInt( unit->m_owner->GetIndex() ); - buf.WriteInt( unit->m_tile->coord.x ); - buf.WriteInt( unit->m_tile->coord.y ); + buf.WriteInt( base->m_id ); + buf.WriteInt( base->m_owner->GetIndex() ); + buf.WriteInt( base->m_tile->coord.x ); + buf.WriteInt( base->m_tile->coord.y ); + buf.WriteString( base->m_name ); return buf; } @@ -55,7 +57,8 @@ Base* Base::Unserialize( types::Buffer& buf, Game* game ) { const auto pos_x = buf.ReadInt(); const auto pos_y = buf.ReadInt(); auto* tile = game ? game->GetMap()->GetTile( pos_x, pos_y ) : nullptr; - return new Base( game, id, slot, tile ); + const auto name = buf.ReadString(); + return new Base( game, id, slot, tile, name ); } WRAPIMPL_DYNAMIC_GETTERS( Base, CLASS_BASE ) diff --git a/src/game/base/Base.h b/src/game/base/Base.h index b10732e4..333dcd23 100644 --- a/src/game/base/Base.h +++ b/src/game/base/Base.h @@ -29,14 +29,16 @@ class Base : public gse::Wrappable, public MapObject { Game* game, const size_t id, slot::Slot* owner, - map::tile::Tile* tile + map::tile::Tile* tile, + const std::string& name ); virtual ~Base() = default; const size_t m_id; slot::Slot* m_owner; + std::string m_name; - static const types::Buffer Serialize( const Base* unit ); + static const types::Buffer Serialize( const Base* base ); static Base* Unserialize( types::Buffer& buf, Game* game ); WRAPDEFS_DYNAMIC( Base ); diff --git a/src/game/bindings/Bases.cpp b/src/game/bindings/Bases.cpp index 74df46aa..ced36fd2 100644 --- a/src/game/bindings/Bases.cpp +++ b/src/game/bindings/Bases.cpp @@ -6,6 +6,7 @@ #include "gse/Exception.h" #include "gse/type/Undefined.h" #include "gse/type/Object.h" +#include "gse/type/String.h" #include "game/Game.h" #include "game/slot/Slot.h" #include "game/map/tile/Tile.h" @@ -19,15 +20,20 @@ BINDING_IMPL( bases ) { { "spawn", NATIVE_CALL( this ) { - N_EXPECT_ARGS( 2 ); + N_EXPECT_ARGS( 3 ); 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, "" ); + auto* game = GAME; game->AddEvent( new event::SpawnBase( game->GetSlotNum(), owner->GetIndex(), tile->coord.x, - tile->coord.y + tile->coord.y, + name ) ); return VALUE( gse::type::Undefined ); }) diff --git a/src/game/bindings/Factions.cpp b/src/game/bindings/Factions.cpp index 229fb78a..b36b7399 100644 --- a/src/game/bindings/Factions.cpp +++ b/src/game/bindings/Factions.cpp @@ -106,6 +106,21 @@ BINDING_IMPL( factions ) { 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 ); diff --git a/src/game/event/SpawnBase.cpp b/src/game/event/SpawnBase.cpp index c0adfec2..965737e6 100644 --- a/src/game/event/SpawnBase.cpp +++ b/src/game/event/SpawnBase.cpp @@ -16,12 +16,14 @@ SpawnBase::SpawnBase( const size_t initiator_slot, const size_t owner_slot, const size_t pos_x, - const size_t pos_y + const size_t pos_y, + const std::string& name ) : Event( initiator_slot, ET_BASE_SPAWN ) , m_owner_slot( owner_slot ) , m_pos_x( pos_x ) - , m_pos_y( pos_y ) { + , m_pos_y( pos_y ) + , m_name( name ) { // } @@ -41,7 +43,8 @@ const gse::Value SpawnBase::Apply( game::Game* game ) const { game, base::Base::GetNextId(), &owner, - tile + tile, + m_name ); game->SpawnBase( base ); return base->Wrap(); @@ -52,6 +55,7 @@ TS_BEGIN( SpawnBase ) TS_FUNC_ARG_NUM( "owner_slot", m_owner_slot ) + TS_FUNC_ARG_NUM( "pos_x", m_pos_x ) + TS_FUNC_ARG_NUM( "pos_y", m_pos_y ) + + TS_FUNC_ARG_STR( "name", m_name ) + TS_FUNC_END() TS_END() @@ -59,13 +63,15 @@ void SpawnBase::Serialize( types::Buffer& buf, const SpawnBase* event ) { buf.WriteInt( event->m_owner_slot ); buf.WriteInt( event->m_pos_x ); buf.WriteInt( event->m_pos_y ); + buf.WriteString( event->m_name ); } SpawnBase* SpawnBase::Unserialize( types::Buffer& buf, const size_t initiator_slot ) { const auto owner_slot = buf.ReadInt(); const auto pos_x = buf.ReadInt(); const auto pos_y = buf.ReadInt(); - return new SpawnBase( initiator_slot, owner_slot, pos_x, pos_y ); + const auto name = buf.ReadString(); + return new SpawnBase( initiator_slot, owner_slot, pos_x, pos_y, name ); } } diff --git a/src/game/event/SpawnBase.h b/src/game/event/SpawnBase.h index fe209c5d..c034b13d 100644 --- a/src/game/event/SpawnBase.h +++ b/src/game/event/SpawnBase.h @@ -1,5 +1,7 @@ #pragma once +#include + #include "Event.h" #include "game/unit/Types.h" @@ -13,7 +15,8 @@ class SpawnBase : public Event { const size_t initiator_slot, const size_t owner_slot, const size_t pos_x, - const size_t pos_y + const size_t pos_y, + const std::string& name ); const std::string* Validate( Game* game ) const override; @@ -30,6 +33,7 @@ class SpawnBase : public Event { const size_t m_owner_slot; const size_t m_pos_x; const size_t m_pos_y; + const std::string m_name; }; } diff --git a/src/game/rules/Faction.h b/src/game/rules/Faction.h index 3828030a..372a44e2 100644 --- a/src/game/rules/Faction.h +++ b/src/game/rules/Faction.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include "types/Serializable.h" @@ -30,6 +31,11 @@ CLASS2( Faction, types::Serializable, gse::Wrappable ) types::Color border = {}; } m_colors = {}; + struct { + std::vector< std::string > land = {}; + std::vector< std::string > water = {}; + } m_base_names = {}; + bases_render_info_t m_bases_render = {}; const types::Buffer Serialize() const override; diff --git a/src/gse/callable/Native.h b/src/gse/callable/Native.h index f8905933..1c8ec497 100644 --- a/src/gse/callable/Native.h +++ b/src/gse/callable/Native.h @@ -55,11 +55,13 @@ namespace callable { arg = arguments.at( _index ).Get(); \ N_CHECKARG( arg, _index, _type ); \ auto _var = ((gse::type::_type*)arg)->value; -#define N_GETVALUE( _var, _index, _type ) \ - ASSERT_NOLOG( _index < arguments.size(), "argument index overflow" ); \ - arg = arguments.at( _index ).Get(); \ +#define N_GETELEMENT( _var, _arr, _index, _type ) \ + ASSERT_NOLOG( _index < _arr.size(), #_arr " index overflow" ); \ + arg = _arr.at( _index ).Get(); \ N_CHECKARG( arg, _index, _type ); \ const auto& _var = ((gse::type::_type*)arg)->value; +#define N_GETVALUE( _var, _index, _type ) \ + N_GETELEMENT( _var, arguments, _index, _type ); #define N_GETOBJ( _value, _class ) \ arg = _value.Get()->Deref(); \ N_CHECKTYPE( arg, Object ); \ diff --git a/src/loader/txt/TXTLoader.cpp b/src/loader/txt/TXTLoader.cpp index d4bada90..de98eefd 100644 --- a/src/loader/txt/TXTLoader.cpp +++ b/src/loader/txt/TXTLoader.cpp @@ -43,7 +43,7 @@ TXTLoader::txt_data_t::txt_data_t( const std::string& source ) { } } else { - if ( section_it != sections.end() ) { + if ( !line.empty() && section_it != sections.end() ) { section_it->second.push_back( line ); } } diff --git a/src/task/game/CMakeLists.txt b/src/task/game/CMakeLists.txt index e5c3473c..de4713ee 100644 --- a/src/task/game/CMakeLists.txt +++ b/src/task/game/CMakeLists.txt @@ -11,6 +11,7 @@ SET( SRC ${SRC} ${PWD}/Game.cpp ${PWD}/Slot.cpp + ${PWD}/TileObject.cpp ${PWD}/AnimationDef.cpp ${PWD}/Animation.cpp diff --git a/src/task/game/Game.cpp b/src/task/game/Game.cpp index 68083c11..bf107ef3 100644 --- a/src/task/game/Game.cpp +++ b/src/task/game/Game.cpp @@ -1079,7 +1079,8 @@ void Game::ProcessRequest( const ::game::FrontendRequest* request ) { rc.x, rc.y, rc.z - } + }, + *d.base_info.name ); break; } @@ -1606,7 +1607,7 @@ void Game::Initialize( m_is_resize_handler_set = true; ResetMapState(); - + m_is_initialized = true; } diff --git a/src/task/game/TileObject.cpp b/src/task/game/TileObject.cpp new file mode 100644 index 00000000..9ffbdb7f --- /dev/null +++ b/src/task/game/TileObject.cpp @@ -0,0 +1,19 @@ +#include "TileObject.h" + +#include "tile/Tile.h" + +namespace task { +namespace game { + +TileObject::TileObject( tile::Tile* tile ) + : m_tile( tile ) { + // +} + +void TileObject::UpdateFromTile() { + ASSERT_NOLOG( m_tile, "tile not set" ); + SetRenderCoords( m_tile->GetRenderData().coords.InvertY() ); +} + +} +} diff --git a/src/task/game/TileObject.h b/src/task/game/TileObject.h new file mode 100644 index 00000000..ec5a18d8 --- /dev/null +++ b/src/task/game/TileObject.h @@ -0,0 +1,29 @@ +#pragma once + +#include "types/Vec3.h" + +namespace task { +namespace game { + +namespace tile { +class Tile; +} + +class TileObject { +public: + TileObject( tile::Tile* tile ); + + void UpdateFromTile(); + +protected: + + tile::Tile* m_tile = nullptr; + + virtual void SetRenderCoords( const types::Vec3& coords ) = 0; + +private: + +}; + +} +} \ No newline at end of file diff --git a/src/task/game/base/Base.cpp b/src/task/game/base/Base.cpp index e7f57a0d..cb96bde1 100644 --- a/src/task/game/base/Base.cpp +++ b/src/task/game/base/Base.cpp @@ -8,6 +8,7 @@ #include "task/game/Slot.h" #include "task/game/sprite/Sprite.h" #include "task/game/sprite/InstancedSprite.h" +#include "task/game/text/InstancedText.h" #include "scene/actor/Instanced.h" #include "types/mesh/Rectangle.h" #include "scene/actor/Sprite.h" @@ -18,20 +19,20 @@ namespace game { namespace base { Base::Base( - BaseManager* bm, const size_t id, Slot* slot, tile::Tile* tile, + const bool is_owned, const types::Vec3& render_coords, - const bool is_owned + text::InstancedText* render_name_sprite ) - : m_bm( bm ) + : TileObject( tile ) , m_id( id ) , m_faction( slot->GetFaction() ) - , m_tile( tile ) , m_render( { render_coords, + render_name_sprite, false, 0, } @@ -44,6 +45,7 @@ Base::Base( Base::~Base() { Hide(); m_tile->UnsetBase( this ); + delete m_render.name_sprite; } const size_t Base::GetId() const { @@ -79,6 +81,14 @@ void Base::Show() { } ); + m_render.name_sprite->ShowAt( + { + m_render.coords.x, + m_render.coords.y - 0.25f, + m_render.coords.z - 0.25f + } + ); + m_render.is_rendered = true; } } @@ -86,6 +96,7 @@ void Base::Show() { void Base::Hide() { if ( m_render.is_rendered ) { GetSprite()->instanced_sprite->actor->RemoveInstance( m_render.instance_id ); + m_render.name_sprite->Hide(); m_render.is_rendered = false; } } @@ -94,6 +105,12 @@ const Base::render_data_t& Base::GetRenderData() const { return m_render_data; } +void Base::SetRenderCoords( const types::Vec3& coords ) { + Hide(); + m_render.coords = coords; + Show(); +} + Base::meshtex_t Base::GetMeshTex( const sprite::InstancedSprite* sprite ) { auto* texture = sprite->actor->GetSpriteActor()->GetTexture(); NEWV( mesh, types::mesh::Rectangle ); diff --git a/src/task/game/base/Base.h b/src/task/game/base/Base.h index f5da8916..f2fe2366 100644 --- a/src/task/game/base/Base.h +++ b/src/task/game/base/Base.h @@ -2,6 +2,8 @@ #include +#include "task/game/TileObject.h" + #include "game/unit/Types.h" #include "util/Timer.h" @@ -28,6 +30,10 @@ class Sprite; class InstancedSprite; } +namespace text { +class InstancedText; +} + namespace tile { class Tile; } @@ -40,17 +46,16 @@ namespace base { class BaseManager; -class Base { +class Base : public TileObject { public: - // TODO: refactor Base( - BaseManager* bm, const size_t id, Slot* slot, tile::Tile* tile, + const bool is_owned, const types::Vec3& render_coords, - const bool is_owned + text::InstancedText* render_name_sprite ); ~Base(); @@ -72,17 +77,18 @@ class Base { }; const render_data_t& GetRenderData() const; -private: +protected: + void SetRenderCoords( const types::Vec3& coords ) override; - BaseManager* m_bm = nullptr; +private: size_t m_id = 0; faction::Faction* m_faction = nullptr; - tile::Tile* m_tile = nullptr; struct { types::Vec3 coords = {}; + text::InstancedText* name_sprite = nullptr; bool is_rendered = false; size_t instance_id = 0; } m_render; diff --git a/src/task/game/base/BaseManager.cpp b/src/task/game/base/BaseManager.cpp index 60932333..6a844996 100644 --- a/src/task/game/base/BaseManager.cpp +++ b/src/task/game/base/BaseManager.cpp @@ -20,8 +20,7 @@ namespace base { BaseManager::BaseManager( Game* game ) : m_game( game ) - , m_ism( game->GetISM() ) - , m_slot_index( game->GetMySlotIndex() ) { + , m_name_font( game->GetITM()->GetInstancedFont( g_engine->GetFontLoader()->LoadFont( resource::TTF_ARIALN, 36 ) ) ) { } @@ -40,53 +39,36 @@ void BaseManager::SpawnBase( const size_t base_id, const size_t slot_index, const types::Vec2< size_t >& tile_coords, - const types::Vec3& render_coords + const types::Vec3& render_coords, + const std::string& name ) { ASSERT( m_bases.find( base_id ) == m_bases.end(), "base id already exists" ); - auto* slot = m_game->GetSlot( slot_index ); auto* tile = m_game->GetTM()->GetTile( tile_coords ); + auto* slot = m_game->GetSlot( slot_index ); + auto* faction = slot->GetFaction(); m_bases.insert( { base_id, new base::Base( - this, base_id, slot, tile, - { - render_coords.x, - render_coords.y, - render_coords.z - }, - slot_index == m_slot_index + slot_index == m_game->GetMySlotIndex(), + render_coords, + m_game->GetITM()->CreateInstancedText( + name, + m_name_font, + faction->m_colors.text + ) ) } ); m_game->RenderTile( tile, m_game->GetUM()->GetSelectedUnit() ); - // TEST - - auto* itm = m_game->GetITM(); - auto* font = itm->GetInstancedFont( g_engine->GetFontLoader()->LoadFont( resource::TTF_ARIALN, 48 ) ); - - auto* text = itm->CreateInstancedText( - "Base_Name", - font, - slot->GetFaction()->m_colors.text - ); - - text->ShowAt( - { - render_coords.x, - render_coords.y - 0.25f, - render_coords.z - 0.25f - } - ); - } /* TODO void BaseManager::DespawnBase( const size_t base_id ) { diff --git a/src/task/game/base/BaseManager.h b/src/task/game/base/BaseManager.h index d707e935..b584ec3e 100644 --- a/src/task/game/base/BaseManager.h +++ b/src/task/game/base/BaseManager.h @@ -18,11 +18,18 @@ namespace task { namespace game { class Game; +class Slot; namespace sprite { class InstancedSpriteManager; } +namespace text { +class InstancedFont; +class InstancedText; +class InstancedTextManager; +} + namespace tile { class Tile; } @@ -46,16 +53,18 @@ CLASS( BaseManager, common::Class ) const size_t base_id, const size_t slot_index, const ::types::Vec2< size_t >& tile_coords, - const ::types::Vec3& render_coords + const ::types::Vec3& render_coords, + const std::string& name ); // TODO void DespawnBase( const size_t base_id ); private: + friend class base::Base; - Game* m_game; - sprite::InstancedSpriteManager* m_ism; +private: - const size_t m_slot_index; + Game* m_game; + text::InstancedFont* m_name_font; std::unordered_map< size_t, base::Base* > m_bases = {}; diff --git a/src/task/game/faction/Faction.cpp b/src/task/game/faction/Faction.cpp index 39a07a94..9dfb0425 100644 --- a/src/task/game/faction/Faction.cpp +++ b/src/task/game/faction/Faction.cpp @@ -20,6 +20,12 @@ Faction::Faction( const ::game::rules::Faction* def, sprite::InstancedSpriteMana def->m_colors.border } ) + , m_base_names( + { + def->m_base_names.land, + def->m_base_names.water, + } + ) , m_is_progenitor( def->m_flags & ::game::rules::Faction::FF_PROGENITOR ) , m_render( { diff --git a/src/task/game/faction/Faction.h b/src/task/game/faction/Faction.h index 642e21c7..9f3c6583 100644 --- a/src/task/game/faction/Faction.h +++ b/src/task/game/faction/Faction.h @@ -1,6 +1,8 @@ #pragma once #include +#include +#include #include "game/rules/Types.h" @@ -40,6 +42,11 @@ class Faction { const types::Color border = {}; } m_colors = {}; + struct { + const std::vector< std::string > land = {}; + const std::vector< std::string > water = {}; + } m_base_names = {}; + const bool m_is_progenitor; private: diff --git a/src/task/game/tile/Tile.cpp b/src/task/game/tile/Tile.cpp index ee13de19..40104c31 100644 --- a/src/task/game/tile/Tile.cpp +++ b/src/task/game/tile/Tile.cpp @@ -481,6 +481,9 @@ void Tile::Update( const ::game::map::tile::Tile& tile, const ::game::map::tile: for ( const auto& it : m_units ) { it.second->UpdateFromTile(); } + if ( m_base ) { + m_base->UpdateFromTile(); + } } } diff --git a/src/task/game/unit/Unit.cpp b/src/task/game/unit/Unit.cpp index de573fb7..a9243a17 100644 --- a/src/task/game/unit/Unit.cpp +++ b/src/task/game/unit/Unit.cpp @@ -33,12 +33,12 @@ Unit::Unit( const std::string& morale_string, const ::game::unit::health_t health ) - : m_um( um ) + : TileObject( tile ) + , m_um( um ) , m_badge_defs( badge_defs ) , m_id( id ) , m_def( def ) , m_slot_badges( m_um->GetSlotBadges( slot->GetIndex() ) ) - , m_tile( tile ) , m_render( { render_coords, @@ -333,11 +333,6 @@ void Unit::MoveToTile( tile::Tile* dst_tile ) { m_mover.Scroll( from, m_um->GetCloserCoords( to, from ), MOVE_DURATION_MS ); } -void Unit::UpdateFromTile() { - ASSERT_NOLOG( m_tile, "tile not set" ); - SetRenderCoords( m_tile->GetRenderData().coords.InvertY() ); -} - const bool Unit::IsMoving() const { return m_mover.IsRunning(); } diff --git a/src/task/game/unit/Unit.h b/src/task/game/unit/Unit.h index 41bbf201..d2aada6b 100644 --- a/src/task/game/unit/Unit.h +++ b/src/task/game/unit/Unit.h @@ -2,6 +2,8 @@ #include +#include "task/game/TileObject.h" + #include "game/unit/Types.h" #include "util/Timer.h" @@ -39,12 +41,11 @@ class BadgeDefs; class SlotBadges; class UnitManager; -class Unit { +class Unit : public TileObject { public: static constexpr size_t MOVE_DURATION_MS = 125; - // TODO: refactor Unit( UnitManager* um, BadgeDefs* badge_defs, @@ -102,7 +103,6 @@ class Unit { void SetTile( tile::Tile* dst_tile ); void MoveToTile( tile::Tile* dst_tile ); - void UpdateFromTile(); const bool IsMoving() const; @@ -117,6 +117,9 @@ class Unit { }; const render_data_t& GetRenderData() const; +protected: + void SetRenderCoords( const types::Vec3& coords ) override; + private: UnitManager* m_um = nullptr; @@ -126,7 +129,6 @@ class Unit { size_t m_id = 0; UnitDef* m_def = nullptr; - tile::Tile* m_tile = nullptr; struct { types::Vec3 coords = {}; bool is_rendered = false; @@ -164,7 +166,6 @@ class Unit { util::Scroller< types::Vec3 > m_mover; Unit::meshtex_t GetMeshTex( const sprite::InstancedSprite* sprite ); - void SetRenderCoords( const types::Vec3& coords ); }; } From 330feced02b600f9ea8dece368cc366b792c3364 Mon Sep 17 00:00:00 2001 From: afwbkbc Date: Sat, 29 Jun 2024 13:29:29 +0300 Subject: [PATCH 10/18] added --datapath --- src/config/Config.cpp | 16 +++++++++++++--- src/config/Config.h | 3 ++- src/game/bindings/Bindings.cpp | 5 +++-- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/config/Config.cpp b/src/config/Config.cpp index cca96dc9..00b9ef38 100644 --- a/src/config/Config.cpp +++ b/src/config/Config.cpp @@ -41,8 +41,9 @@ const types::Vec2< size_t > Config::ParseSize( const std::string& value ) { Config::Config( const int argc, const char* argv[] ) : m_smac_path( "" ) + , m_data_path( "GLSMAC_data" ) , m_prefix( DEFAULT_GLSMAC_PREFIX + util::FS::PATH_SEPARATOR ) { - + m_parser = new util::ArgParser( argc, argv ); m_parser->AddRule( @@ -51,8 +52,8 @@ Config::Config( const int argc, const char* argv[] ) } ); m_parser->AddRule( - "showfps", "Show FPS counter at top left corner", AH( this ) { - m_launch_flags |= LF_SHOWFPS; + "datapath", "DATA_PATH", "Specify path to GLSMAC_data directory (default: " + util::FS::GetCurrentDirectory() + "/" + m_data_path + ")", AH( this ) { + m_data_path = value; } ); m_parser->AddRule( @@ -76,6 +77,11 @@ Config::Config( const int argc, const char* argv[] ) m_launch_flags |= LF_SKIPINTRO; } ); + m_parser->AddRule( + "showfps", "Show FPS counter at top left corner", AH( this ) { + m_launch_flags |= LF_SHOWFPS; + } + ); m_parser->AddRule( "smacpath", "SMAC_PATH", "Specify path to SMAC directory (must contain SMACX too)", AH( this ) { m_smac_path = value; @@ -309,6 +315,10 @@ const std::string Config::GetDebugPath() const { #endif +const std::string& Config::GetDataPath() const { + return m_data_path; +} + const std::vector< std::string > Config::GetPossibleSMACPaths() const { std::vector< std::string > result = {}; if ( !m_smac_path.empty() ) { diff --git a/src/config/Config.h b/src/config/Config.h index 80b05da2..ea52feb7 100644 --- a/src/config/Config.h +++ b/src/config/Config.h @@ -57,7 +57,7 @@ CLASS( Config, common::Module ) const std::string GetEnv( const std::string& var ) const; const std::string& GetPrefix() const; - + const std::string& GetDataPath() const; const std::vector< std::string > GetPossibleSMACPaths() const; #ifdef DEBUG @@ -100,6 +100,7 @@ CLASS( Config, common::Module ) ; std::string m_prefix; + std::string m_data_path; std::string m_smac_path; uint8_t m_launch_flags = LF_NONE; diff --git a/src/game/bindings/Bindings.cpp b/src/game/bindings/Bindings.cpp index 970811e2..6911adbf 100644 --- a/src/game/bindings/Bindings.cpp +++ b/src/game/bindings/Bindings.cpp @@ -11,7 +11,8 @@ #include "gse/type/Object.h" #include "gse/type/Callable.h" #include "gse/type/Undefined.h" - +#include "engine/Engine.h" +#include "config/Config.h" #include "game/State.h" namespace game { @@ -22,7 +23,7 @@ Bindings::Bindings( State* state ) , m_entry_script( util::FS::GeneratePath( { - "GLSMAC_data", // directory is expected to be in working dir + g_engine->GetConfig()->GetDataPath(), "default", // only 'default' mod for now "main" // script name (extension is appended automatically) }, gse::GSE::PATH_SEPARATOR From 48f2f7a3f00d8f3842340ffe8d8efb5923f49d5f Mon Sep 17 00:00:00 2001 From: afwbkbc Date: Sat, 29 Jun 2024 14:03:59 +0300 Subject: [PATCH 11/18] small badge blink fix --- src/task/game/tile/Tile.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/task/game/tile/Tile.cpp b/src/task/game/tile/Tile.cpp index 40104c31..17f4945e 100644 --- a/src/task/game/tile/Tile.cpp +++ b/src/task/game/tile/Tile.cpp @@ -139,6 +139,9 @@ void Tile::Render( size_t selected_unit_id ) { if ( id == most_important_unit_id ) { m_render.currently_rendered_unit->ShowBadge(); } + if ( id == selected_unit_id ) { + m_render.currently_rendered_unit->StartBadgeBlink(); + } } size_t idx; const auto& fake_badges = m_render.currently_rendered_fake_badges; From 8d7e0d196dc88102ac6b9933c3153959f50afa31 Mon Sep 17 00:00:00 2001 From: afwbkbc Date: Sat, 29 Jun 2024 16:44:32 +0300 Subject: [PATCH 12/18] gse: implemented break and continue, return undefined by default --- GLSMAC_data/default/factions/gaians.gls.js | 2 - GLSMAC_data/default/main.gls.js | 29 ++---- GLSMAC_data/default/units/movement.gls.js | 4 +- GLSMAC_data/tests/types.gls.js | 84 +++++++++++++++- src/game/bindings/Bindings.cpp | 4 + src/gse/Value.cpp | 2 + src/gse/parser/JS.cpp | 18 ++++ src/gse/parser/JS.h | 39 +++++++- src/gse/parser/Parser.cpp | 2 +- src/gse/parser/Parser.h | 22 +++++ src/gse/program/CMakeLists.txt | 1 + src/gse/program/LoopControl.cpp | 27 ++++++ src/gse/program/LoopControl.h | 24 +++++ src/gse/program/Operand.h | 1 + src/gse/program/Operator.cpp | 8 ++ src/gse/program/Types.h | 8 ++ src/gse/runner/Interpreter.cpp | 107 ++++++++++++++++++--- src/gse/runner/Interpreter.h | 1 + src/gse/type/LoopControl.h | 24 +++++ src/gse/type/Nothing.h | 21 ++++ src/gse/type/Type.cpp | 17 ++++ src/gse/type/Type.h | 2 + 22 files changed, 400 insertions(+), 47 deletions(-) create mode 100644 src/gse/program/LoopControl.cpp create mode 100644 src/gse/program/LoopControl.h create mode 100644 src/gse/type/LoopControl.h create mode 100644 src/gse/type/Nothing.h diff --git a/GLSMAC_data/default/factions/gaians.gls.js b/GLSMAC_data/default/factions/gaians.gls.js index 78ebb535..d07ddeee 100644 --- a/GLSMAC_data/default/factions/gaians.gls.js +++ b/GLSMAC_data/default/factions/gaians.gls.js @@ -16,6 +16,4 @@ const faction = { }, }; -#print('FACTION', faction); - return faction; \ No newline at end of file diff --git a/GLSMAC_data/default/main.gls.js b/GLSMAC_data/default/main.gls.js index fbd57160..9f81766a 100644 --- a/GLSMAC_data/default/main.gls.js +++ b/GLSMAC_data/default/main.gls.js @@ -60,14 +60,14 @@ units.init(); if (tile.has_fungus && #game.random.get_int(0, 3) == 0) { // morale depends on count of fungus tiles around let morale = 1; - let neighbours = tile.get_surrounding_tiles(); - let sz = #size(neighbours); - let i = 0; - while (morale < 6 && i < sz) { - if (neighbours[i].has_fungus) { + for (neighbour of tile.get_surrounding_tiles()) { + if (morale >= 6) { + break; + } + #print(neighbour); + if (neighbour.has_fungus) { morale++; } - i++; } #game.units.spawn('FungalTower', owner, tile, morale, random_health()); units_spawned++; @@ -100,23 +100,6 @@ units.init(); #game.message('Total units spawned: ' + #to_string(units_spawned)); #game.message('Total bases spawned: ' + #to_string(bases_spawned)); - // spawn bases - - /*let i = 0; - while (i < players_sz) { - let base_x = 0; - let base_y = 1; - while (base_x % 2 != base_y % 2) { - base_x = #game.random.get_int(1, w - 2); - base_y = #game.random.get_int(1, h - 2); - } - #game.bases.spawn( - players[i], - #game.map.get_tile(base_x, base_y) - ); - i++; - }*/ - }); #game.on.turn((e) => { diff --git a/GLSMAC_data/default/units/movement.gls.js b/GLSMAC_data/default/units/movement.gls.js index 997f6a72..264ca18a 100644 --- a/GLSMAC_data/default/units/movement.gls.js +++ b/GLSMAC_data/default/units/movement.gls.js @@ -58,10 +58,10 @@ const result = { } let any_foreign_units_in_tile = false; - for (unit of e.dst_tile.get_units()) { // TODO: for loop + for (unit of e.dst_tile.get_units()) { if (unit.get_owner() != e.unit.get_owner()) { any_foreign_units_in_tile = true; - // TODO: break + break; } } if (any_foreign_units_in_tile) { diff --git a/GLSMAC_data/tests/types.gls.js b/GLSMAC_data/tests/types.gls.js index f96c8397..b6b9cd4c 100644 --- a/GLSMAC_data/tests/types.gls.js +++ b/GLSMAC_data/tests/types.gls.js @@ -13,6 +13,18 @@ let c = (a + 2) * 4; c = 123; c -= 23; +let emptymethod = () => {}; +test.assert(#typeof(emptymethod()) == 'Undefined'); + +let ret = 0; +let testmethod0 = () => { + ret = 1; + return; + ret = 2; +}; +test.assert(#typeof(testmethod0()) == 'Undefined'); +test.assert(ret == 1); + let testmethod1 = (a, b, c) => { return a + b + c; }; @@ -256,6 +268,72 @@ const for_func = () => { }; test.assert(for_func() == 6); +arr = []; +for ( i = 0 ; i < 10 ; i++ ) { + arr []= i; + if (i >= 3) { + arr []= 'x'; + for ( i = 5 ; i < 10 ; i++ ) { + if ( i == 7 ) { + break; + } + arr []= i; + } + arr []= 'y'; + break; + } +} +test.assert(arr == [0, 1, 2, 3, 'x', 5, 6, 'y']); + +arr = []; +i = 5; +while (i > 0) { + if (i == 2 ) { + break; + } + arr []= i; + i--; +} +test.assert(arr == [5, 4, 3]); + +arr = []; +for ( i of [4, 7, 1, 5] ) { + if ( i == 1 ) { + break; + } + arr []= i; +} +test.assert(arr == [4, 7]); + +arr = []; +for ( i = 0 ; i < 10 ; i++ ) { + if ( i < 5 || i > 8 ) { + continue; + } + arr []= i; +} +test.assert(arr == [5, 6, 7, 8]); + +arr = []; +i = 10; +while (i > 0) { + i--; + if (i > 7 || i < 3) { + for ( ii of ['a', 'b', 'c', 'd', 'e', 'f'] ) { + if ( ii == 'a' || ii == 'c' ) { + continue; + } + elseif (ii == 'e') { + break; + } + arr []= ii; + } + continue; + } + arr []= i; +} +test.assert(arr == ['b', 'd', 'b', 'd', 7, 6, 5, 4, 3, 'b', 'd', 'b', 'd', 'b', 'd']); + arr = []; try { arr [] = 'BEFORE EXCEPTION'; // should be printed @@ -288,9 +366,9 @@ test.assert(arr == [ 'failfunc', 'failfunc2', 'CAUGHT TestError : something happened', - '\tat ' + test.get_script_path() + ':265: throw TestError(\'something happened\');', - '\tat ' + test.get_script_path() + ':270: realfailfunc();', - '\tat ' + test.get_script_path() + ':272: failfunc();' + '\tat ' + test.get_script_path() + ':343: throw TestError(\'something happened\');', + '\tat ' + test.get_script_path() + ':348: realfailfunc();', + '\tat ' + test.get_script_path() + ':350: failfunc();' ]); test.assert(#to_string(2 + 3) + ' (five)' == '5 (five)'); diff --git a/src/game/bindings/Bindings.cpp b/src/game/bindings/Bindings.cpp index 6911adbf..5a7fa1aa 100644 --- a/src/game/bindings/Bindings.cpp +++ b/src/game/bindings/Bindings.cpp @@ -85,6 +85,10 @@ gse::Value Bindings::Call( const callback_slot_t slot, const callback_arguments_ if ( game ) { game->PushUnitUpdates(); } + if ( result.Get()->type == gse::type::Type::T_NOTHING ) { + // return undefined by default + return VALUE( gse::type::Undefined ); + } return result; } catch ( gse::Exception& e ) { diff --git a/src/gse/Value.cpp b/src/gse/Value.cpp index 532893bc..521b590b 100644 --- a/src/gse/Value.cpp +++ b/src/gse/Value.cpp @@ -104,6 +104,8 @@ const Value Value::New( const type::Type* type ) const { } case type::Type::T_RANGE: THROW( "ranges are not supposed to be cloned" ); + case type::Type::T_LOOPCONTROL: + THROW( "loop controls are not supposed to be cloned" ); default: { THROW( "unsupported value type: " + ToString() ); } diff --git a/src/gse/parser/JS.cpp b/src/gse/parser/JS.cpp index c2a3ed49..ee95ab8d 100644 --- a/src/gse/parser/JS.cpp +++ b/src/gse/parser/JS.cpp @@ -28,6 +28,7 @@ #include "gse/program/SimpleCondition.h" #include "gse/program/ForConditionInOf.h" #include "gse/program/ForConditionExpressions.h" +#include "gse/program/LoopControl.h" namespace gse { @@ -46,6 +47,7 @@ void JS::GetElements( source_elements_t& elements ) { si_t si; std::string value; std::unordered_map< std::string, Parser::Conditional::conditional_type_t >::const_iterator control_it; + std::unordered_map< std::string, program::loop_control_type_t >::const_iterator loop_control_it; while ( !eof() ) { begin = get_si_pos(); if ( match_sequence( "//", true ) ) { @@ -74,6 +76,10 @@ void JS::GetElements( source_elements_t& elements ) { } else if ( KEYWORDS.find( value ) != KEYWORDS.end() ) { elements.push_back( new Operator( value, si ) ); + loop_control_it = LOOP_CONTROL_KEYWORDS.find( value ); + if ( loop_control_it != LOOP_CONTROL_KEYWORDS.end() ) { + elements.push_back( new LoopControl( loop_control_it->second, si ) ); + } } else { elements.push_back( new Identifier( value, IDENTIFIER_VARIABLE, si ) ); @@ -661,6 +667,10 @@ const program::Operand* JS::GetExpressionOrOperand( const source_elements_t::con } else { elements.push_back( GetOperator( (Operator*)( *it ) ) ); + if ( op == "return" && ( ( it + 1 == it_end ) || ( *(it + 1))->m_type == SourceElement::ET_DELIMITER ) ) { + // return undefined by default + elements.push_back( new program::Value( (*it)->m_si, VALUE( type::Undefined ) ) ); + } } var_hints_allowed = false; break; @@ -864,6 +874,10 @@ const program::Operand* JS::GetExpressionOrOperand( const source_elements_t::con it = it_end; break; } + case SourceElement::ET_LOOP_CONTROL: { + elements.push_back( GetLoopControl( (LoopControl*)*it ) ); + break; + } default: throw Exception( EC.PARSE_ERROR, "Unexpected: " + ( *it )->ToString() + "", nullptr, ( *it )->m_si ); } @@ -1026,6 +1040,10 @@ const program::Object* JS::GetObject( const source_elements_t::const_iterator& b return new program::Object( GetSI( begin - 1, end + 1 ), ordered_properties ); } +const program::LoopControl* JS::GetLoopControl( const LoopControl* loop_control ) { + return new program::LoopControl( loop_control->m_si, loop_control->m_loop_control_type ); +} + const JS::source_elements_t::const_iterator JS::GetBracketsEnd( const source_elements_t::const_iterator& begin, const source_elements_t::const_iterator& end ) const { ASSERT( ( *begin )->m_type == SourceElement::ET_BLOCK, "expected block" ); std::stack< uint8_t > brackets = {}; diff --git a/src/gse/parser/JS.h b/src/gse/parser/JS.h index de420038..922f5374 100644 --- a/src/gse/parser/JS.h +++ b/src/gse/parser/JS.h @@ -24,6 +24,7 @@ class Operator; class Operand; class Object; class Array; +class LoopControl; } namespace parser { @@ -50,6 +51,7 @@ CLASS( JS, Parser ) const program::Operator* GetOperator( const Operator* element ); const program::Array* GetArray( const source_elements_t::const_iterator& begin, const source_elements_t::const_iterator& end ); const program::Object* GetObject( const source_elements_t::const_iterator& begin, const source_elements_t::const_iterator& end ); + const program::LoopControl* GetLoopControl( const LoopControl* loop_control ); const std::string CHARS_WHITESPACE = CHARS_EOLN + " "; const std::string CHARS_NAMES = CHARS_LETTERS + "_#"; @@ -93,6 +95,8 @@ CLASS( JS, Parser ) const std::unordered_set< std::string > KEYWORDS = { "return", + "break", + "continue", "throw", "let", "const", @@ -133,6 +137,17 @@ CLASS( JS, Parser ) } }; + const std::unordered_map< std::string, program::loop_control_type_t > LOOP_CONTROL_KEYWORDS = { + { + "break", + program::LCT_BREAK + }, + { + "continue", + program::LCT_CONTINUE + } + }; + const std::unordered_map< std::string, program::variable_hints_t > MODIFIER_OPERATORS = { { { @@ -170,6 +185,14 @@ CLASS( JS, Parser ) "return", program::OT_RETURN }, + { + "break", + program::OT_BREAK + }, + { + "continue", + program::OT_CONTINUE + }, { "throw", program::OT_THROW @@ -296,7 +319,21 @@ CLASS( JS, Parser ) program::OT_RETURN, { 1, - OL_RIGHT + OL_RIGHT // undefined will be auto-appended if missing + } + }, + { + program::OT_BREAK, + { + 1, + OL_RIGHT // loop control will be auto-appended + } + }, + { + program::OT_CONTINUE, + { + 1, + OL_RIGHT // loop control will be auto-appended } }, { diff --git a/src/gse/parser/Parser.cpp b/src/gse/parser/Parser.cpp index 8c714aea..8431d500 100644 --- a/src/gse/parser/Parser.cpp +++ b/src/gse/parser/Parser.cpp @@ -6,7 +6,7 @@ namespace gse { namespace parser { Parser::Parser( const std::string& filename, const std::string& source, const size_t initial_line_num ) - : m_source( source ) + : m_source( source + '\n' ) , m_filename( filename ) , m_begin( m_source.c_str() ) , m_end( m_source.c_str() + m_source.size() ) { diff --git a/src/gse/parser/Parser.h b/src/gse/parser/Parser.h index e5f7588d..96409686 100644 --- a/src/gse/parser/Parser.h +++ b/src/gse/parser/Parser.h @@ -6,6 +6,7 @@ #include "common/Common.h" #include "gse/Types.h" +#include "gse/program/Types.h" namespace gse { @@ -40,6 +41,7 @@ CLASS( Parser, common::Class ) ET_DELIMITER, ET_CONDITIONAL, ET_BLOCK, + ET_LOOP_CONTROL }; SourceElement( const element_type_t type, const si_t& si ) : m_type( type ) @@ -205,6 +207,26 @@ CLASS( Parser, common::Class ) } } }; + class LoopControl : public SourceElement { + public: + LoopControl( const program::loop_control_type_t loop_control_type, const si_t& si ) + : SourceElement( ET_LOOP_CONTROL, si ) + , m_loop_control_type( loop_control_type ) {} + const program::loop_control_type_t m_loop_control_type; + const std::string ToString() const override { + switch ( m_loop_control_type ) { + case program::LCT_BREAK: + return "break"; + case program::LCT_CONTINUE: + return "continue"; + default: + THROW( "unexpected loop control type: " + std::to_string( m_loop_control_type ) ); + } + } + const std::string Dump() const override { + return "loop_control{" + ToString() + "}"; + } + }; virtual void GetElements( source_elements_t& elements ) = 0; virtual const program::Program* GetProgram( const source_elements_t& elements ) = 0; diff --git a/src/gse/program/CMakeLists.txt b/src/gse/program/CMakeLists.txt index a8e714df..b4b2da8e 100644 --- a/src/gse/program/CMakeLists.txt +++ b/src/gse/program/CMakeLists.txt @@ -9,6 +9,7 @@ SET( SRC ${SRC} ${PWD}/ForCondition.cpp ${PWD}/ForConditionInOf.cpp ${PWD}/ForConditionExpressions.cpp + ${PWD}/LoopControl.cpp ${PWD}/Control.cpp ${PWD}/Element.cpp ${PWD}/Else.cpp diff --git a/src/gse/program/LoopControl.cpp b/src/gse/program/LoopControl.cpp new file mode 100644 index 00000000..a47b887c --- /dev/null +++ b/src/gse/program/LoopControl.cpp @@ -0,0 +1,27 @@ +#include "LoopControl.h" + +#include "common/Assert.h" + +namespace gse { +namespace program { + +LoopControl::LoopControl( const si_t& si, const loop_control_type_t loop_control_type ) + : Operand( si, OT_LOOP_CONTROL ) + , loop_control_type( loop_control_type ) {} + +const std::string LoopControl::ToString() const { + switch ( loop_control_type ) { + case LCT_BREAK: + return "break"; + case LCT_CONTINUE: + return "continue"; + default: + THROW( "unexpected control type: " + std::to_string( loop_control_type ) ); + } +} +const std::string LoopControl::Dump( const size_t depth ) const { + return Formatted( "LoopControl" + m_si.ToString() + "( " + ToString() + " )", depth ); +} + +} +} diff --git a/src/gse/program/LoopControl.h b/src/gse/program/LoopControl.h new file mode 100644 index 00000000..cb46b060 --- /dev/null +++ b/src/gse/program/LoopControl.h @@ -0,0 +1,24 @@ +#pragma once + +#include + +#include "Operand.h" + +#include "Types.h" + +namespace gse { +namespace program { + +class LoopControl : public Operand { +public: + + LoopControl( const si_t& si, const loop_control_type_t loop_control_type ); + + const loop_control_type_t loop_control_type; + + const std::string ToString() const override; + const std::string Dump( const size_t depth = 0 ) const override; +}; + +} +} diff --git a/src/gse/program/Operand.h b/src/gse/program/Operand.h index 243a6af6..974ee924 100644 --- a/src/gse/program/Operand.h +++ b/src/gse/program/Operand.h @@ -17,6 +17,7 @@ class Operand : public Element { OT_EXPRESSION, OT_FUNCTION, OT_CALL, + OT_LOOP_CONTROL, }; Operand( const si_t& si, const operand_type_t type ); diff --git a/src/gse/program/Operator.cpp b/src/gse/program/Operator.cpp index d46c769d..42a403bc 100644 --- a/src/gse/program/Operator.cpp +++ b/src/gse/program/Operator.cpp @@ -14,6 +14,14 @@ static const std::unordered_map< operator_type_t, std::string > s_op_labels = { OT_RETURN, "return" }, + { + OT_BREAK, + "break" + }, + { + OT_CONTINUE, + "continue" + }, { OT_THROW, "throw" diff --git a/src/gse/program/Types.h b/src/gse/program/Types.h index 51e422c4..8657225e 100644 --- a/src/gse/program/Types.h +++ b/src/gse/program/Types.h @@ -14,6 +14,8 @@ enum variable_hints_t : uint8_t { enum operator_type_t { OT_NOOP, OT_RETURN, + OT_BREAK, + OT_CONTINUE, OT_THROW, OT_ASSIGN, OT_NOT, @@ -43,5 +45,11 @@ enum operator_type_t { OT_RANGE, }; +enum loop_control_type_t { + LCT_NONE, + LCT_BREAK, + LCT_CONTINUE, +}; + } } diff --git a/src/gse/runner/Interpreter.cpp b/src/gse/runner/Interpreter.cpp index 715455e6..30b57ae4 100644 --- a/src/gse/runner/Interpreter.cpp +++ b/src/gse/runner/Interpreter.cpp @@ -6,7 +6,6 @@ #include "gse/program/Object.h" #include "gse/program/Value.h" #include "gse/program/Expression.h" -#include "gse/program/Condition.h" #include "gse/program/SimpleCondition.h" #include "gse/program/ForCondition.h" #include "gse/program/ForConditionInOf.h" @@ -25,8 +24,10 @@ #include "gse/program/For.h" #include "gse/program/Try.h" #include "gse/program/Catch.h" +#include "gse/program/LoopControl.h" #include "gse/type/Type.h" #include "gse/type/Undefined.h" +#include "gse/type/Nothing.h" #include "gse/type/Bool.h" #include "gse/type/Int.h" #include "gse/type/Float.h" @@ -38,6 +39,7 @@ #include "gse/type/ObjectRef.h" #include "gse/type/Callable.h" #include "gse/type/Range.h" +#include "gse/type/LoopControl.h" #include "gse/type/Exception.h" #include "gse/type/Undefined.h" @@ -57,7 +59,7 @@ const gse::Value Interpreter::EvaluateScope( context::Context* ctx, const Scope* const auto subctx = ctx->ForkContext( ctx, scope->m_si, false ); subctx->IncRefs(); - gse::Value result = VALUE( Undefined ); + gse::Value result = VALUE( Nothing ); for ( const auto& it : scope->body ) { switch ( it->control_type ) { case Control::CT_STATEMENT: { @@ -71,7 +73,7 @@ const gse::Value Interpreter::EvaluateScope( context::Context* ctx, const Scope* default: THROW( "unexpected control type: " + it->Dump() ); } - if ( result.Get()->type != Type::T_UNDEFINED ) { + if ( result.Get()->type != Type::T_NOTHING ) { // got return statement break; } @@ -93,7 +95,7 @@ const gse::Value Interpreter::EvaluateStatement( context::Context* ctx, const St if ( returnflag ) { return result; } - return VALUE( Undefined ); + return VALUE( Nothing ); } const gse::Value Interpreter::EvaluateConditional( context::Context* ctx, const Conditional* conditional, bool is_nested ) const { @@ -107,7 +109,7 @@ const gse::Value Interpreter::EvaluateConditional( context::Context* ctx, const return EvaluateConditional( ctx, c->els, true ); } else { - return VALUE( Undefined ); + return VALUE( Nothing ); } } case Conditional::CT_ELSEIF: { @@ -123,7 +125,7 @@ const gse::Value Interpreter::EvaluateConditional( context::Context* ctx, const return EvaluateConditional( ctx, c->els, true ); } else { - return VALUE( Undefined ); + return VALUE( Nothing ); } } case Conditional::CT_ELSE: { @@ -136,10 +138,16 @@ const gse::Value Interpreter::EvaluateConditional( context::Context* ctx, const } case Conditional::CT_WHILE: { const auto* c = (While*)conditional; - gse::Value result = VALUE( Undefined ); + gse::Value result = VALUE( Nothing ); + bool need_break = false; + bool need_clear = false; while ( EvaluateBool( ctx, c->condition->expression ) ) { result = EvaluateScope( ctx, c->body ); - if ( result.Get()->type != Type::T_UNDEFINED ) { + CheckBreakCondition( result, &need_break, &need_clear ); + if ( need_clear ) { + result = VALUE( Nothing ); + } + if ( need_break ) { break; } } @@ -147,14 +155,20 @@ const gse::Value Interpreter::EvaluateConditional( context::Context* ctx, const } case Conditional::CT_FOR: { const auto* c = (For*)conditional; - gse::Value result = VALUE( Undefined ); + gse::Value result = VALUE( Nothing ); + bool need_break = false; + bool need_clear = false; switch ( c->condition->for_type ) { case ForCondition::FCT_EXPRESSIONS: { const auto* condition = (ForConditionExpressions*)c->condition; EvaluateExpression( ctx, condition->init ); while ( EvaluateBool( ctx, condition->check ) ) { result = EvaluateScope( ctx, c->body ); - if ( result.Get()->type != Type::T_UNDEFINED ) { + CheckBreakCondition( result, &need_break, &need_clear ); + if ( need_break ) { + if ( need_clear ) { + result = VALUE( Nothing ); + } break; } EvaluateExpression( ctx, condition->iterate ); @@ -175,7 +189,11 @@ const gse::Value Interpreter::EvaluateConditional( context::Context* ctx, const forctx->CreateConst( condition->variable->name, VALUE( Int, i ), &condition->m_si ); result = EvaluateScope( forctx, c->body ); forctx->DestroyVariable( condition->variable->name, &condition->m_si ); - if ( result.Get()->type != Type::T_UNDEFINED ) { + CheckBreakCondition( result, &need_break, &need_clear ); + if ( need_break ) { + if ( need_clear ) { + result = VALUE( Nothing ); + } break; } } @@ -186,7 +204,11 @@ const gse::Value Interpreter::EvaluateConditional( context::Context* ctx, const forctx->CreateConst( condition->variable->name, v, &condition->m_si ); result = EvaluateScope( forctx, c->body ); forctx->DestroyVariable( condition->variable->name, &condition->m_si ); - if ( result.Get()->type != Type::T_UNDEFINED ) { + CheckBreakCondition( result, &need_break, &need_clear ); + if ( need_break ) { + if ( need_clear ) { + result = VALUE( Nothing ); + } break; } } @@ -210,7 +232,7 @@ const gse::Value Interpreter::EvaluateConditional( context::Context* ctx, const ); result = EvaluateScope( forctx, c->body ); forctx->DestroyVariable( condition->variable->name, &condition->m_si ); - if ( result.Get()->type != Type::T_UNDEFINED ) { + if ( result.Get()->type != Type::T_NOTHING ) { break; } } @@ -225,6 +247,9 @@ const gse::Value Interpreter::EvaluateConditional( context::Context* ctx, const default: THROW( "unexpected for condition type: " + std::to_string( c->condition->for_type ) ); } + if ( result.Get()->type == Type::T_LOOPCONTROL ) { + return VALUE( Nothing ); // we don't want to break out from parent scope + } return result; } case Conditional::CT_TRY: { @@ -286,6 +311,18 @@ const gse::Value Interpreter::EvaluateExpression( context::Context* ctx, const E *returnflag = true; return Deref( ctx, expression->b->m_si, EvaluateOperand( ctx, expression->b ) ); } + case OT_BREAK: { + ASSERT( returnflag, "break keyword not allowed here" ); + ASSERT( !*returnflag, "already returning" ); + *returnflag = true; + return EvaluateOperand( ctx, expression->b ); + } + case OT_CONTINUE: { + ASSERT( returnflag, "continue keyword not allowed here" ); + ASSERT( !*returnflag, "already returning" ); + *returnflag = true; + return EvaluateOperand( ctx, expression->b ); + } case OT_THROW: { ASSERT( !expression->a, "unexpected left operand before throw" ); const auto& invalid_error_definition = [ &expression, &ctx ]() -> gse::Exception { @@ -483,7 +520,7 @@ const gse::Value Interpreter::EvaluateExpression( context::Context* ctx, const E if ( a->type != b->type ) { \ throw operation_not_supported( a->ToString(), b->ToString() ); \ } \ - gse::Value result = VALUE( Undefined ); \ + gse::Value result = VALUE( Nothing ); \ switch ( a->type ) { \ case Type::T_INT: { \ result = VALUE( Int, ( (Int*)a )->value _op ( (Int*)b )->value ); \ @@ -666,6 +703,9 @@ const gse::Value Interpreter::EvaluateExpression( context::Context* ctx, const E case Type::T_ARRAYRANGEREF: { THROW( "TODO: T_ARRAYRANGEREF" ); } + case Type::T_LOOPCONTROL: { + THROW( "TODO: T_LOOPCONTROL" ); + } case Type::T_OBJECTREF: { const auto arrv = Deref( ctx, expression->a->m_si, refv ); const auto* arr = arrv.Get(); @@ -787,12 +827,20 @@ const gse::Value Interpreter::EvaluateOperand( context::Context* ctx, const Oper for ( const auto& it : call->arguments ) { arguments.push_back( Deref( ctx, it->m_si, EvaluateExpression( ctx, it ) ) ); } - return ( (Callable*)callable.Get() )->Run( ctx, call->m_si, arguments ); + const auto result = ( (Callable*)callable.Get() )->Run( ctx, call->m_si, arguments ); + if ( result.Get()->type == Type::T_NOTHING ) { + // function will return undefined by default + return VALUE( Undefined ); + } + return result; } default: throw gse::Exception( EC.INVALID_CALL, "Callable expected, found: " + callable.ToString(), ctx, call->m_si ); } } + case Operand::OT_LOOP_CONTROL: { + return VALUE( type::LoopControl, ( (program::LoopControl*)operand )->loop_control_type ); + } default: { THROW( "operand " + operand->ToString() + " not implemented" ); } @@ -930,6 +978,35 @@ void Interpreter::ValidateRange( context::Context* ctx, const si_t& si, const ty } } +void Interpreter::CheckBreakCondition( const gse::Value& result, bool* need_break, bool* need_clear ) const { + switch ( result.Get()->type ) { + case Type::T_NOTHING: + *need_break = false; + *need_clear = false; + return; + case Type::T_LOOPCONTROL: { + *need_clear = true; + const auto type = ( (type::LoopControl*)result.Get() )->value; + switch ( type ) { + case program::LCT_BREAK: { + *need_break = true; + return; + } + case program::LCT_CONTINUE: { + *need_break = false; + return; + } + default: + THROW( "unexpected loop control type: " + std::to_string( type ) ); + } + } + default: + // got something to return + *need_break = true; + *need_clear = false; + } +} + Interpreter::Function::Function( const Interpreter* runner, context::Context* context, diff --git a/src/gse/runner/Interpreter.h b/src/gse/runner/Interpreter.h index e974a1b3..34466730 100644 --- a/src/gse/runner/Interpreter.h +++ b/src/gse/runner/Interpreter.h @@ -65,6 +65,7 @@ CLASS( Interpreter, Runner ) const Value Deref( context::Context* ctx, const si_t& si, const Value& value ) const; void WriteByRef( context::Context* ctx, const si_t& si, const Value& ref, const Value& value ) const; void ValidateRange( context::Context* ctx, const si_t& si, const type::Array* array, const std::optional< size_t > from, const std::optional< size_t > to ) const; + void CheckBreakCondition( const gse::Value& result, bool* need_break, bool* need_clear ) const; }; diff --git a/src/gse/type/LoopControl.h b/src/gse/type/LoopControl.h new file mode 100644 index 00000000..5af4cdd0 --- /dev/null +++ b/src/gse/type/LoopControl.h @@ -0,0 +1,24 @@ +#pragma once + +#include "Type.h" + +#include "gse/program/Types.h" + +namespace gse { +namespace type { + +class LoopControl : public Type { +public: + + static const type_t GetType() { return Type::T_LOOPCONTROL; } + + LoopControl( const program::loop_control_type_t initial_value ) + : Type( GetType() ) + , value( initial_value ) { + } + + program::loop_control_type_t value; +}; + +} +} diff --git a/src/gse/type/Nothing.h b/src/gse/type/Nothing.h new file mode 100644 index 00000000..4af5d1a5 --- /dev/null +++ b/src/gse/type/Nothing.h @@ -0,0 +1,21 @@ +#pragma once + +#include "Type.h" + +namespace gse { +namespace type { + +class Nothing : public Type { +public: + + static const type_t GetType() { return Type::T_NOTHING; } + + Nothing() + : Type( GetType() ) {} + + Nothing( const Nothing& other ) + : Nothing() {} +}; + +} +} diff --git a/src/gse/type/Type.cpp b/src/gse/type/Type.cpp index 6440bdfb..5f75a8e1 100644 --- a/src/gse/type/Type.cpp +++ b/src/gse/type/Type.cpp @@ -15,6 +15,7 @@ #include "ObjectRef.h" #include "Range.h" #include "Exception.h" +#include "LoopControl.h" #include "types/Buffer.h" @@ -34,6 +35,7 @@ static const std::string s_t_arrayref = "Arrayref"; static const std::string s_t_arrayrangeref = "Arrayrangeref"; static const std::string s_t_objectref = "Objectref"; static const std::string s_t_range = "Range"; +static const std::string s_t_loopcontrol = "LoopControl"; static const std::string s_t_unknown = "Unknown"; const std::string& Type::GetTypeString( const type_t type ) { switch ( type ) { @@ -63,6 +65,8 @@ const std::string& Type::GetTypeString( const type_t type ) { return s_t_objectref; case T_RANGE: return s_t_range; + case T_LOOPCONTROL: + return s_t_loopcontrol; default: return s_t_unknown; } @@ -143,6 +147,16 @@ const std::string Type::ToString() const { : "" ) + "]"; } + case T_LOOPCONTROL: { + switch ( ( (LoopControl*)this )->value ) { + case program::LCT_BREAK: + return "break"; + case program::LCT_CONTINUE: + return "continue"; + default: + THROW( "unexpected loop control type: " + std::to_string( ( (LoopControl*)this )->value ) ); + } + } default: THROW( "unknown is not intended to be printed" ); } @@ -232,6 +246,9 @@ const std::string Type::Dump() const { : "" ) + "}"; } + case T_LOOPCONTROL: { + return "loopcontrol{" + ToString() + "}"; + } default: return "unknown{" + std::to_string( type ) + "}"; } diff --git a/src/gse/type/Type.h b/src/gse/type/Type.h index e3aab79c..583f98da 100644 --- a/src/gse/type/Type.h +++ b/src/gse/type/Type.h @@ -14,6 +14,7 @@ namespace type { class Type { public: enum type_t : uint8_t { + T_NOTHING, T_UNDEFINED, T_NULL, T_BOOL, @@ -27,6 +28,7 @@ class Type { T_ARRAYRANGEREF, T_OBJECTREF, T_RANGE, + T_LOOPCONTROL, }; static const std::string& GetTypeString( const type_t type ); From e1886e315dbc4f95ab13009c09949cd3b9ce0238 Mon Sep 17 00:00:00 2001 From: afwbkbc Date: Sat, 29 Jun 2024 16:54:30 +0300 Subject: [PATCH 13/18] fixed wrong morale-based sprites --- src/task/game/unit/UnitDef.cpp | 4 ++-- src/task/game/unit/UnitDef.h | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/task/game/unit/UnitDef.cpp b/src/task/game/unit/UnitDef.cpp index bab086b3..063aeaaf 100644 --- a/src/task/game/unit/UnitDef.cpp +++ b/src/task/game/unit/UnitDef.cpp @@ -45,7 +45,7 @@ UnitDef::UnitDef( sprite::InstancedSpriteManager* ism, const ::game::unit::Def* UnitDef::~UnitDef() { if ( m_type == ::game::unit::DT_STATIC ) { - if ( static_.render.morale_based_xshift ) { + if ( m_render.morale_based_xshift ) { if ( static_.render.morale_based_sprites ) { DELETE( static_.render.morale_based_sprites ); } @@ -67,7 +67,7 @@ sprite::Sprite* UnitDef::GetSprite( const ::game::unit::morale_t morale ) { } auto it = static_.render.morale_based_sprites->find( morale ); if ( it == static_.render.morale_based_sprites->end() ) { - const uint32_t xshift = static_.render.morale_based_xshift * ( morale - ::game::unit::MORALE_MIN ); + const uint32_t xshift = m_render.morale_based_xshift * ( morale - ::game::unit::MORALE_MIN ); it = static_.render.morale_based_sprites->insert( { morale, diff --git a/src/task/game/unit/UnitDef.h b/src/task/game/unit/UnitDef.h index 29d7806f..d85dd6ee 100644 --- a/src/task/game/unit/UnitDef.h +++ b/src/task/game/unit/UnitDef.h @@ -59,7 +59,6 @@ class UnitDef { ::game::unit::movement_t movement_per_turn; struct { bool is_sprite = false; - uint32_t morale_based_xshift = 0; types::texture::Texture* texture = nullptr; sprite::Sprite sprite = {}; morale_based_sprites_t* morale_based_sprites = nullptr; From f239b59b498c7e0692ea8b23c239a9352f47329f Mon Sep 17 00:00:00 2001 From: afwbkbc Date: Sat, 29 Jun 2024 22:25:33 +0300 Subject: [PATCH 14/18] fixed base sprites, prevent spawning adjactent bases --- GLSMAC_data/default/main.gls.js | 25 +++++++++++++++------- src/game/FrontendRequest.h | 1 + src/game/Game.cpp | 17 ++++++++------- src/game/base/Base.cpp | 10 ++++----- src/game/base/Base.h | 7 ++++-- src/game/base/BaseData.cpp | 34 ++++++++++++++++++++++++++++++ src/game/base/BaseData.h | 30 ++++++++++++++++++++++++++ src/game/base/CMakeLists.txt | 1 + src/game/bindings/Bases.cpp | 4 +++- src/game/event/SpawnBase.cpp | 14 ++++++------ src/game/event/SpawnBase.h | 6 +++--- src/game/map/tile/Tile.cpp | 14 ++++++++++++ src/gse/builtins/Math.cpp | 14 ++++++++---- src/gse/type/Object.cpp | 4 ++++ src/task/game/Game.cpp | 5 ++++- src/task/game/base/Base.cpp | 22 ++++++++++++++++--- src/task/game/base/Base.h | 5 ++++- src/task/game/base/BaseManager.cpp | 7 +++--- src/task/game/base/BaseManager.h | 4 +++- src/task/game/tile/Tile.cpp | 6 ++++++ src/task/game/tile/Tile.h | 4 ++++ 21 files changed, 187 insertions(+), 47 deletions(-) create mode 100644 src/game/base/BaseData.cpp create mode 100644 src/game/base/BaseData.h diff --git a/GLSMAC_data/default/main.gls.js b/GLSMAC_data/default/main.gls.js index 9f81766a..4439d8d9 100644 --- a/GLSMAC_data/default/main.gls.js +++ b/GLSMAC_data/default/main.gls.js @@ -64,7 +64,6 @@ units.init(); if (morale >= 6) { break; } - #print(neighbour); if (neighbour.has_fungus) { morale++; } @@ -85,14 +84,24 @@ units.init(); } } if (#game.random.get_int(0, 2) == 0) { - #game.bases.spawn( - owner, - tile, - { - // name: 'base name', + let has_adjactent_bases = false; + for (neighbour of tile.get_surrounding_tiles()) { + if (neighbour.get_base() != null) { + has_adjactent_bases = true; + break; } - ); - bases_spawned++; + } + if (!has_adjactent_bases) { + #game.bases.spawn( + owner, + tile, + { + // name: 'base name', + population: #game.random.get_int(0, 4) * #game.random.get_int(0, 4) + #game.random.get_int(1, 3), + } + ); + bases_spawned++; + } } } } diff --git a/src/game/FrontendRequest.h b/src/game/FrontendRequest.h index bee27f01..2a8f9ebb 100644 --- a/src/game/FrontendRequest.h +++ b/src/game/FrontendRequest.h @@ -153,6 +153,7 @@ class FrontendRequest { } render_coords; struct { const std::string* name; + size_t population; } base_info; } base_spawn; } data; diff --git a/src/game/Game.cpp b/src/game/Game.cpp index 9e1e8abd..337aafd6 100644 --- a/src/game/Game.cpp +++ b/src/game/Game.cpp @@ -929,14 +929,14 @@ void Game::SpawnBase( base::Base* base ) { // validate and fix name if needed (or assign if empty) std::vector< std::string > names_to_try = {}; - if ( base->m_name.empty() ) { + if ( base->m_data.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 }; + else if ( m_registered_base_names.find( base->m_data.name ) != m_registered_base_names.end() ) { + names_to_try = { base->m_data.name }; } if ( !names_to_try.empty() ) { size_t cycle = 0; @@ -944,19 +944,19 @@ void Game::SpawnBase( base::Base* base ) { while ( !found ) { cycle++; for ( const auto& name_to_try : names_to_try ) { - base->m_name = cycle == 1 + base->m_data.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() ) { + if ( m_registered_base_names.find( base->m_data.name ) == m_registered_base_names.end() ) { found = true; break; } } } } - m_registered_base_names.insert( base->m_name ); + m_registered_base_names.insert( base->m_data.name ); - Log( "Spawning base #" + std::to_string( base->m_id ) + " ( " + base->m_name + " ) at " + base->GetTile()->ToString() ); + Log( "Spawning base #" + std::to_string( base->m_id ) + " ( " + base->m_data.name + ", " + std::to_string( base->m_data.population ) + " ) 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 ); @@ -2129,7 +2129,8 @@ void Game::PushBaseUpdates() { c.y, c.z }; - NEW( fr.data.base_spawn.base_info.name, std::string, base->m_name ); + NEW( fr.data.base_spawn.base_info.name, std::string, base->m_data.name ); + fr.data.base_spawn.base_info.population = base->m_data.population; AddFrontendRequest( fr ); } if ( bu.ops & BUO_REFRESH ) { diff --git a/src/game/base/Base.cpp b/src/game/base/Base.cpp index 29f8d227..8de0320e 100644 --- a/src/game/base/Base.cpp +++ b/src/game/base/Base.cpp @@ -26,13 +26,13 @@ Base::Base( const size_t id, slot::Slot* owner, map::tile::Tile* tile, - const std::string& name + const BaseData& data ) : MapObject( game->GetMap(), tile ) , m_game( game ) , m_id( id ) , m_owner( owner ) - , m_name( name ) { + , m_data( data ) { if ( next_id <= id ) { next_id = id + 1; } @@ -47,7 +47,7 @@ const types::Buffer Base::Serialize( const Base* base ) { buf.WriteInt( base->m_owner->GetIndex() ); buf.WriteInt( base->m_tile->coord.x ); buf.WriteInt( base->m_tile->coord.y ); - buf.WriteString( base->m_name ); + base->m_data.Serialize( buf ); return buf; } @@ -57,8 +57,8 @@ Base* Base::Unserialize( types::Buffer& buf, Game* game ) { const auto pos_x = buf.ReadInt(); const auto pos_y = buf.ReadInt(); auto* tile = game ? game->GetMap()->GetTile( pos_x, pos_y ) : nullptr; - const auto name = buf.ReadString(); - return new Base( game, id, slot, tile, name ); + const auto data = BaseData( buf ); + return new Base( game, id, slot, tile, data ); } WRAPIMPL_DYNAMIC_GETTERS( Base, CLASS_BASE ) diff --git a/src/game/base/Base.h b/src/game/base/Base.h index 333dcd23..ad03a1f9 100644 --- a/src/game/base/Base.h +++ b/src/game/base/Base.h @@ -2,6 +2,8 @@ #include +#include "BaseData.h" + #include "gse/Wrappable.h" #include "game/MapObject.h" @@ -30,13 +32,14 @@ class Base : public gse::Wrappable, public MapObject { const size_t id, slot::Slot* owner, map::tile::Tile* tile, - const std::string& name + const BaseData& data ); virtual ~Base() = default; const size_t m_id; slot::Slot* m_owner; - std::string m_name; + + BaseData m_data; static const types::Buffer Serialize( const Base* base ); static Base* Unserialize( types::Buffer& buf, Game* game ); diff --git a/src/game/base/BaseData.cpp b/src/game/base/BaseData.cpp new file mode 100644 index 00000000..0f900cb9 --- /dev/null +++ b/src/game/base/BaseData.cpp @@ -0,0 +1,34 @@ +#include "BaseData.h" + +namespace game { +namespace base { + +BaseData::BaseData( const std::string& name, const size_t population ) + : name( name ) + , population( population ) { + // +} + +BaseData::BaseData( types::Buffer& buf ) { + Unserialize( buf ); +} + +void BaseData::Serialize( types::Buffer& buf ) const { + buf.WriteString( name ); + buf.WriteInt( population ); +} + +void BaseData::Unserialize( types::Buffer& buf ) { + name = buf.ReadString(); + population = buf.ReadInt(); +} + +TS_BEGIN( BaseData ) + TS_FUNC_BEGIN( "BaseData" ) + + TS_OBJ_PROP_STR( "name", name ) + + TS_OBJ_PROP_NUM( "population", population ) + + TS_FUNC_END() +TS_END() + +} +} diff --git a/src/game/base/BaseData.h b/src/game/base/BaseData.h new file mode 100644 index 00000000..93931ae7 --- /dev/null +++ b/src/game/base/BaseData.h @@ -0,0 +1,30 @@ +#pragma once + +#include "gse/Wrappable.h" +#include "gse/Value.h" + +#include "types/Buffer.h" + +namespace game { +namespace base { + +class BaseData : public gse::Wrappable { +public: + + BaseData( const std::string& name, const size_t population ); + BaseData( types::Buffer& buf ); + + std::string name; + size_t population; + + void Serialize( types::Buffer& buf ) const; + void Unserialize( types::Buffer& buf ); + + const std::string ToString( const std::string& prefix = "" ) const; + + WRAPDEFS_DYNAMIC( BaseData ); + +}; + +} +} diff --git a/src/game/base/CMakeLists.txt b/src/game/base/CMakeLists.txt index c4dcf676..74476b9c 100644 --- a/src/game/base/CMakeLists.txt +++ b/src/game/base/CMakeLists.txt @@ -1,5 +1,6 @@ SET( SRC ${SRC} ${PWD}/Base.cpp + ${PWD}/BaseData.cpp PARENT_SCOPE ) diff --git a/src/game/bindings/Bases.cpp b/src/game/bindings/Bases.cpp index ced36fd2..f81f059e 100644 --- a/src/game/bindings/Bases.cpp +++ b/src/game/bindings/Bases.cpp @@ -6,6 +6,7 @@ #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 "game/Game.h" #include "game/slot/Slot.h" @@ -26,6 +27,7 @@ BINDING_IMPL( bases ) { N_GETVALUE( info, 2, Object ); N_GETPROP_OPT( std::string, name, info, "name", String, "" ); + N_GETPROP_OPT( size_t, population, info, "population", Int, 1 ); auto* game = GAME; game->AddEvent( new event::SpawnBase( @@ -33,7 +35,7 @@ BINDING_IMPL( bases ) { owner->GetIndex(), tile->coord.x, tile->coord.y, - name + base::BaseData( name, population ) ) ); return VALUE( gse::type::Undefined ); }) diff --git a/src/game/event/SpawnBase.cpp b/src/game/event/SpawnBase.cpp index 965737e6..3453aadf 100644 --- a/src/game/event/SpawnBase.cpp +++ b/src/game/event/SpawnBase.cpp @@ -17,13 +17,13 @@ SpawnBase::SpawnBase( const size_t owner_slot, const size_t pos_x, const size_t pos_y, - const std::string& name + const base::BaseData& data ) : Event( initiator_slot, ET_BASE_SPAWN ) , m_owner_slot( owner_slot ) , m_pos_x( pos_x ) , m_pos_y( pos_y ) - , m_name( name ) { + , m_data( data ) { // } @@ -44,7 +44,7 @@ const gse::Value SpawnBase::Apply( game::Game* game ) const { base::Base::GetNextId(), &owner, tile, - m_name + m_data ); game->SpawnBase( base ); return base->Wrap(); @@ -55,7 +55,7 @@ TS_BEGIN( SpawnBase ) TS_FUNC_ARG_NUM( "owner_slot", m_owner_slot ) + TS_FUNC_ARG_NUM( "pos_x", m_pos_x ) + TS_FUNC_ARG_NUM( "pos_y", m_pos_y ) + - TS_FUNC_ARG_STR( "name", m_name ) + + TS_FUNC_ARG_STR( "data", m_data.ToString( TS_PREFIX_NEXT ) ) + TS_FUNC_END() TS_END() @@ -63,15 +63,15 @@ void SpawnBase::Serialize( types::Buffer& buf, const SpawnBase* event ) { buf.WriteInt( event->m_owner_slot ); buf.WriteInt( event->m_pos_x ); buf.WriteInt( event->m_pos_y ); - buf.WriteString( event->m_name ); + event->m_data.Serialize( buf ); } SpawnBase* SpawnBase::Unserialize( types::Buffer& buf, const size_t initiator_slot ) { const auto owner_slot = buf.ReadInt(); const auto pos_x = buf.ReadInt(); const auto pos_y = buf.ReadInt(); - const auto name = buf.ReadString(); - return new SpawnBase( initiator_slot, owner_slot, pos_x, pos_y, name ); + const auto data = base::BaseData( buf ); + return new SpawnBase( initiator_slot, owner_slot, pos_x, pos_y, data ); } } diff --git a/src/game/event/SpawnBase.h b/src/game/event/SpawnBase.h index c034b13d..8a737e9a 100644 --- a/src/game/event/SpawnBase.h +++ b/src/game/event/SpawnBase.h @@ -4,7 +4,7 @@ #include "Event.h" -#include "game/unit/Types.h" +#include "game/base/BaseData.h" namespace game { namespace event { @@ -16,7 +16,7 @@ class SpawnBase : public Event { const size_t owner_slot, const size_t pos_x, const size_t pos_y, - const std::string& name + const base::BaseData& data ); const std::string* Validate( Game* game ) const override; @@ -33,7 +33,7 @@ class SpawnBase : public Event { const size_t m_owner_slot; const size_t m_pos_x; const size_t m_pos_y; - const std::string m_name; + const base::BaseData m_data; }; } diff --git a/src/game/map/tile/Tile.cpp b/src/game/map/tile/Tile.cpp index a2dd36be..e41006c8 100644 --- a/src/game/map/tile/Tile.cpp +++ b/src/game/map/tile/Tile.cpp @@ -4,11 +4,13 @@ #include "gse/type/Array.h" #include "gse/type/Int.h" #include "gse/type/Bool.h" +#include "gse/type/Null.h" #include "gse/type/Undefined.h" #include "gse/callable/Native.h" #include "gse/Exception.h" #include "game/unit/Unit.h" +#include "game/base/Base.h" namespace game { namespace map { @@ -219,6 +221,18 @@ WRAPIMPL_BEGIN( Tile, CLASS_TILE ) return VALUE( gse::type::Array, result ); } ) }, + { + "get_base", + NATIVE_CALL( this ) { + N_EXPECT_ARGS( 0 ); + if ( base ) { + return base->Wrap(); + } + else { + return VALUE( gse::type::Null ); + } + } ) + }, }; WRAPIMPL_END_PTR( Tile ) diff --git a/src/gse/builtins/Math.cpp b/src/gse/builtins/Math.cpp index e2344cde..782875bd 100644 --- a/src/gse/builtins/Math.cpp +++ b/src/gse/builtins/Math.cpp @@ -42,11 +42,17 @@ void Math::AddToContext( context::Context* ctx ) { AB( "max", std::max, std::fmax ) #undef AB - ctx->CreateBuiltin( "round", NATIVE_CALL() { - N_EXPECT_ARGS( 1 ); - N_GETVALUE( v, 0, Float ); - return VALUE( type::Int, std::round( v ) ); +#define F( _func, _in_type, _out_type ) \ + ctx->CreateBuiltin( #_func, NATIVE_CALL() { \ + N_EXPECT_ARGS( 1 ); \ + N_GETVALUE( v, 0, _in_type ); \ + return VALUE( type::_out_type, std::_func( v ) ); \ } ) ); + F( floor, Float, Int ) + F( round, Float, Int ) + F( ceil, Float, Int ) + F( sqrt, Float, Float ) +#undef F } diff --git a/src/gse/type/Object.cpp b/src/gse/type/Object.cpp index e146d946..343303a4 100644 --- a/src/gse/type/Object.cpp +++ b/src/gse/type/Object.cpp @@ -47,6 +47,10 @@ static const std::unordered_map< Object::object_class_t, std::string > s_object_ Object::CLASS_UNIT, "#unit" }, + { + Object::CLASS_BASE, + "#base" + }, }; 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/task/game/Game.cpp b/src/task/game/Game.cpp index bf107ef3..669c6023 100644 --- a/src/task/game/Game.cpp +++ b/src/task/game/Game.cpp @@ -1080,7 +1080,10 @@ void Game::ProcessRequest( const ::game::FrontendRequest* request ) { rc.y, rc.z }, - *d.base_info.name + { + *d.base_info.name, + d.base_info.population + } ); break; } diff --git a/src/task/game/base/Base.cpp b/src/task/game/base/Base.cpp index cb96bde1..2cee9567 100644 --- a/src/task/game/base/Base.cpp +++ b/src/task/game/base/Base.cpp @@ -18,13 +18,21 @@ namespace task { namespace game { namespace base { +static const std::vector< uint8_t > s_base_render_population_thresholds = { + 1, + 4, + 8, + 15, +}; + Base::Base( const size_t id, Slot* slot, tile::Tile* tile, const bool is_owned, const types::Vec3& render_coords, - text::InstancedText* render_name_sprite + text::InstancedText* render_name_sprite, + size_t population ) : TileObject( tile ) , m_id( id ) @@ -37,7 +45,8 @@ Base::Base( 0, } ) - , m_is_owned( is_owned ) { + , m_is_owned( is_owned ) + , m_population( population ) { m_render_data.base = GetMeshTex( GetSprite()->instanced_sprite ); m_tile->SetBase( this ); } @@ -61,7 +70,14 @@ tile::Tile* Base::GetTile() const { } sprite::Sprite* Base::GetSprite() const { - return m_faction->GetBaseSprite( false, 1, 0 ); + uint8_t size = 0; + for ( uint8_t i = 0 ; i < s_base_render_population_thresholds.size() ; i++ ) { + if ( s_base_render_population_thresholds.at( i ) > m_population ) { + break; + } + size = i; + } + return m_faction->GetBaseSprite( m_tile->IsWater(), size, 0 ); // TODO: perimeter } void Base::Show() { diff --git a/src/task/game/base/Base.h b/src/task/game/base/Base.h index f2fe2366..bafa97f2 100644 --- a/src/task/game/base/Base.h +++ b/src/task/game/base/Base.h @@ -55,7 +55,8 @@ class Base : public TileObject { tile::Tile* tile, const bool is_owned, const types::Vec3& render_coords, - text::InstancedText* render_name_sprite + text::InstancedText* render_name_sprite, + size_t population ); ~Base(); @@ -93,6 +94,8 @@ class Base : public TileObject { size_t instance_id = 0; } m_render; + size_t m_population = 0; + const bool m_is_owned = false; render_data_t m_render_data = {}; diff --git a/src/task/game/base/BaseManager.cpp b/src/task/game/base/BaseManager.cpp index 6a844996..5f705f70 100644 --- a/src/task/game/base/BaseManager.cpp +++ b/src/task/game/base/BaseManager.cpp @@ -40,7 +40,7 @@ void BaseManager::SpawnBase( const size_t slot_index, const types::Vec2< size_t >& tile_coords, const types::Vec3& render_coords, - const std::string& name + const ::game::base::BaseData& data ) { ASSERT( m_bases.find( base_id ) == m_bases.end(), "base id already exists" ); @@ -59,10 +59,11 @@ void BaseManager::SpawnBase( slot_index == m_game->GetMySlotIndex(), render_coords, m_game->GetITM()->CreateInstancedText( - name, + data.name, m_name_font, faction->m_colors.text - ) + ), + data.population ) } ); diff --git a/src/task/game/base/BaseManager.h b/src/task/game/base/BaseManager.h index b584ec3e..f876dd11 100644 --- a/src/task/game/base/BaseManager.h +++ b/src/task/game/base/BaseManager.h @@ -5,6 +5,8 @@ #include "common/Common.h" +#include "game/base/BaseData.h" + #include "game/unit/Types.h" #include "types/Vec2.h" @@ -54,7 +56,7 @@ CLASS( BaseManager, common::Class ) const size_t slot_index, const ::types::Vec2< size_t >& tile_coords, const ::types::Vec3& render_coords, - const std::string& name + const ::game::base::BaseData& data ); // TODO void DespawnBase( const size_t base_id ); diff --git a/src/task/game/tile/Tile.cpp b/src/task/game/tile/Tile.cpp index 17f4945e..8cb60901 100644 --- a/src/task/game/tile/Tile.cpp +++ b/src/task/game/tile/Tile.cpp @@ -35,6 +35,10 @@ Tile::Tile( const types::Vec2< size_t >& coords ) } +const bool Tile::IsWater() const { + return m_is_water; +} + const types::Vec2< size_t >& Tile::GetCoords() const { return m_coords; } @@ -210,6 +214,8 @@ const Tile::render_data_t& Tile::GetRenderData() const { void Tile::Update( const ::game::map::tile::Tile& tile, const ::game::map::tile::TileState& ts ) { + m_is_water = tile.is_water_tile; + ::game::map::tile::tile_layer_type_t lt = ( tile.is_water_tile ? ::game::map::tile::LAYER_WATER : ::game::map::tile::LAYER_LAND diff --git a/src/task/game/tile/Tile.h b/src/task/game/tile/Tile.h index da665789..1c5aa582 100644 --- a/src/task/game/tile/Tile.h +++ b/src/task/game/tile/Tile.h @@ -38,6 +38,8 @@ class Tile { Tile( const types::Vec2< size_t >& coords ); + const bool IsWater() const; + const types::Vec2< size_t >& GetCoords() const; void AddUnit( unit::Unit* unit ); @@ -86,6 +88,8 @@ class Tile { std::vector< unit::Unit* > m_ordered_units = {}; bool m_is_units_reorder_needed = true; + bool m_is_water = false; + base::Base* m_base = nullptr; render_data_t m_render_data = {}; From 9113e9ae9d9540ec3a8e9bf2366146b1b9aec851 Mon Sep 17 00:00:00 2001 From: afwbkbc Date: Sun, 30 Jun 2024 20:45:23 +0300 Subject: [PATCH 15/18] improved bases text shadows a bit, imported colors from faction pcx files --- GLSMAC_data/default/factions/angels.gls.js | 5 +- GLSMAC_data/default/factions/believers.gls.js | 5 +- .../default/factions/caretakers.gls.js | 5 +- .../default/factions/consciousness.gls.js | 5 +- GLSMAC_data/default/factions/drones.gls.js | 5 +- GLSMAC_data/default/factions/gaians.gls.js | 5 +- GLSMAC_data/default/factions/hive.gls.js | 5 +- .../default/factions/morganites.gls.js | 5 +- .../default/factions/peacekeepers.gls.js | 5 +- GLSMAC_data/default/factions/pirates.gls.js | 5 +- .../default/factions/planetcult.gls.js | 5 +- GLSMAC_data/default/factions/spartans.gls.js | 5 +- .../default/factions/university.gls.js | 5 +- GLSMAC_data/default/factions/usurpers.gls.js | 5 +- src/game/bindings/Factions.cpp | 27 +++++- src/game/rules/Faction.cpp | 2 + src/game/rules/Faction.h | 1 + src/task/game/base/BaseManager.cpp | 3 +- src/task/game/faction/Faction.cpp | 1 + src/task/game/faction/Faction.h | 1 + src/task/game/text/InstancedFont.cpp | 86 +++++++++++++------ src/task/game/text/InstancedFont.h | 2 +- src/task/game/text/InstancedText.cpp | 4 +- src/task/game/text/InstancedText.h | 2 +- src/task/game/text/InstancedTextManager.cpp | 7 +- src/task/game/text/InstancedTextManager.h | 3 +- src/types/texture/Texture.cpp | 14 ++- src/types/texture/Texture.h | 2 +- 28 files changed, 131 insertions(+), 94 deletions(-) diff --git a/GLSMAC_data/default/factions/angels.gls.js b/GLSMAC_data/default/factions/angels.gls.js index 1f739f4a..90f4c8d2 100644 --- a/GLSMAC_data/default/factions/angels.gls.js +++ b/GLSMAC_data/default/factions/angels.gls.js @@ -1,9 +1,6 @@ return { name: 'Angels', - colors: { - text: #to_color(103, 91, 181), - border: #to_color(103, 91, 181), - }, + colors: #game.factions.import_colors('angels.pcx'), bases: { render: { type: 'sprite_grid', diff --git a/GLSMAC_data/default/factions/believers.gls.js b/GLSMAC_data/default/factions/believers.gls.js index 64f76da5..7383f124 100644 --- a/GLSMAC_data/default/factions/believers.gls.js +++ b/GLSMAC_data/default/factions/believers.gls.js @@ -1,9 +1,6 @@ return { name: 'Believers', - colors: { - text: #to_color(224, 156, 28), - border: #to_color(236, 92, 0), - }, + colors: #game.factions.import_colors('believe.pcx'), bases: { render: { type: 'sprite_grid', diff --git a/GLSMAC_data/default/factions/caretakers.gls.js b/GLSMAC_data/default/factions/caretakers.gls.js index ac73b3c8..021223a8 100644 --- a/GLSMAC_data/default/factions/caretakers.gls.js +++ b/GLSMAC_data/default/factions/caretakers.gls.js @@ -1,9 +1,6 @@ return { name: 'Caretakers', - colors: { - text: #to_color(116, 156, 56), - border: #to_color(116, 156, 56), - }, + colors: #game.factions.import_colors('caretake.pcx'), bases: { render: { type: 'sprite_grid', diff --git a/GLSMAC_data/default/factions/consciousness.gls.js b/GLSMAC_data/default/factions/consciousness.gls.js index 725786f1..4a8bdfc8 100644 --- a/GLSMAC_data/default/factions/consciousness.gls.js +++ b/GLSMAC_data/default/factions/consciousness.gls.js @@ -1,9 +1,6 @@ return { name: 'Consciousness', - colors: { - text: #to_color(44, 128, 104), - border: #to_color(44, 128, 104), - }, + colors: #game.factions.import_colors('cyborg.pcx'), bases: { render: { type: 'sprite_grid', diff --git a/GLSMAC_data/default/factions/drones.gls.js b/GLSMAC_data/default/factions/drones.gls.js index be31bedc..bf70c73a 100644 --- a/GLSMAC_data/default/factions/drones.gls.js +++ b/GLSMAC_data/default/factions/drones.gls.js @@ -1,9 +1,6 @@ return { name: 'Drones', - colors: { - text: #to_color(173, 196, 192), - border: #to_color(136, 12, 12), - }, + colors: #game.factions.import_colors('drone.pcx'), bases: { render: { type: 'sprite_grid', diff --git a/GLSMAC_data/default/factions/gaians.gls.js b/GLSMAC_data/default/factions/gaians.gls.js index d07ddeee..1b944258 100644 --- a/GLSMAC_data/default/factions/gaians.gls.js +++ b/GLSMAC_data/default/factions/gaians.gls.js @@ -1,9 +1,6 @@ const faction = { name: 'Gaians', - colors: { - text: #to_color(16, 228, 0), - border: #to_color(0, 252, 0), - }, + colors: #game.factions.import_colors('gaians.pcx'), bases: { render: { type: 'sprite_grid', diff --git a/GLSMAC_data/default/factions/hive.gls.js b/GLSMAC_data/default/factions/hive.gls.js index 3d4f2444..b05b73b9 100644 --- a/GLSMAC_data/default/factions/hive.gls.js +++ b/GLSMAC_data/default/factions/hive.gls.js @@ -1,9 +1,6 @@ return { name: 'Hive', - colors: { - text: #to_color(0, 97, 255), - border: #to_color(0, 0, 255), - }, + colors: #game.factions.import_colors('hive.pcx'), bases: { render: { type: 'sprite_grid', diff --git a/GLSMAC_data/default/factions/morganites.gls.js b/GLSMAC_data/default/factions/morganites.gls.js index 6cb4a371..4618b49f 100644 --- a/GLSMAC_data/default/factions/morganites.gls.js +++ b/GLSMAC_data/default/factions/morganites.gls.js @@ -1,9 +1,6 @@ return { name: 'Morganites', - colors: { - text: #to_color(255, 255, 0), - border: #to_color(255, 255, 0), - }, + colors: #game.factions.import_colors('morgan.pcx'), bases: { render: { type: 'sprite_grid', diff --git a/GLSMAC_data/default/factions/peacekeepers.gls.js b/GLSMAC_data/default/factions/peacekeepers.gls.js index 78a8ab9c..486f76e1 100644 --- a/GLSMAC_data/default/factions/peacekeepers.gls.js +++ b/GLSMAC_data/default/factions/peacekeepers.gls.js @@ -1,9 +1,6 @@ return { name: 'Peacekeepers', - colors: { - text: #to_color(164, 176, 232), - border: #to_color(164, 176, 232), - }, + colors: #game.factions.import_colors('peace.pcx'), bases: { render: { type: 'sprite_grid', diff --git a/GLSMAC_data/default/factions/pirates.gls.js b/GLSMAC_data/default/factions/pirates.gls.js index e08450cd..e3c92643 100644 --- a/GLSMAC_data/default/factions/pirates.gls.js +++ b/GLSMAC_data/default/factions/pirates.gls.js @@ -1,9 +1,6 @@ return { name: 'Pirates', - colors: { - text: #to_color(0, 255, 255), - border: #to_color(0, 255, 255), - }, + colors: #game.factions.import_colors('pirates.pcx'), bases: { render: { type: 'sprite_grid', diff --git a/GLSMAC_data/default/factions/planetcult.gls.js b/GLSMAC_data/default/factions/planetcult.gls.js index 2eeb5d5e..874201c0 100644 --- a/GLSMAC_data/default/factions/planetcult.gls.js +++ b/GLSMAC_data/default/factions/planetcult.gls.js @@ -1,9 +1,6 @@ return { name: 'Planet Cult', - colors: { - text: #to_color(232, 84, 84), - border: #to_color(232, 84, 84), - }, + colors: #game.factions.import_colors('fungboy.pcx'), bases: { render: { type: 'sprite_grid', diff --git a/GLSMAC_data/default/factions/spartans.gls.js b/GLSMAC_data/default/factions/spartans.gls.js index 53902f69..df015be8 100644 --- a/GLSMAC_data/default/factions/spartans.gls.js +++ b/GLSMAC_data/default/factions/spartans.gls.js @@ -1,9 +1,6 @@ return { name: 'Spartans', - colors: { - text: #to_color(136, 166, 166), - border: #to_color(0, 0, 0), - }, + colors: #game.factions.import_colors('spartans.pcx'), bases: { render: { type: 'sprite_grid', diff --git a/GLSMAC_data/default/factions/university.gls.js b/GLSMAC_data/default/factions/university.gls.js index 98217cd8..55453a74 100644 --- a/GLSMAC_data/default/factions/university.gls.js +++ b/GLSMAC_data/default/factions/university.gls.js @@ -1,9 +1,6 @@ return { name: 'University', - colors: { - text: #to_color(216, 224, 244), - border: #to_color(255, 255, 255), - }, + colors: #game.factions.import_colors('univ.pcx'), bases: { render: { type: 'sprite_grid', diff --git a/GLSMAC_data/default/factions/usurpers.gls.js b/GLSMAC_data/default/factions/usurpers.gls.js index 56dd55e2..bddd7574 100644 --- a/GLSMAC_data/default/factions/usurpers.gls.js +++ b/GLSMAC_data/default/factions/usurpers.gls.js @@ -1,9 +1,6 @@ return { name: 'Usurpers', - colors: { - text: #to_color(212, 208, 116), - border: #to_color(212, 208, 116), - }, + colors: #game.factions.import_colors('usurper.pcx'), bases: { render: { type: 'sprite_grid', diff --git a/src/game/bindings/Factions.cpp b/src/game/bindings/Factions.cpp index b36b7399..b2419616 100644 --- a/src/game/bindings/Factions.cpp +++ b/src/game/bindings/Factions.cpp @@ -14,6 +14,8 @@ #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 "types/Color.h" @@ -47,6 +49,24 @@ BINDING_IMPL( factions ) { 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 ); + } ) + }, { "define", NATIVE_CALL( &factions, &factions_order ) { @@ -62,8 +82,13 @@ BINDING_IMPL( factions ) { N_GETPROP( colors, faction_def, "colors", Object ); N_GETPROP_UNWRAP( colors_text, colors, "text", types::Color ); + N_GETPROP_UNWRAP( colors_text_shadow, colors, "text", types::Color ); N_GETPROP_UNWRAP( colors_border, colors, "border", types::Color ); - faction.m_colors = { colors_text, colors_border }; + faction.m_colors = { + colors_text, + colors_text_shadow, + colors_border + }; N_GETPROP_OPT_BOOL( is_naval, faction_def, "is_naval") if ( is_naval ) { diff --git a/src/game/rules/Faction.cpp b/src/game/rules/Faction.cpp index 65a8d45a..7b876b9f 100644 --- a/src/game/rules/Faction.cpp +++ b/src/game/rules/Faction.cpp @@ -26,6 +26,7 @@ const types::Buffer Faction::Serialize() const { buf.WriteString( m_name ); buf.WriteColor( m_colors.text ); + buf.WriteColor( m_colors.text_shadow ); buf.WriteColor( m_colors.border ); buf.WriteString( m_bases_render.file ); @@ -48,6 +49,7 @@ void Faction::Unserialize( types::Buffer buf ) { m_name = buf.ReadString(); m_colors.text = buf.ReadColor(); + m_colors.text_shadow = buf.ReadColor(); m_colors.border = buf.ReadColor(); m_bases_render.file = buf.ReadString(); diff --git a/src/game/rules/Faction.h b/src/game/rules/Faction.h index 372a44e2..dfdea4d4 100644 --- a/src/game/rules/Faction.h +++ b/src/game/rules/Faction.h @@ -28,6 +28,7 @@ CLASS2( Faction, types::Serializable, gse::Wrappable ) struct { types::Color text = {}; + types::Color text_shadow = {}; types::Color border = {}; } m_colors = {}; diff --git a/src/task/game/base/BaseManager.cpp b/src/task/game/base/BaseManager.cpp index 5f705f70..103b495b 100644 --- a/src/task/game/base/BaseManager.cpp +++ b/src/task/game/base/BaseManager.cpp @@ -61,7 +61,8 @@ void BaseManager::SpawnBase( m_game->GetITM()->CreateInstancedText( data.name, m_name_font, - faction->m_colors.text + faction->m_colors.text, + faction->m_colors.text_shadow ), data.population ) diff --git a/src/task/game/faction/Faction.cpp b/src/task/game/faction/Faction.cpp index 9dfb0425..819de039 100644 --- a/src/task/game/faction/Faction.cpp +++ b/src/task/game/faction/Faction.cpp @@ -17,6 +17,7 @@ Faction::Faction( const ::game::rules::Faction* def, sprite::InstancedSpriteMana , m_colors( { def->m_colors.text, + def->m_colors.text_shadow, def->m_colors.border } ) diff --git a/src/task/game/faction/Faction.h b/src/task/game/faction/Faction.h index 9f3c6583..d7b2cb45 100644 --- a/src/task/game/faction/Faction.h +++ b/src/task/game/faction/Faction.h @@ -39,6 +39,7 @@ class Faction { struct { const types::Color text = {}; + const types::Color text_shadow = {}; const types::Color border = {}; } m_colors = {}; diff --git a/src/task/game/text/InstancedFont.cpp b/src/task/game/text/InstancedFont.cpp index 34a9930e..f92a633c 100644 --- a/src/task/game/text/InstancedFont.cpp +++ b/src/task/game/text/InstancedFont.cpp @@ -11,18 +11,13 @@ namespace game { namespace text { static const types::Vec2< float > s_font_scale = { - 0.004f, - 0.005f, + 0.0045f, + 0.006f, }; -static const types::Vec2< uint8_t > s_shadow_offsets = { - 0, - 0 -}; - -static const types::Vec2< uint8_t > s_normal_offsets = { - 1, - 1 +const std::vector< float > s_shadow_alpha_levels = { + 1.0f, + 0.6f, }; InstancedFont::InstancedFont( sprite::InstancedSpriteManager* ism, const types::Font* font ) @@ -32,22 +27,23 @@ InstancedFont::InstancedFont( sprite::InstancedSpriteManager* ism, const types:: // load font into texture - const uint32_t sym_offset = 1; // to keep antialiasing working + const uint8_t sym_offset = 2; // to keep antialiasing working + const uint8_t shadow_offset = s_shadow_alpha_levels.size(); - unsigned int w = sym_offset; + unsigned int w = sym_offset + shadow_offset; unsigned int h = 0; for ( uint8_t i = 32 ; i < 128 ; i++ ) { const auto& sym = m_font->m_symbols[ i ]; w += sym.width + sym_offset; - h = std::max( h, sym.top + sym.height + 1 ); + h = std::max( h, sym.top + sym.height + sym_offset ); } NEW( m_base_texture, types::texture::Texture, "InstancedFont_" + m_name + "_BASE", - w + std::max( s_shadow_offsets.x, s_normal_offsets.x ), - sym_offset + h + sym_offset + std::max( s_shadow_offsets.y, s_normal_offsets.y ) + w + sym_offset + shadow_offset, + h + ( sym_offset + shadow_offset ) * 2 ); const auto f_paint_base_texture = [ this ]( const types::Vec2< uint8_t >& offsets, const types::Color& multiplier ) -> void { unsigned int sym_x = sym_offset; @@ -76,16 +72,58 @@ InstancedFont::InstancedFont( sprite::InstancedSpriteManager* ism, const types:: sym_x += sym.width + sym_offset; } }; - f_paint_base_texture( - s_shadow_offsets, types::Color{ + for ( uint8_t i = 0 ; i < shadow_offset ; i++ ) { + const auto& c = types::Color{ 0.0f, 0.0f, 0.0f, - 1.0f + s_shadow_alpha_levels.at( i ) + }; + const uint8_t before = shadow_offset - i - 1; + const uint8_t after = shadow_offset + i + 1; + std::vector< types::Vec2< uint8_t > > offsets = { + { + before, + shadow_offset, + }, + { + shadow_offset, + before, + }, + { + after, + shadow_offset, + }, + { + shadow_offset, + after, + }, + { + before, + before, + }, + { + after, + before, + }, + { + after, + after, + }, + { + before, + after, + }, + }; + for ( const auto& o : offsets ) { + f_paint_base_texture( o, c ); } - ); + } f_paint_base_texture( - s_normal_offsets, types::Color{ + { + shadow_offset, + shadow_offset, + }, types::Color{ 1.0f, 1.0f, 1.0f, @@ -105,8 +143,8 @@ InstancedFont::InstancedFont( sprite::InstancedSpriteManager* ism, const types:: sym_offset }, { - sym.width + s_normal_offsets.x, - sym.height + s_normal_offsets.y + sym.width + ( sym_offset + shadow_offset ) - 1, + sym.height + ( sym_offset + shadow_offset ) - 1 }, { sym_x,// + sym.width / 2, @@ -142,7 +180,7 @@ const std::string& InstancedFont::GetFontName() const { return m_name; } -const std::vector< sprite::InstancedSprite* > InstancedFont::GetSymbolSprites( const std::string& text, const types::Color& color ) { +const std::vector< sprite::InstancedSprite* > InstancedFont::GetSymbolSprites( const std::string& text, const types::Color& color, const types::Color& shadow_color ) { std::vector< sprite::InstancedSprite* > sprites = {}; const auto texture_key = color.GetRGBA(); auto texture_it = m_color_textures.find( texture_key ); @@ -154,7 +192,7 @@ const std::vector< sprite::InstancedSprite* > InstancedFont::GetSymbolSprites( c m_base_texture->m_width, m_base_texture->m_height ); - texture->ColorizeFrom( m_base_texture, color ); + texture->ColorizeFrom( m_base_texture, color, shadow_color ); texture_it = m_color_textures.insert( { texture_key, diff --git a/src/task/game/text/InstancedFont.h b/src/task/game/text/InstancedFont.h index fee8e66e..58e3abbc 100644 --- a/src/task/game/text/InstancedFont.h +++ b/src/task/game/text/InstancedFont.h @@ -32,7 +32,7 @@ class InstancedFont { ~InstancedFont(); const std::string& GetFontName() const; - const std::vector< sprite::InstancedSprite* > GetSymbolSprites( const std::string& text, const types::Color& color ); + const std::vector< sprite::InstancedSprite* > GetSymbolSprites( const std::string& text, const types::Color& color, const types::Color& shadow_color ); const std::vector< types::Vec2< float > > GetSymbolOffsets( const std::string& text ) const; private: diff --git a/src/task/game/text/InstancedText.cpp b/src/task/game/text/InstancedText.cpp index bdc5fef6..345d02d6 100644 --- a/src/task/game/text/InstancedText.cpp +++ b/src/task/game/text/InstancedText.cpp @@ -8,9 +8,9 @@ namespace task { namespace game { namespace text { -InstancedText::InstancedText( const std::string& text, InstancedFont* font, const types::Color& color ) +InstancedText::InstancedText( const std::string& text, InstancedFont* font, const types::Color& color, const types::Color& shadow_color ) : m_text( text ) { - m_text_sprites = font->GetSymbolSprites( m_text, color ); + m_text_sprites = font->GetSymbolSprites( m_text, color, shadow_color ); m_offsets = font->GetSymbolOffsets( m_text ); m_instance_ids.reserve( m_text_sprites.size() ); } diff --git a/src/task/game/text/InstancedText.h b/src/task/game/text/InstancedText.h index c373b2d3..d12d8271 100644 --- a/src/task/game/text/InstancedText.h +++ b/src/task/game/text/InstancedText.h @@ -20,7 +20,7 @@ class InstancedFont; class InstancedText { public: - InstancedText( const std::string& text, InstancedFont* font, const types::Color& color ); + InstancedText( const std::string& text, InstancedFont* font, const types::Color& color, const types::Color& shadow_color ); ~InstancedText(); void ShowAt( const types::Vec3& coords ); diff --git a/src/task/game/text/InstancedTextManager.cpp b/src/task/game/text/InstancedTextManager.cpp index 4ea85203..ef8226c3 100644 --- a/src/task/game/text/InstancedTextManager.cpp +++ b/src/task/game/text/InstancedTextManager.cpp @@ -39,10 +39,11 @@ InstancedFont* InstancedTextManager::GetInstancedFont( const types::Font* font ) InstancedText* InstancedTextManager::CreateInstancedText( const std::string& text, InstancedFont* font, - const types::Color& color + const types::Color& color, + const types::Color& shadow_color ) { - Log( "Creating instanced text: '" + text + "', " + font->GetFontName() + ", " + color.ToString() ); - return new InstancedText( text, font, color ); + Log( "Creating instanced text: '" + text + "', " + font->GetFontName() + ", " + color.ToString() + "-" + shadow_color.ToString() ); + return new InstancedText( text, font, color, shadow_color ); } } diff --git a/src/task/game/text/InstancedTextManager.h b/src/task/game/text/InstancedTextManager.h index 46d733cf..0c35035c 100644 --- a/src/task/game/text/InstancedTextManager.h +++ b/src/task/game/text/InstancedTextManager.h @@ -37,7 +37,8 @@ CLASS( InstancedTextManager, common::Class ) InstancedText* CreateInstancedText( const std::string& text, InstancedFont* font, - const types::Color& color + const types::Color& color, + const types::Color& shadow_color ); private: diff --git a/src/types/texture/Texture.cpp b/src/types/texture/Texture.cpp index b1c1f682..2e904f70 100644 --- a/src/types/texture/Texture.cpp +++ b/src/types/texture/Texture.cpp @@ -746,7 +746,7 @@ void Texture::RepaintFrom( const types::texture::Texture* original, const repain } -void Texture::ColorizeFrom( const types::texture::Texture* original, const types::Color& color ) { +void Texture::ColorizeFrom( const types::texture::Texture* original, const types::Color& color, const types::Color& shadow_color ) { ASSERT( m_width == original->m_width, "repaint width mismatch" ); ASSERT( m_height == original->m_height, "repaint width mismatch" ); ASSERT( m_bpp == original->m_bpp, "repaint bpp mismatch" ); @@ -759,8 +759,16 @@ void Texture::ColorizeFrom( const types::texture::Texture* original, const types for ( size_t x = 0 ; x < m_width ; x++ ) { const auto idx = ( y * m_width + x ) * m_bpp; memcpy( &rgba, ptr( original->m_bitmap, idx, m_bpp ), m_bpp ); - rgba = ( types::Color::FromRGBA( rgba ) * color ).GetRGBA(); - memcpy( ptr( m_bitmap, idx, m_bpp ), &rgba, m_bpp ); + if ( rgba ) { + const auto c = types::Color::FromRGBA( rgba ); + if ( !c.value.red && !c.value.green && !c.value.blue ) { + rgba = ( c * shadow_color ).GetRGBA(); + } + else { + rgba = ( c * color ).GetRGBA(); + } + memcpy( ptr( m_bitmap, idx, m_bpp ), &rgba, m_bpp ); + } } } } diff --git a/src/types/texture/Texture.h b/src/types/texture/Texture.h index 83de9411..6977f5be 100644 --- a/src/types/texture/Texture.h +++ b/src/types/texture/Texture.h @@ -74,7 +74,7 @@ CLASS( Texture, Serializable ) void Fill( const size_t x1, const size_t y1, const size_t x2, const size_t y2, const types::Color& color ); void RepaintFrom( const types::texture::Texture* original, const repaint_rules_t& rules ); - void ColorizeFrom( const types::texture::Texture* original, const types::Color& color ); + void ColorizeFrom( const types::texture::Texture* original, const types::Color& color, const types::Color& shadow_color ); void Rotate(); void FlipV(); From 19224808afe05169175b06eac775229e5036fc23 Mon Sep 17 00:00:00 2001 From: afwbkbc Date: Sun, 30 Jun 2024 20:56:56 +0300 Subject: [PATCH 16/18] hide units in bases unless selected --- src/task/game/Game.cpp | 1 + src/task/game/tile/Tile.cpp | 22 ++++++++++++++++++---- src/task/game/tile/TileManager.cpp | 7 +++++++ src/task/game/tile/TileManager.h | 1 + 4 files changed, 27 insertions(+), 4 deletions(-) diff --git a/src/task/game/Game.cpp b/src/task/game/Game.cpp index 669c6023..d0504a99 100644 --- a/src/task/game/Game.cpp +++ b/src/task/game/Game.cpp @@ -1823,6 +1823,7 @@ void Game::DeselectTileOrUnit() { HideTileSelector(); m_um->DeselectUnit(); + m_tm->DeselectTile(); m_ui.bottom_bar->HideTilePreview(); } diff --git a/src/task/game/tile/Tile.cpp b/src/task/game/tile/Tile.cpp index 8cb60901..d6eb61f4 100644 --- a/src/task/game/tile/Tile.cpp +++ b/src/task/game/tile/Tile.cpp @@ -102,11 +102,18 @@ void Tile::Render( size_t selected_unit_id ) { } m_render.currently_rendered_fake_badges.clear(); + bool should_show_units = !m_units.empty(); if ( m_base ) { m_base->Show(); + should_show_units = false; + for ( const auto& it : m_units ) { + if ( it.second->GetId() == selected_unit_id ) { + should_show_units = true; + break; + } + } } - - if ( !m_units.empty() ) { + if ( should_show_units ) { const auto units_order = GetUnitsOrder( m_units ); ASSERT_NOLOG( !units_order.empty(), "units order is empty" ); @@ -138,12 +145,12 @@ void Tile::Render( size_t selected_unit_id ) { } } if ( m_render.currently_rendered_unit ) { - m_render.currently_rendered_unit->Show(); const auto id = m_render.currently_rendered_unit->GetId(); + m_render.currently_rendered_unit->Show(); if ( id == most_important_unit_id ) { m_render.currently_rendered_unit->ShowBadge(); } - if ( id == selected_unit_id ) { + if ( id == selected_unit_id && m_render.currently_rendered_unit->IsActive() ) { m_render.currently_rendered_unit->StartBadgeBlink(); } } @@ -154,6 +161,13 @@ void Tile::Render( size_t selected_unit_id ) { fake_badges.at( idx )->ShowFakeBadge( i ); } } + else { + for ( const auto& it : m_units ) { + it.second->Hide(); + it.second->HideFakeBadge(); + it.second->HideBadge(); + } + } } diff --git a/src/task/game/tile/TileManager.cpp b/src/task/game/tile/TileManager.cpp index cc15f1f4..e8ae036a 100644 --- a/src/task/game/tile/TileManager.cpp +++ b/src/task/game/tile/TileManager.cpp @@ -102,6 +102,13 @@ void TileManager::SelectTile( Tile* tile ) { m_selected_tile = tile; } +void TileManager::DeselectTile() { + if ( m_selected_tile ) { + m_selected_tile->Render(); + m_selected_tile = nullptr; + } +} + } } } diff --git a/src/task/game/tile/TileManager.h b/src/task/game/tile/TileManager.h index 65207dc2..325fbaa9 100644 --- a/src/task/game/tile/TileManager.h +++ b/src/task/game/tile/TileManager.h @@ -27,6 +27,7 @@ CLASS( TileManager, ::common::Class ) Tile* GetSelectedTile() const; void SelectTile( Tile* tile ); + void DeselectTile(); private: From 9ceadc0561099b323b2c04288ffc6b7c5120129c Mon Sep 17 00:00:00 2001 From: afwbkbc Date: Thu, 4 Jul 2024 19:15:17 +0300 Subject: [PATCH 17/18] fixed/improved base name fonts --- src/task/game/base/BaseManager.cpp | 2 +- src/task/game/text/InstancedFont.cpp | 16 +++++++++------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/task/game/base/BaseManager.cpp b/src/task/game/base/BaseManager.cpp index 103b495b..166d6c53 100644 --- a/src/task/game/base/BaseManager.cpp +++ b/src/task/game/base/BaseManager.cpp @@ -20,7 +20,7 @@ namespace base { BaseManager::BaseManager( Game* game ) : m_game( game ) - , m_name_font( game->GetITM()->GetInstancedFont( g_engine->GetFontLoader()->LoadFont( resource::TTF_ARIALN, 36 ) ) ) { + , m_name_font( game->GetITM()->GetInstancedFont( g_engine->GetFontLoader()->LoadFont( resource::TTF_ARIALN, 48 ) ) ) { } diff --git a/src/task/game/text/InstancedFont.cpp b/src/task/game/text/InstancedFont.cpp index f92a633c..127a7eb3 100644 --- a/src/task/game/text/InstancedFont.cpp +++ b/src/task/game/text/InstancedFont.cpp @@ -11,13 +11,15 @@ namespace game { namespace text { static const types::Vec2< float > s_font_scale = { - 0.0045f, - 0.006f, + 0.003f, + 0.004f, }; const std::vector< float > s_shadow_alpha_levels = { 1.0f, - 0.6f, + 1.0f, + 0.5f, + 0.25f, }; InstancedFont::InstancedFont( sprite::InstancedSpriteManager* ism, const types::Font* font ) @@ -27,7 +29,7 @@ InstancedFont::InstancedFont( sprite::InstancedSpriteManager* ism, const types:: // load font into texture - const uint8_t sym_offset = 2; // to keep antialiasing working + const uint8_t sym_offset = s_shadow_alpha_levels.size() * 2 + 1; // to keep antialiasing working const uint8_t shadow_offset = s_shadow_alpha_levels.size(); unsigned int w = sym_offset + shadow_offset; @@ -45,7 +47,7 @@ InstancedFont::InstancedFont( sprite::InstancedSpriteManager* ism, const types:: w + sym_offset + shadow_offset, h + ( sym_offset + shadow_offset ) * 2 ); - const auto f_paint_base_texture = [ this ]( const types::Vec2< uint8_t >& offsets, const types::Color& multiplier ) -> void { + const auto f_paint_base_texture = [ this, sym_offset ]( const types::Vec2< uint8_t >& offsets, const types::Color& multiplier ) -> void { unsigned int sym_x = sym_offset; for ( uint8_t i = 32 ; i < 128 ; i++ ) { const auto& sym = m_font->m_symbols[ i ]; @@ -143,8 +145,8 @@ InstancedFont::InstancedFont( sprite::InstancedSpriteManager* ism, const types:: sym_offset }, { - sym.width + ( sym_offset + shadow_offset ) - 1, - sym.height + ( sym_offset + shadow_offset ) - 1 + sym.width + sym_offset, + sym.height + sym_offset }, { sym_x,// + sym.width / 2, From b1808182373123ebb3417921f8f703e0810c52a0 Mon Sep 17 00:00:00 2001 From: afwbkbc Date: Sun, 7 Jul 2024 17:27:52 +0300 Subject: [PATCH 18/18] added win64 workflow (test) --- .github/workflows/buildall.yml | 128 +++++++++++++++++++++++++++++++++ .github/workflows/c-cpp.yml | 66 ----------------- CMakeLists.txt | 5 ++ src/env/Win32.h | 4 +- 4 files changed, 136 insertions(+), 67 deletions(-) create mode 100644 .github/workflows/buildall.yml delete mode 100644 .github/workflows/c-cpp.yml diff --git a/.github/workflows/buildall.yml b/.github/workflows/buildall.yml new file mode 100644 index 00000000..41f05398 --- /dev/null +++ b/.github/workflows/buildall.yml @@ -0,0 +1,128 @@ +name: GLSMAC autobuild + +on: + push: + branches: [ "stable" ] + pull_request: + branches: [ "stable" ] + +jobs: + + prepare: + runs-on: ubuntu-latest + outputs: + upload_url: ${{ steps.create_release.outputs.upload_url }} + release_id: ${{ steps.create_release.outputs.id }} + ver: ${{ steps.info.outputs.ver }} + sha: ${{ steps.info.outputs.sha }} + steps: + - uses: benjlevesque/short-sha@v2.1 + id: short-sha + with: + length: 7 + - uses: dev-drprasad/delete-older-releases@v0.2.0 + with: + keep_latest: 4 + delete_tags: true + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: info + id: info + env: + ver: v0.3 + sha: ${{ steps.short-sha.outputs.sha }} + run: | + echo ${{ env.ver }} ${{ env.sha }} + echo "ver=${{ env.ver }}" >> $GITHUB_OUTPUT + echo "sha=${{ env.sha }}" >> $GITHUB_OUTPUT + - name: create-release + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ steps.info.outputs.ver }}-${{ steps.info.outputs.sha }} + release_name: ${{ steps.info.outputs.ver }}-${{ steps.info.outputs.sha }} + draft: false + prerelease: false + + + build_linux: + needs: [prepare] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: install_dependencies + run: | + sudo apt-get update + sudo apt-get install libfreetype-dev libsdl2-dev libsdl2-image-dev libglu-dev libglew-dev libossp-uuid-dev libyaml-cpp-dev + - name: prepare + run: | + mkdir build + - name: cmake + working-directory: ./build + run: cmake -DCMAKE_BUILD_TYPE=Portable64 .. + - name: make + working-directory: ./build + run: make + - name: data + working-directory: ./build + run: mv ../GLSMAC_data bin/ + - name: pack + working-directory: ./build + run: tar -C bin -zcvf GLSMAC.tar.gz GLSMAC GLSMAC_data + - name: publish + uses: actions/upload-artifact@v3 + with: + name: GLSMAC-linux64-bin + path: | + ./build/GLSMAC.tar.gz + - name: upload + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ needs.prepare.outputs.upload_url }} + asset_path: ./build/GLSMAC.tar.gz + asset_name: GLSMAC-${{ needs.prepare.outputs.ver }}-linux64-${{ needs.prepare.outputs.sha }}.tar.gz + asset_content_type: application/gzip + + + build_windows: + needs: [prepare] + runs-on: windows-latest + steps: + - uses: actions/checkout@v3 + - name: install_dependencies + run: | + choco install ninja + - name: prepare + run: | + mkdir build + - name: cmake + working-directory: ./build + run: cmake -G Ninja -DCMAKE_BUILD_TYPE=Portable64 -DVENDORED_DEPENDENCIES=1 .. + - name: make + working-directory: ./build + run: ninja + - name: data + working-directory: ./build + run: mv ../GLSMAC_data bin/ + - name: pack + working-directory: ./build/bin + run: Compress-Archive -Path GLSMAC.exe,GLSMAC_data -Destination ../GLSMAC.zip + - name: publish + uses: actions/upload-artifact@v3 + with: + name: GLSMAC-windows64-bin + path: | + ./build/GLSMAC.zip + - name: upload + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ needs.prepare.outputs.upload_url }} + asset_path: ./build/GLSMAC.zip + asset_name: GLSMAC-${{ needs.prepare.outputs.ver }}-win64-${{ needs.prepare.outputs.sha }}.zip + asset_content_type: application/gzip diff --git a/.github/workflows/c-cpp.yml b/.github/workflows/c-cpp.yml deleted file mode 100644 index 930a95b9..00000000 --- a/.github/workflows/c-cpp.yml +++ /dev/null @@ -1,66 +0,0 @@ -name: C/C++ CI - -on: - push: - branches: [ "stable" ] - pull_request: - branches: [ "stable" ] - -jobs: - build: - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v3 - - uses: benjlevesque/short-sha@v2.1 - id: short-sha - with: - length: 7 - - run: echo $SHA - env: - SHA: ${{ steps.short-sha.outputs.sha }} - - name: install_dependencies - run: | - sudo apt-get update - sudo apt-get install libfreetype-dev libsdl2-dev libsdl2-image-dev libglu-dev libglew-dev libossp-uuid-dev libyaml-cpp-dev - - name: cmake64 - run: cmake -DCMAKE_BUILD_TYPE=Portable64 . - - name: make64 - run: make - - name: data64 - run: mv GLSMAC_data bin/ - - name: pack64 - run: tar -C bin -zcvf GLSMAC-v0.3-${{ env.SHA }}-bin64.tar.gz GLSMAC GLSMAC_data - - name: publish - uses: actions/upload-artifact@v3 - with: - name: GLSMAC-ubuntu-bin - path: | - ./GLSMAC-v0.3-${{ env.SHA }}-bin64.tar.gz - - uses: dev-drprasad/delete-older-releases@v0.2.0 - with: - keep_latest: 4 - delete_tags: true - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: Create Release - id: create_release - uses: actions/create-release@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - tag_name: v0.3-${{ env.SHA }} - release_name: v0.3-${{ env.SHA }} - draft: false - prerelease: false - - name: Upload Release Asset 64 - id: upload-release-asset-64 - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps - name: upload - asset_path: ./GLSMAC-v0.3-${{ env.SHA }}-bin64.tar.gz - asset_name: GLSMAC-v0.3-${{ env.SHA }}-bin64.tar.gz - asset_content_type: application/gzip diff --git a/CMakeLists.txt b/CMakeLists.txt index 64035fef..cbb435f1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -67,6 +67,11 @@ INCLUDE_DIRECTORIES( "src" ) IF ( VISUAL_STUDIO ) # (Provided in CMakePresets) ADD_COMPILE_DEFINITIONS( _ITERATOR_DEBUG_LEVEL=0 ) ADD_COMPILE_DEFINITIONS( VISUAL_STUDIO ) +ELSEIF ( WIN32 ) # probably mingw or github runner + TARGET_LINK_LIBRARIES( ${PROJECT_NAME} PRIVATE wsock32 ) + TARGET_LINK_LIBRARIES( ${PROJECT_NAME} PRIVATE ws2_32 ) +# TARGET_LINK_OPTIONS( ${PROJECT_NAME} PRIVATE -static-libgcc -static-libstdc++ -static-winpthread ) + TARGET_LINK_OPTIONS( ${PROJECT_NAME} PRIVATE -static ) ENDIF () # needed for some uncommon IDE and OS combinations diff --git a/src/env/Win32.h b/src/env/Win32.h index eb43375d..d5bdf13c 100644 --- a/src/env/Win32.h +++ b/src/env/Win32.h @@ -7,10 +7,12 @@ #include typedef SSIZE_T ssize_t; #endif +#if !defined(NOMINMAX) #define NOMINMAX +#endif #define WIN32_LEAN_AND_MEAN -#define _WINSOCKAPI_ +#define _WINSOCKAPI_ #include // TODO: check if compiles with mingw #include