From 4be41d72598bcba270b758a8fb8d85a6d9bf6056 Mon Sep 17 00:00:00 2001 From: afwbkbc Date: Fri, 16 Feb 2024 01:40:22 +0200 Subject: [PATCH 01/21] "turn complete" notification --- src/game/Event.cpp | 4 + src/game/Event.h | 4 + src/game/Game.cpp | 27 +++++- src/game/Game.h | 3 + src/graphics/opengl/actor/Mesh.cpp | 3 + .../opengl/shader_program/Simple2D.cpp | 8 +- src/graphics/opengl/shader_program/Simple2D.h | 1 + src/task/game/Game.cpp | 5 ++ src/task/game/ui/bottom_bar/BottomBar.cpp | 10 ++- src/task/game/ui/bottom_bar/BottomBar.h | 4 + src/task/game/ui/bottom_bar/CMakeLists.txt | 1 + src/task/game/ui/bottom_bar/MiniMap.cpp | 6 -- src/task/game/ui/bottom_bar/MiniMap.h | 3 - .../game/ui/bottom_bar/TurnCompleteButton.cpp | 87 +++++++++++++++++++ .../game/ui/bottom_bar/TurnCompleteButton.h | 38 ++++++++ src/task/game/ui/style/BottomBar.cpp | 9 +- src/task/game/ui/style/Popup.cpp | 3 +- src/ui/object/Mesh.cpp | 9 +- src/ui/object/Mesh.h | 3 +- 19 files changed, 210 insertions(+), 18 deletions(-) create mode 100644 src/task/game/ui/bottom_bar/TurnCompleteButton.cpp create mode 100644 src/task/game/ui/bottom_bar/TurnCompleteButton.h diff --git a/src/game/Event.cpp b/src/game/Event.cpp index 87393d65..fbecf463 100644 --- a/src/game/Event.cpp +++ b/src/game/Event.cpp @@ -27,6 +27,10 @@ Event::Event( const Event& other ) NEW( data.global_message.message, std::string, *other.data.global_message.message ); break; } + case ET_TURN_COMPLETE_STATUS: { + data.turn_complete_status.is_turn_complete = other.data.turn_complete_status.is_turn_complete; + break; + } case ET_UNIT_SPAWN: { NEW( data.unit_spawn.serialized_unit, std::string, *other.data.unit_spawn.serialized_unit ); data.unit_spawn.coords = other.data.unit_spawn.coords; diff --git a/src/game/Event.h b/src/game/Event.h index 25b80477..9fb841e9 100644 --- a/src/game/Event.h +++ b/src/game/Event.h @@ -15,6 +15,7 @@ class Event { ET_QUIT, ET_ERROR, ET_GLOBAL_MESSAGE, + ET_TURN_COMPLETE_STATUS, ET_UNIT_SPAWN, ET_UNIT_DESPAWN, }; @@ -35,6 +36,9 @@ class Event { struct { std::string* message; } global_message; + struct { + bool is_turn_complete; + } turn_complete_status; struct { std::string* serialized_unit; struct { diff --git a/src/game/Game.cpp b/src/game/Game.cpp index 5a295e50..e44c103b 100644 --- a/src/game/Game.cpp +++ b/src/game/Game.cpp @@ -303,6 +303,8 @@ void Game::Iterate() { if ( m_state->IsMaster() ) { m_bindings->Call( Bindings::CS_ON_START ); } + + CheckTurnComplete(); } } else { @@ -311,7 +313,7 @@ void Game::Iterate() { } } else if ( m_game_state == GS_RUNNING ) { - // TODO: iterate GSE + // TODO: iterate GSE? } } @@ -906,6 +908,11 @@ void Game::DefineUnit( const unit::Def* def ) { } void Game::SpawnUnit( unit::Unit* unit ) { + if ( m_game_state != GS_RUNNING ) { + m_unprocessed_units.push_back( unit ); + return; + } + Log( "Spawning unit ('" + unit->m_def->m_name + "') at [ " + std::to_string( unit->m_pos_x ) + " " + std::to_string( unit->m_pos_y ) + " ]" ); ASSERT( m_units.find( unit->m_id ) == m_units.end(), "duplicate unit id" ); @@ -1005,7 +1012,7 @@ void Game::UnserializeUnits( types::Buffer& buf ) { for ( size_t i = 0 ; i < sz ; i++ ) { const auto unit_id = buf.ReadInt(); auto b = Buffer( buf.ReadString() ); - m_unprocessed_units.push_back( unit::Unit::Unserialize( b ) ); + SpawnUnit( unit::Unit::Unserialize( b ) ); } unit::Unit::SetNextId( buf.ReadInt() ); Log( "Restored next unit id: " + std::to_string( unit::Unit::GetNextId() ) ); @@ -1310,6 +1317,7 @@ void Game::ResetGame() { DELETE( m_current_turn ); m_current_turn = nullptr; } + m_is_turn_complete = false; if ( m_state ) { // ui thread will reset state as needed @@ -1337,4 +1345,19 @@ void Game::NextTurn() { Log( "turn " + std::to_string( m_current_turn->GetId() ) + " started" ); } +void Game::CheckTurnComplete() { + bool is_turn_complete = false; + + // TODO: check if any units should be moved + is_turn_complete = true; + + if ( m_is_turn_complete != is_turn_complete ) { + m_is_turn_complete = is_turn_complete; + Log( "Sending turn complete status: " + std::to_string( m_is_turn_complete ) ); + auto e = Event( Event::ET_TURN_COMPLETE_STATUS ); + e.data.turn_complete_status.is_turn_complete = m_is_turn_complete; + AddEvent( e ); + } +} + } diff --git a/src/game/Game.h b/src/game/Game.h index 3090f1cd..2863a0aa 100644 --- a/src/game/Game.h +++ b/src/game/Game.h @@ -317,6 +317,9 @@ CLASS( Game, MTModule ) Turn* m_current_turn = nullptr; + bool m_is_turn_complete = false; + void CheckTurnComplete(); + }; } diff --git a/src/graphics/opengl/actor/Mesh.cpp b/src/graphics/opengl/actor/Mesh.cpp index e5c0db97..91bc7205 100644 --- a/src/graphics/opengl/actor/Mesh.cpp +++ b/src/graphics/opengl/actor/Mesh.cpp @@ -261,6 +261,9 @@ void Mesh::Draw( shader_program::ShaderProgram* shader_program, Camera* camera ) case ( shader_program::ShaderProgram::TYPE_SIMPLE2D ) : { auto* sp = (shader_program::Simple2D*)shader_program; glUniform1ui( sp->uniforms.flags, flags ); + if ( flags & actor::Actor::RF_USE_TINT ) { + glUniform4fv( sp->uniforms.tint_color, 1, (const GLfloat*)&mesh_actor->GetTintColor() ); + } if ( flags & actor::Actor::RF_USE_AREA_LIMITS ) { const auto& limits = mesh_actor->GetAreaLimits(); glUniform3fv( sp->uniforms.area_limits.min, 1, (const GLfloat*)&limits.first ); diff --git a/src/graphics/opengl/shader_program/Simple2D.cpp b/src/graphics/opengl/shader_program/Simple2D.cpp index 1fafda9f..ea264b24 100644 --- a/src/graphics/opengl/shader_program/Simple2D.cpp +++ b/src/graphics/opengl/shader_program/Simple2D.cpp @@ -34,6 +34,7 @@ void main(void) { \ in vec2 texpos; \ in vec3 fragpos; \ uniform uint uFlags; \ +uniform vec4 uTintColor; \ uniform sampler2D uTexture; \ uniform vec3 uAreaLimitsMin; \ uniform vec3 uAreaLimitsMax; \ @@ -52,7 +53,11 @@ void main(void) { \ return; \ } \ } \ - FragColor = vec4(texture2D(uTexture, vec2(texpos.xy))); \ + vec4 color = vec4(texture2D(uTexture, vec2(texpos.xy))); \ + if ( " + S_HasFlag( "uFlags", actor::Actor::RF_USE_TINT ) + " ) { \ + color *= uTintColor; \ + } \ + FragColor = color; \ } \ \ " @@ -64,6 +69,7 @@ void Simple2D::Initialize() { attributes.tex_coord = GetAttributeLocation( "aTexCoord" ); attributes.coord = GetAttributeLocation( "aCoord" ); uniforms.flags = GetUniformLocation( "uFlags" ); + uniforms.tint_color = GetUniformLocation( "uTintColor" ); uniforms.position = GetUniformLocation( "uPosition" ); uniforms.texture = GetUniformLocation( "uTexture" ); uniforms.area_limits.min = GetUniformLocation( "uAreaLimitsMin" ); diff --git a/src/graphics/opengl/shader_program/Simple2D.h b/src/graphics/opengl/shader_program/Simple2D.h index 8dd711f3..0540e3d1 100644 --- a/src/graphics/opengl/shader_program/Simple2D.h +++ b/src/graphics/opengl/shader_program/Simple2D.h @@ -20,6 +20,7 @@ CLASS( Simple2D, ShaderProgram ) struct { GLuint flags; + GLuint tint_color; GLuint position; GLuint texture; struct { diff --git a/src/task/game/Game.cpp b/src/task/game/Game.cpp index b0c2b765..daf7440d 100644 --- a/src/task/game/Game.cpp +++ b/src/task/game/Game.cpp @@ -903,6 +903,11 @@ void Game::ProcessEvent( const ::game::Event& event ) { AddMessage( *event.data.global_message.message ); break; } + case ::game::Event::ET_TURN_COMPLETE_STATUS: { + ASSERT( m_ui.bottom_bar, "bottom bar not initialized" ); + m_ui.bottom_bar->SetTurnCompleteStatus( event.data.turn_complete_status.is_turn_complete ); + break; + } case ::game::Event::ET_UNIT_SPAWN: { types::Buffer buf( *event.data.unit_spawn.serialized_unit ); const auto* unit = ::game::unit::Unit::Unserialize( buf ); diff --git a/src/task/game/ui/bottom_bar/BottomBar.cpp b/src/task/game/ui/bottom_bar/BottomBar.cpp index 98e5367b..2fa097a5 100644 --- a/src/task/game/ui/bottom_bar/BottomBar.cpp +++ b/src/task/game/ui/bottom_bar/BottomBar.cpp @@ -134,6 +134,9 @@ void BottomBar::Create() { } AddChild( m_sections.mini_map ); + NEW( m_sections.turn_complete_button, TurnCompleteButton ); + m_sections.mini_map->AddChild( m_sections.turn_complete_button ); + // side menus auto* ui = g_engine->GetUI(); @@ -201,8 +204,9 @@ void BottomBar::Destroy() { RemoveChild( m_sections.unit_preview ); RemoveChild( m_sections.tile_preview ); RemoveChild( m_sections.middle_area ); - RemoveChild( m_sections.mini_map ); RemoveChild( m_sections.units_list ); + m_sections.mini_map->RemoveChild( m_sections.turn_complete_button ); + RemoveChild( m_sections.mini_map ); auto* ui = g_engine->GetUI(); ui->RemoveObject( m_side_menus.left ); @@ -286,6 +290,10 @@ void BottomBar::UpdateMapFileName() { m_sections.middle_area->UpdateMapFileName(); } +void BottomBar::SetTurnCompleteStatus( const bool is_turn_complete ) { + m_sections.turn_complete_button->SetTurnCompleteStatus( is_turn_complete ); +} + } } } diff --git a/src/task/game/ui/bottom_bar/BottomBar.h b/src/task/game/ui/bottom_bar/BottomBar.h index 1bb8717f..92e8e99a 100644 --- a/src/task/game/ui/bottom_bar/BottomBar.h +++ b/src/task/game/ui/bottom_bar/BottomBar.h @@ -19,6 +19,7 @@ #include "TilePreview.h" #include "MiddleArea.h" #include "UnitsList.h" +#include "TurnCompleteButton.h" #include "MiniMap.h" // side menus @@ -55,6 +56,8 @@ CLASS( BottomBar, UI ) void AddMessage( const std::string& text ); void UpdateMapFileName(); + void SetTurnCompleteStatus( const bool is_turn_complete ); + private: struct { Surface* left = nullptr; @@ -77,6 +80,7 @@ CLASS( BottomBar, UI ) TilePreview* tile_preview = nullptr; MiddleArea* middle_area = nullptr; UnitsList* units_list = nullptr; + TurnCompleteButton* turn_complete_button = nullptr; MiniMap* mini_map = nullptr; } m_sections = {}; diff --git a/src/task/game/ui/bottom_bar/CMakeLists.txt b/src/task/game/ui/bottom_bar/CMakeLists.txt index f8bd0921..2cc06b20 100644 --- a/src/task/game/ui/bottom_bar/CMakeLists.txt +++ b/src/task/game/ui/bottom_bar/CMakeLists.txt @@ -14,5 +14,6 @@ SET( SRC ${SRC} ${PWD}/InfoPanel.cpp ${PWD}/UnitsList.cpp ${PWD}/MiniMap.cpp + ${PWD}/TurnCompleteButton.cpp PARENT_SCOPE ) diff --git a/src/task/game/ui/bottom_bar/MiniMap.cpp b/src/task/game/ui/bottom_bar/MiniMap.cpp index 91f80222..77fb6da4 100644 --- a/src/task/game/ui/bottom_bar/MiniMap.cpp +++ b/src/task/game/ui/bottom_bar/MiniMap.cpp @@ -15,10 +15,6 @@ MiniMap::MiniMap( Game* game ) void MiniMap::Create() { BBSection::Create(); - NEW( m_turn_complete_button, object::Button, "BBMinimapTurnCompleteButton" ); - m_turn_complete_button->SetLabel( "TURN COMPLETE" ); - AddChild( m_turn_complete_button ); - NEW( m_map_surface, object::Mesh, "BBMinimapImage" ); m_map_surface->SetMesh( types::mesh::Render::Rectangle() ); if ( m_texture ) { @@ -244,8 +240,6 @@ void MiniMap::Destroy() { ClearMinimapSelection(); - RemoveChild( m_turn_complete_button ); - auto* ui = g_engine->GetUI(); ui->RemoveGlobalEventHandler( m_handlers.mouse_move ); ui->RemoveGlobalEventHandler( m_handlers.mouse_up ); diff --git a/src/task/game/ui/bottom_bar/MiniMap.h b/src/task/game/ui/bottom_bar/MiniMap.h index 41e02b32..09322389 100644 --- a/src/task/game/ui/bottom_bar/MiniMap.h +++ b/src/task/game/ui/bottom_bar/MiniMap.h @@ -2,7 +2,6 @@ #include "BBSection.h" -#include "ui/object/Button.h" #include "ui/object/Mesh.h" #include "ui/object/Section.h" #include "ui/object/Label.h" @@ -41,8 +40,6 @@ CLASS( MiniMap, BBSection ) const UIEventHandler* mouse_move = nullptr; } m_handlers; - object::Button* m_turn_complete_button = nullptr; - object::Mesh* m_map_surface = nullptr; object::Mesh* m_map_selection = nullptr; diff --git a/src/task/game/ui/bottom_bar/TurnCompleteButton.cpp b/src/task/game/ui/bottom_bar/TurnCompleteButton.cpp new file mode 100644 index 00000000..cfc1a8fb --- /dev/null +++ b/src/task/game/ui/bottom_bar/TurnCompleteButton.cpp @@ -0,0 +1,87 @@ +#include "TurnCompleteButton.h" + +#include "engine/Engine.h" + +namespace task { +namespace game { +namespace ui { + +TurnCompleteButton::TurnCompleteButton() + : ::ui::object::Button( "BBTurnCompleteButton" ) { + SetLabel( "TURN COMPLETE" ); + On( + UIEvent::EV_BUTTON_CLICK, EH( this ) { + Log( "TURN COMPLETE" ); + return true; + } + ); + On( + UIEvent::EV_MOUSE_OVER, EH( this ) { + m_flashing->Hide(); + return true; + } + ); + On( + UIEvent::EV_MOUSE_OUT, EH( this ) { + m_flashing->Show(); + return true; + } + ); +} + +void TurnCompleteButton::Create() { + ::ui::object::Button::Create(); + + NEW( m_flashing, ::ui::object::Surface ); + m_flashing->SetMargin( 4 ); + m_flashing->SetTintAlpha( 0.0f ); + m_flashing->SetTexture( g_engine->GetTextureLoader()->LoadTexture( "console_x2_a.pcx", 199, 113, 373, 129 ) ); + AddChild( m_flashing ); + + NEW( m_sound, SoundEffect, "BBTurnCompleteSound" ); + AddChild( m_sound ); +} + +void TurnCompleteButton::Iterate() { + ::ui::object::Button::Iterate(); + + while ( m_flash_timer.HasTicked() ) { + if ( m_flash_reverse ) { + m_flash_alpha -= FLASH_ALPHA_STEP; + if ( m_flash_alpha <= FLASH_ALPHA_MIN ) { + m_flash_alpha = FLASH_ALPHA_MIN; + m_flash_reverse = false; + } + } + else { + m_flash_alpha += FLASH_ALPHA_STEP; + if ( m_flash_alpha >= FLASH_ALPHA_MAX ) { + m_flash_alpha = FLASH_ALPHA_MAX; + m_flash_reverse = true; + } + } + m_flashing->SetTintAlpha( m_flash_alpha ); + } +} + +void TurnCompleteButton::Destroy() { + RemoveChild( m_flashing ); + RemoveChild( m_sound ); + + ::ui::object::Button::Destroy(); +} + +void TurnCompleteButton::SetTurnCompleteStatus( const bool is_turn_complete ) { + if ( is_turn_complete && !m_flash_timer.IsRunning() ) { + m_flash_timer.SetInterval( FLASH_INTERVAL_MS ); + m_sound->Play(); + } + else if ( !is_turn_complete && m_flash_timer.IsRunning() ) { + m_flash_timer.Stop(); + m_flashing->SetTintAlpha( 0.0f ); + } +} + +} +} +} diff --git a/src/task/game/ui/bottom_bar/TurnCompleteButton.h b/src/task/game/ui/bottom_bar/TurnCompleteButton.h new file mode 100644 index 00000000..3ebbb531 --- /dev/null +++ b/src/task/game/ui/bottom_bar/TurnCompleteButton.h @@ -0,0 +1,38 @@ +#pragma once + +#include "ui/object/Button.h" + +#include "util/Timer.h" +#include "ui/object/SoundEffect.h" + +namespace task { +namespace game { +namespace ui { + +CLASS( TurnCompleteButton, ::ui::object::Button ) + + TurnCompleteButton(); + + void Create() override; + void Iterate() override; + void Destroy() override; + + void SetTurnCompleteStatus( const bool is_turn_complete ); + +private: + const size_t FLASH_INTERVAL_MS = 10; + const float FLASH_ALPHA_MIN = 0.0f; + const float FLASH_ALPHA_MAX = 1.0f; + const float FLASH_ALPHA_STEP = 0.0125f; + util::Timer m_flash_timer; + float m_flash_alpha = 0.0f; + bool m_flash_reverse = false; + + ::ui::object::Surface* m_flashing = nullptr; + ::ui::object::SoundEffect* m_sound = nullptr; + +}; + +} +} +} diff --git a/src/task/game/ui/style/BottomBar.cpp b/src/task/game/ui/style/BottomBar.cpp index 95a3f581..e3c35315 100644 --- a/src/task/game/ui/style/BottomBar.cpp +++ b/src/task/game/ui/style/BottomBar.cpp @@ -534,7 +534,7 @@ void BottomBar::AddStyles() { ); AddStyle( - "MinimapTurnCompleteButton", SH() { + "TurnCompleteButton", SH() { const auto f_buttonstyle = [ &s ]( const size_t ox, const size_t oy ) -> void { const std::string t = "console_x2_a.pcx"; @@ -576,6 +576,13 @@ void BottomBar::AddStyles() { s->Set( ::Style::A_SOUND_VOLUME, 0.5f ); } ); + AddStyle( + "TurnCompleteSound", SH() { + s->SetSound( Style::A_SOUND, "cpu turn complete.wav" ); + s->Set( Style::A_SOUND_AUTOSTOP ); + s->Set( Style::A_SOUND_VOLUME, 0.5f ); + } + ); AddStyle( "MinimapImage", SH() { diff --git a/src/task/game/ui/style/Popup.cpp b/src/task/game/ui/style/Popup.cpp index d1223dcb..8d5caf9f 100644 --- a/src/task/game/ui/style/Popup.cpp +++ b/src/task/game/ui/style/Popup.cpp @@ -159,8 +159,7 @@ void Popup::AddStyles() { s->SetSound( Style::A_SOUND, "CPU please don't go.wav" ); s->Set( Style::A_SOUND_AUTOPLAY ); s->Set( Style::A_SOUND_AUTOSTOP ); - //s->Set( Style::A_SOUND_START_DELAY, 60 ); - s->Set( Style::A_SOUND_VOLUME, 0.5 ); + s->Set( Style::A_SOUND_VOLUME, 0.5f ); } ); diff --git a/src/ui/object/Mesh.cpp b/src/ui/object/Mesh.cpp index fd7830e2..b443a1e6 100644 --- a/src/ui/object/Mesh.cpp +++ b/src/ui/object/Mesh.cpp @@ -68,12 +68,18 @@ void Mesh::ClearTexture() { } } -void Mesh::SetTintColor( const types::Color color ) { +void Mesh::SetTintColor( const types::Color& color ) { m_tint_color.enabled = true; m_tint_color.color = color; UpdateRenderFlags(); } +void Mesh::SetTintAlpha( const float alpha ) { + m_tint_color.enabled = true; + m_tint_color.color = Color( 1.0f, 1.0f, 1.0f, alpha ); + UpdateRenderFlags(); +} + void Mesh::Destroy() { if ( m_actor ) { RemoveActor( m_actor ); @@ -273,6 +279,7 @@ void Mesh::UpdateRenderFlags() { if ( m_tint_color.enabled ) { m_actor->SetTintColor( m_tint_color.color ); } + Refresh(); } } diff --git a/src/ui/object/Mesh.h b/src/ui/object/Mesh.h index 5786471f..b50e6473 100644 --- a/src/ui/object/Mesh.h +++ b/src/ui/object/Mesh.h @@ -21,7 +21,8 @@ CLASS( Mesh, UIObject ) virtual void SetTexture( types::Texture* texture ); void ClearTexture(); - void SetTintColor( const types::Color color ); + void SetTintColor( const types::Color& color ); + void SetTintAlpha( const float alpha ); virtual void Destroy() override; virtual void Align() override; From 39314ac1fce30426f2a29aad1a7404ffd5c17d51 Mon Sep 17 00:00:00 2001 From: afwbkbc Date: Fri, 16 Feb 2024 12:16:41 +0200 Subject: [PATCH 02/21] moved gse bindings to state because need to configure stuff like factions before game starts --- gse/default/main.gls.js | 23 +++++++++++----- src/game/Game.cpp | 37 +++++++++----------------- src/game/Game.h | 6 +---- src/game/State.cpp | 32 ++++++++++++++++++++-- src/game/State.h | 18 ++++++++++++- src/game/bindings/Binding.cpp | 5 ++-- src/game/bindings/Binding.h | 6 ++++- src/game/bindings/Bindings.cpp | 21 +++++++++++---- src/game/bindings/Bindings.h | 11 +++++--- src/game/bindings/Exit.cpp | 6 +++-- src/game/bindings/Map.cpp | 22 +++++++++------ src/game/bindings/Message.cpp | 2 +- src/game/bindings/On.cpp | 19 +++++-------- src/game/bindings/Random.cpp | 2 +- src/game/bindings/Units.cpp | 6 ++--- src/game/event/DespawnUnit.cpp | 2 ++ src/game/event/Event.cpp | 1 - src/task/mainmenu/menu/Main.cpp | 6 +++++ src/task/mainmenu/menu/lobby/Lobby.cpp | 4 +++ 19 files changed, 150 insertions(+), 79 deletions(-) diff --git a/gse/default/main.gls.js b/gse/default/main.gls.js index d9fa9fb8..e6b4d259 100644 --- a/gse/default/main.gls.js +++ b/gse/default/main.gls.js @@ -1,18 +1,29 @@ -#game.on.start(() => { +#game.on.configure(() => { + + #print('CONFIGURE'); const rules = #include('rules'); - const units = #include('units'); + // TODO + +}); + +#game.on.start(() => { + + const units = #include('units'); let i = 0; - while (i < #size(units)) { + let sz = #size(units); + while (i < sz) { #game.units.define(units[i][0], units[i][1]); i++; } let y = 0; - while (y < #game.map.height) { + let w = #game.map.get_width(); + let h = #game.map.get_height(); + while (y < h) { let x = 0; - while (x < #game.map.width) { + while (x < w) { if (x % 2 == y % 2) { if (#game.random.get_int(0, 1) == 1) { let tile = #game.map.get_tile(x, y); @@ -41,9 +52,9 @@ if (def.name != 'MindWorms') { let tile = unit.get_tile(); let neighbours = [tile.get_W(), tile.get_NW(), tile.get_N(), tile.get_NE(), tile.get_E(), tile.get_SE(), tile.get_S(), tile.get_SW()]; + let nearby_units_count = 0; let i = 0; let sz = #size(neighbours); - let nearby_units_count = 0; while (i < sz) { if (!#is_empty(neighbours[i].get_units())) { nearby_units_count++; diff --git a/src/game/Game.cpp b/src/game/Game.cpp index e44c103b..025c3da3 100644 --- a/src/game/Game.cpp +++ b/src/game/Game.cpp @@ -268,16 +268,9 @@ void Game::Iterate() { m_connection->UpdateSlot( m_slot_num, m_slot, true ); } - // init gse - ASSERT( !m_bindings, "bindings already set" ); - NEW( m_bindings, Bindings, this ); - ASSERT( !m_current_turn, "turn is already initialized" ); try { - // run main gse entrypoint - m_bindings->RunMain(); - // start initial turn NextTurn(); @@ -301,7 +294,7 @@ void Game::Iterate() { m_unprocessed_events.clear(); if ( m_state->IsMaster() ) { - m_bindings->Call( Bindings::CS_ON_START ); + m_state->m_bindings->Call( Bindings::CS_ON_START ); } CheckTurnComplete(); @@ -349,6 +342,7 @@ const MT_Response Game::ProcessRequest( const MT_Request& request, MT_CANCELABLE //Log( "Got init request" ); ASSERT( request.data.init.state, "state not set" ); m_state = request.data.init.state; + m_state->SetGame( this ); InitGame( response, MT_C ); break; } @@ -874,14 +868,6 @@ void Game::OnGSEError( gse::Exception& err ) { AddEvent( e ); } -void Game::AddUnitDef( const std::string& name, const unit::Def* def, gse::Context* ctx, const gse::si_t& si ) { - if ( m_unit_defs.find( name ) != m_unit_defs.end() ) { - delete def; - throw gse::Exception( gse::EC.GAME_ERROR, "Unit definition '" + name + "' already exists", ctx, si ); - } - DefineUnit( def ); -} - const unit::Def* Game::GetUnitDef( const std::string& name ) const { const auto& it = m_unit_defs.find( name ); if ( it != m_unit_defs.end() ) { @@ -892,11 +878,16 @@ const unit::Def* Game::GetUnitDef( const std::string& name ) const { } } -const gse::Value Game::AddGameEvent( const event::Event* event, gse::Context* ctx, const gse::si_t& si ) { +const gse::Value Game::AddGameEvent( const event::Event* event, gse::Context* ctx, const gse::si_t& call_si ) { if ( m_connection ) { m_connection->SendGameEvent( event ); } - return ProcessGameEvent( event ); + try { + return ProcessGameEvent( event ); + } + catch ( std::runtime_error& err ) { + ERROR( gse::EC.GAME_ERROR, err.what() ); + } } void Game::DefineUnit( const unit::Def* def ) { @@ -945,7 +936,7 @@ void Game::SpawnUnit( unit::Unit* unit ) { AddEvent( e ); if ( m_state->IsMaster() ) { - m_bindings->Call( Bindings::CS_ON_SPAWN_UNIT, { unit->Wrap() } ); + m_state->m_bindings->Call( Bindings::CS_ON_SPAWN_UNIT, { unit->Wrap() } ); } } @@ -967,7 +958,7 @@ void Game::DespawnUnit( const size_t unit_id ) { m_units.erase( it ); if ( m_state->IsMaster() ) { - m_bindings->Call( Bindings::CS_ON_DESPAWN_UNIT, { unit->Wrap() } ); + m_state->m_bindings->Call( Bindings::CS_ON_DESPAWN_UNIT, { unit->Wrap() } ); } delete unit; @@ -1308,11 +1299,6 @@ void Game::ResetGame() { ASSERT( m_pending_events, "pending events not set" ); m_pending_events->clear(); - if ( m_bindings ) { - DELETE( m_bindings ); - m_bindings = nullptr; - } - if ( m_current_turn ) { DELETE( m_current_turn ); m_current_turn = nullptr; @@ -1321,6 +1307,7 @@ void Game::ResetGame() { if ( m_state ) { // ui thread will reset state as needed + m_state->UnsetGame(); m_state = nullptr; if ( m_connection ) { m_connection->Disconnect(); diff --git a/src/game/Game.h b/src/game/Game.h index 2863a0aa..aa3b8db4 100644 --- a/src/game/Game.h +++ b/src/game/Game.h @@ -19,7 +19,6 @@ #include "gse/GSE.h" #include "gse/GlobalContext.h" -#include "game/bindings/Bindings.h" #include "unit/Def.h" #include "unit/Unit.h" @@ -261,9 +260,8 @@ CLASS( Game, MTModule ) void Message( const std::string& text ); void Quit( const std::string& reason ); void OnGSEError( gse::Exception& e ); - void AddUnitDef( const std::string& name, const unit::Def* def, gse::Context* ctx, const gse::si_t& si ); const unit::Def* GetUnitDef( const std::string& name ) const; - const gse::Value AddGameEvent( const event::Event* event, gse::Context* ctx, const gse::si_t& si ); + const gse::Value AddGameEvent( const event::Event* event, gse::Context* ctx, const gse::si_t& call_si ); void DefineUnit( const unit::Def* def ); void SpawnUnit( unit::Unit* unit ); void DespawnUnit( const size_t unit_id ); @@ -310,8 +308,6 @@ CLASS( Game, MTModule ) map::Map* m_old_map = nullptr; // to restore state, for example if loading of another map failed map_editor::MapEditor* m_map_editor = nullptr; - bindings::Bindings* m_bindings = nullptr; - std::vector< const game::event::Event* > m_unprocessed_events = {}; std::vector< unit::Unit* > m_unprocessed_units = {}; diff --git a/src/game/State.cpp b/src/game/State.cpp index 516aff34..bcfbcc3d 100644 --- a/src/game/State.cpp +++ b/src/game/State.cpp @@ -2,12 +2,31 @@ namespace game { -State::State() { - // +State::State() + : m_bindings( new bindings::Bindings( this ) ) { + m_bindings->RunMain(); } State::~State() { Reset(); + delete m_bindings; +} + +void State::SetGame( Game* game ) { + ASSERT( !m_game, "game already set" ); + m_game = game; + m_on_gse_error = [ this ]( gse::Exception& e ) -> void { + m_game->OnGSEError( e ); + }; +} + +void State::UnsetGame() { + m_game = nullptr; + m_on_gse_error = nullptr; +} + +Game* State::GetGame() const { + return m_game; } void State::Iterate() { @@ -72,6 +91,13 @@ connection::Connection* State::GetConnection() const { return m_connection; } +void State::Configure() { + + // TODO: reset stuff + + m_bindings->Call( ::game::bindings::Bindings::CS_ON_CONFIGURE ); +} + void State::Reset() { if ( m_connection ) { if ( m_connection->IsConnected() ) { @@ -90,6 +116,8 @@ void State::Reset() { m_players.clear(); m_slots.Clear(); m_cid_slots.clear(); + m_game = nullptr; + m_on_gse_error = nullptr; } void State::DetachConnection() { diff --git a/src/game/State.h b/src/game/State.h index 825fb4ea..b039092c 100644 --- a/src/game/State.h +++ b/src/game/State.h @@ -3,6 +3,7 @@ #include #include #include +#include #include "base/Base.h" @@ -11,19 +12,30 @@ #include "Slots.h" #include "connection/Connection.h" - #include "network/types.h" +#include "bindings/Bindings.h" + namespace game { +class Game; + CLASS( State, base::Base ) State(); virtual ~State(); + void SetGame( Game* game ); + void UnsetGame(); + Game* GetGame() const; + Settings m_settings = {}; Slots m_slots = {}; + bindings::Bindings* const m_bindings = nullptr; + + std::function< void( gse::Exception& ) > m_on_gse_error = nullptr; + void Iterate(); bool IsMaster() const; @@ -40,14 +52,18 @@ CLASS( State, base::Base ) void SetConnection( connection::Connection* connection ); connection::Connection* GetConnection() const; + void Configure(); void Reset(); void DetachConnection(); private: + Game* m_game = nullptr; + std::unordered_set< Player* > m_players = {}; // persistent std::unordered_map< network::cid_t, size_t > m_cid_slots = {}; // volatile ( { cid, slot_num } ) connection::Connection* m_connection = nullptr; + }; } diff --git a/src/game/bindings/Binding.cpp b/src/game/bindings/Binding.cpp index 44e4bf54..269747b0 100644 --- a/src/game/bindings/Binding.cpp +++ b/src/game/bindings/Binding.cpp @@ -1,12 +1,13 @@ #include "Binding.h" +#include "game/State.h" + namespace game { namespace bindings { Binding::Binding( const std::string& name, Bindings* bindings ) : m_name( name ) - , m_bindings( bindings ) - , m_game( bindings->GetGame() ) { + , m_bindings( bindings ) { // } diff --git a/src/game/bindings/Binding.h b/src/game/bindings/Binding.h index d8c899d7..78872e25 100644 --- a/src/game/bindings/Binding.h +++ b/src/game/bindings/Binding.h @@ -9,6 +9,8 @@ namespace game { class Game; + +class State; namespace bindings { class Bindings; @@ -26,7 +28,7 @@ class Binding { const std::string m_name; Bindings* m_bindings; - Game* m_game; + }; } @@ -46,6 +48,8 @@ class Binding { #define BINDING_IMPL( _name ) \ gse::Value BINDING( _name )::Get() +#define GAME m_bindings->GetGame( ctx, call_si ) + #define ERROR( _type, _text ) throw gse::Exception( _type, _text, ctx, call_si ); #define CALLBACK( _type ) NATIVE_CALL( this ) { \ diff --git a/src/game/bindings/Bindings.cpp b/src/game/bindings/Bindings.cpp index 38825708..515b0f74 100644 --- a/src/game/bindings/Bindings.cpp +++ b/src/game/bindings/Bindings.cpp @@ -7,13 +7,15 @@ #include "gse/type/Object.h" #include "gse/type/Callable.h" +#include "game/State.h" + using namespace gse; namespace game { namespace bindings { -Bindings::Bindings( Game* game ) - : m_game( game ) { +Bindings::Bindings( State* state ) + : m_state( state ) { NEW( m_gse, gse::GSE ); m_gse->AddBindings( this ); m_gse_context = m_gse->CreateGlobalContext(); @@ -58,13 +60,22 @@ void Bindings::Call( const callback_slot_t slot, const callback_arguments_t& arg ( (gse::type::Callable*)it->second.Get() )->Run( m_gse_context, m_si_internal, arguments ); } catch ( gse::Exception& e ) { - m_game->OnGSEError( e ); + ASSERT_NOLOG( m_state->m_on_gse_error, "state gse error handler not set" ); + m_state->m_on_gse_error( e ); } } } -Game* Bindings::GetGame() const { - return m_game; +State* Bindings::GetState() const { + return m_state; +} + +Game* Bindings::GetGame( gse::Context* ctx, const si_t& call_si ) const { + auto* game = m_state->GetGame(); + if ( !game ) { + ERROR( EC.GAME_ERROR, "Game not started yet" ); + } + return game; } const Value Bindings::GetPlayer( const Player* player ) const { diff --git a/src/game/bindings/Bindings.h b/src/game/bindings/Bindings.h index 3bcfe95d..c23f17bd 100644 --- a/src/game/bindings/Bindings.h +++ b/src/game/bindings/Bindings.h @@ -15,11 +15,13 @@ namespace game { class Game; +class State; + namespace bindings { class Bindings : public gse::Bindings { public: - Bindings( Game* game ); + Bindings( State* state ); ~Bindings(); void AddToContext( gse::Context* ctx ) override; @@ -27,6 +29,7 @@ class Bindings : public gse::Bindings { void RunMain(); enum callback_slot_t { + CS_ON_CONFIGURE, CS_ON_START, CS_ON_SPAWN_UNIT, CS_ON_DESPAWN_UNIT, @@ -34,8 +37,8 @@ class Bindings : public gse::Bindings { typedef std::vector< gse::Value > callback_arguments_t; void Call( const callback_slot_t slot, const callback_arguments_t& arguments = {} ); - // for bindings - Game* GetGame() const; + State* GetState() const; + Game* GetGame( gse::Context* ctx, const gse::si_t& call_si ) const; void SetCallback( const callback_slot_t slot, const gse::Value& callback, gse::Context* context, const gse::si_t& si ); private: @@ -47,7 +50,7 @@ class Bindings : public gse::Bindings { std::unordered_map< callback_slot_t, gse::Value > m_callbacks = {}; - Game* m_game; + State* m_state = nullptr; const gse::Value GetPlayer( const Player* player ) const; const gse::Value GetFaction( const rules::Faction* faction ) const; diff --git a/src/game/bindings/Exit.cpp b/src/game/bindings/Exit.cpp index 389038f3..1d793d4f 100644 --- a/src/game/bindings/Exit.cpp +++ b/src/game/bindings/Exit.cpp @@ -10,9 +10,11 @@ BINDING_IMPL( exit ) { N_EXPECT_ARGS_MIN_MAX( 0, 1 ); if ( arguments.size() == 1 ) { N_GETVALUE( reason, 0, String ); - m_game->Quit( reason ); + GAME->Quit( reason ); + } + else { + GAME->Quit( "Script exited" ); } - m_game->Quit( "Script exited" ); return VALUE( gse::type::Undefined ); }); } diff --git a/src/game/bindings/Map.cpp b/src/game/bindings/Map.cpp index 43c1f06b..84755075 100644 --- a/src/game/bindings/Map.cpp +++ b/src/game/bindings/Map.cpp @@ -7,24 +7,30 @@ namespace game { namespace bindings { BINDING_IMPL( map ) { - const auto* m = m_game->GetMap(); - const auto w = m->GetWidth(); - const auto h = m->GetHeight(); const gse::type::Object::properties_t properties = { { - "width", - VALUE( gse::type::Int, w ), + "get_width", + NATIVE_CALL( this ) { + const auto* m = GAME->GetMap(); + return VALUE( gse::type::Int, m->GetWidth() ); + }) }, { - "height", - VALUE( gse::type::Int, h ), + "get_height", + NATIVE_CALL( this ) { + const auto* m = GAME->GetMap(); + return VALUE( gse::type::Int, m->GetHeight() ); + }) }, { "get_tile", - NATIVE_CALL( m, w, h ) { + NATIVE_CALL( this ) { N_EXPECT_ARGS( 2 ); N_GETVALUE( x, 0, Int ); N_GETVALUE( y, 1, Int ); + const auto* m = GAME->GetMap(); + const auto w = m->GetWidth(); + const auto h = m->GetHeight(); if ( x >= w ) { ERROR( gse::EC.INVALID_CALL, "X coordinate exceeds map width ( " + std::to_string( x ) + " >= " + std::to_string( w ) + " )" ); } diff --git a/src/game/bindings/Message.cpp b/src/game/bindings/Message.cpp index b55e8797..ed39bb98 100644 --- a/src/game/bindings/Message.cpp +++ b/src/game/bindings/Message.cpp @@ -9,7 +9,7 @@ BINDING_IMPL( message ) { return NATIVE_CALL( this ) { N_EXPECT_ARGS( 1 ); N_GETVALUE( text, 0, String ); - m_game->Message( text ); + GAME->Message( text ); return VALUE( gse::type::Undefined ); }); } diff --git a/src/game/bindings/On.cpp b/src/game/bindings/On.cpp index bcc74c97..8d954836 100644 --- a/src/game/bindings/On.cpp +++ b/src/game/bindings/On.cpp @@ -6,20 +6,15 @@ namespace game { namespace bindings { BINDING_IMPL( on ) { + +#define ON( _property, _cbtype ) { _property, CALLBACK( Bindings::_cbtype ) } const gse::type::Object::properties_t properties = { - { - "start", - CALLBACK( Bindings::CS_ON_START ) - }, - { - "spawn_unit", - CALLBACK( Bindings::CS_ON_SPAWN_UNIT ) - }, - { - "despawn_unit", - CALLBACK( Bindings::CS_ON_DESPAWN_UNIT ) - }, + ON( "configure", CS_ON_CONFIGURE ), + ON( "start", CS_ON_START ), + ON( "spawn_unit", CS_ON_SPAWN_UNIT ), + ON( "despawn_unit", CS_ON_DESPAWN_UNIT ), }; +#undef ON return VALUE( gse::type::Object, properties ); } diff --git a/src/game/bindings/Random.cpp b/src/game/bindings/Random.cpp index 4cbd4bf9..16d85ce3 100644 --- a/src/game/bindings/Random.cpp +++ b/src/game/bindings/Random.cpp @@ -17,7 +17,7 @@ BINDING_IMPL( random ) { if ( max < min ) { ERROR( gse::EC.INVALID_CALL, "Maximum value is smaller than minimum ( " + std::to_string( max ) + " < " + std::to_string( min ) + " )" ); } - return VALUE( gse::type::Int, m_game->GetRandom()->GetInt64( min, max ) ); + return VALUE( gse::type::Int, GAME->GetRandom()->GetInt64( min, max ) ); }) } }; diff --git a/src/game/bindings/Units.cpp b/src/game/bindings/Units.cpp index cfd951f8..94d1a70c 100644 --- a/src/game/bindings/Units.cpp +++ b/src/game/bindings/Units.cpp @@ -38,7 +38,7 @@ BINDING_IMPL( units ) { name, new unit::SpriteRender( sprite_file, sprite_x, sprite_y, sprite_w, sprite_h, sprite_cx, sprite_cy ) ); - return m_game->AddGameEvent( new event::DefineUnit( def ), ctx, call_si ); + return GAME->AddGameEvent( new event::DefineUnit( def ), ctx, call_si ); } else { ERROR( gse::EC.GAME_ERROR, "Unsupported render type: " + render_type ); @@ -56,7 +56,7 @@ BINDING_IMPL( units ) { N_EXPECT_ARGS( 2 ); N_GETVALUE( def_name, 0, String ); N_UNWRAP( tile, 1, map::Tile ); - return m_game->AddGameEvent( new event::SpawnUnit( def_name, tile->coord.x, tile->coord.y ), ctx, call_si ); + return GAME->AddGameEvent( new event::SpawnUnit( def_name, tile->coord.x, tile->coord.y ), ctx, call_si ); }) }, { @@ -64,7 +64,7 @@ BINDING_IMPL( units ) { NATIVE_CALL( this ) { N_EXPECT_ARGS( 1 ); N_UNWRAP( unit, 0, unit::Unit ); - return m_game->AddGameEvent( new event::DespawnUnit( unit->m_id ), ctx, call_si ); + return GAME->AddGameEvent( new event::DespawnUnit( unit->m_id ), ctx, call_si ); }) }, }; diff --git a/src/game/event/DespawnUnit.cpp b/src/game/event/DespawnUnit.cpp index 7d810b8a..83385d02 100644 --- a/src/game/event/DespawnUnit.cpp +++ b/src/game/event/DespawnUnit.cpp @@ -2,6 +2,8 @@ #include "../Game.h" +#include "gse/type/Undefined.h" + namespace game { namespace event { diff --git a/src/game/event/Event.cpp b/src/game/event/Event.cpp index 288f529e..10291d42 100644 --- a/src/game/event/Event.cpp +++ b/src/game/event/Event.cpp @@ -46,7 +46,6 @@ Event* Event::Unserialize( types::Buffer& buf ) { default: THROW( "unknown event type on read: " + std::to_string( type ) ); } - } const types::Buffer Event::SerializeMultiple( const std::vector< const Event* >& events ) { diff --git a/src/task/mainmenu/menu/Main.cpp b/src/task/mainmenu/menu/Main.cpp index a4c7769d..b37f9e79 100644 --- a/src/task/mainmenu/menu/Main.cpp +++ b/src/task/mainmenu/menu/Main.cpp @@ -16,6 +16,9 @@ Main::Main( MainMenu* mainmenu ) { "START GAME", { CH( this ) { + + m_mainmenu->m_state->Configure(); + m_mainmenu->m_state->m_settings.local.game_mode = game::LocalSettings::GM_SINGLEPLAYER; m_mainmenu->m_state->m_settings.global.Initialize(); NEWV( menu, StartGame, m_mainmenu ); @@ -26,6 +29,9 @@ Main::Main( MainMenu* mainmenu ) { "QUICK START", { CH( this ) { + + m_mainmenu->m_state->Configure(); + m_mainmenu->m_state->m_settings.local.game_mode = game::LocalSettings::GM_SINGLEPLAYER; // randomize settings diff --git a/src/task/mainmenu/menu/lobby/Lobby.cpp b/src/task/mainmenu/menu/lobby/Lobby.cpp index 78fd305f..20a7a2f0 100644 --- a/src/task/mainmenu/menu/lobby/Lobby.cpp +++ b/src/task/mainmenu/menu/lobby/Lobby.cpp @@ -17,6 +17,10 @@ Lobby::Lobby( MainMenu* mainmenu, Connection* connection ) , m_state( mainmenu->m_state ) { ASSERT( connection, "connection is null" ); + if ( m_state->IsMaster() ) { + m_state->Configure(); + } // slave will receive config from master + SetWidth( 800 ); SetHeight( 600 ); From ca84a32502253af00e94dc00e408ad131a73e668 Mon Sep 17 00:00:00 2001 From: afwbkbc Date: Fri, 16 Feb 2024 18:31:16 +0200 Subject: [PATCH 03/21] moved factions configuration to script --- gse/default/factions.gls.js | 62 ++++++++++ gse/default/main.gls.js | 10 +- src/game/Player.cpp | 10 +- src/game/Player.h | 13 +- src/game/Slot.cpp | 9 +- src/game/Slot.h | 6 + src/game/Slots.cpp | 12 +- src/game/Slots.h | 6 + src/game/State.cpp | 7 +- src/game/State.h | 2 +- src/game/bindings/Binding.h | 2 + src/game/bindings/Bindings.cpp | 55 ++------- src/game/bindings/Bindings.h | 5 - src/game/bindings/CMakeLists.txt | 1 + src/game/bindings/Factions.cpp | 37 ++++++ src/game/bindings/Units.cpp | 20 +-- src/game/connection/Client.cpp | 38 +++--- src/game/connection/Server.cpp | 6 +- src/game/map/Tile.cpp | 4 +- src/game/map/Tile.h | 2 +- src/game/rules/Faction.cpp | 7 +- src/game/rules/Faction.h | 3 +- src/game/rules/Rules.cpp | 4 +- src/game/rules/Rules.h | 5 +- src/game/rules/default/Default.cpp | 116 ------------------ src/game/rules/default/Default.h | 19 --- src/game/unit/Def.cpp | 4 +- src/game/unit/Def.h | 2 +- src/game/unit/Unit.cpp | 4 +- src/game/unit/Unit.h | 2 +- src/gse/Value.h | 27 +++- src/gse/builtins/Conversions.cpp | 35 ++++++ src/gse/callable/Native.h | 31 +++-- src/gse/type/Object.cpp | 4 + src/gse/type/Object.h | 1 + src/main.cpp | 3 +- src/task/game/ui/popup/PleaseDontGo.cpp | 2 +- src/task/game/ui/popup/PleaseDontGo.h | 2 +- src/task/mainmenu/MainMenu.cpp | 9 +- src/task/mainmenu/menu/HostJoin.cpp | 2 +- src/task/mainmenu/menu/HostJoin.h | 2 +- src/task/mainmenu/menu/Multiplayer.cpp | 2 +- src/task/mainmenu/menu/Multiplayer.h | 2 +- .../menu/lobby/GameSettingsSection.cpp | 28 ++--- .../mainmenu/menu/lobby/GameSettingsSection.h | 4 +- .../mainmenu/menu/lobby/PlayersSection.cpp | 13 +- src/task/mainmenu/menu/lobby/PlayersSection.h | 8 +- .../mainmenu/menu/lobby/PlayersSectionRow.cpp | 30 +++-- .../mainmenu/menu/lobby/PlayersSectionRow.h | 6 +- src/types/Color.cpp | 34 ++++- src/types/Color.h | 7 +- src/types/Packet.cpp | 16 +-- src/types/Packet.h | 3 +- src/ui/event/UIEvent.h | 3 +- src/ui/object/ChoiceList.cpp | 91 +++++++++----- src/ui/object/ChoiceList.h | 10 +- src/ui/object/Dropdown.cpp | 52 +++++--- src/ui/object/Dropdown.h | 12 +- 58 files changed, 545 insertions(+), 367 deletions(-) create mode 100644 gse/default/factions.gls.js create mode 100644 src/game/bindings/Factions.cpp diff --git a/gse/default/factions.gls.js b/gse/default/factions.gls.js new file mode 100644 index 00000000..93597fa5 --- /dev/null +++ b/gse/default/factions.gls.js @@ -0,0 +1,62 @@ +return [ + + // SMAC + ['GAIANS', { + name: 'Gaians', + color: #to_color(16, 228, 0) + }], + ['HIVE', { + name: 'Hive', + color: #to_color(0, 97, 235) + }], + ['UNIVERSITY', { + name: 'University', + color: #to_color(216, 224, 235) + }], + ['MORGANITES', { + name: 'Morganites', + color: #to_color(255, 255, 0) + }], + ['SPARTANS', { + name: 'Spartans', + color: #to_color(137, 166, 166) + }], + ['BELIEVERS', { + name: 'Believers', + color: #to_color(224, 156, 28) + }], + ['PEACEKEEPERS', { + name: 'Peacekeepers', + color: #to_color(164, 176, 232) + }], + + // SMACX + ['CONSCIOUSNESS', { + name: 'Consciousness', + color: #to_color(44, 128, 104) + }], + ['PIRATES', { + name: 'Pirates', + color: #to_color(0, 255, 255) + }], + ['DRONES', { + name: 'Drones', + color: #to_color(173, 196, 192) + }], + ['ANGELS', { + name: 'Angels', + color: #to_color(103, 91, 181) + }], + ['PLANETCULT', { + name: 'Planet Cult', + color: #to_color(232, 84, 84) + }], + ['CARETAKERS', { + name: 'Caretakers', + color: #to_color(116, 156, 56) + }], + ['USURPERS', { + name: 'Usurpers', + color: #to_color(212, 208, 116) + }], +]; diff --git a/gse/default/main.gls.js b/gse/default/main.gls.js index e6b4d259..255c30ad 100644 --- a/gse/default/main.gls.js +++ b/gse/default/main.gls.js @@ -2,8 +2,16 @@ #print('CONFIGURE'); - const rules = #include('rules'); + const factions = #include('factions'); + #print(#to_string(factions)); + let i = 0; + let sz = #size(factions); + while (i < sz) { + #game.factions.define(factions[i][0], factions[i][1]); + i++; + } + const rules = #include('rules'); // TODO }); diff --git a/src/game/Player.cpp b/src/game/Player.cpp index b7c65aeb..37e3eab7 100644 --- a/src/game/Player.cpp +++ b/src/game/Player.cpp @@ -4,17 +4,23 @@ using namespace types; namespace game { -Player::Player( Buffer buf ) { +// builtin +const rules::Faction Player::RANDOM_FACTION( "RANDOM", "Random", types::Color( 1.0f, 1.0f, 1.0f ) ); + +Player::Player( const rules::Rules& rules, Buffer buf ) + : m_rules( rules ) { Player::Unserialize( buf ); } Player::Player( + const rules::Rules& rules, const std::string& name, const role_t role, const rules::Faction& faction, const rules::DifficultyLevel& difficulty_level ) - : m_name( name ) + : m_rules( rules ) + , m_name( name ) , m_role( role ) , m_faction( faction ) , m_difficulty_level( difficulty_level ) { diff --git a/src/game/Player.h b/src/game/Player.h index 5c6d1ea3..3843ceb2 100644 --- a/src/game/Player.h +++ b/src/game/Player.h @@ -4,8 +4,7 @@ #include "types/Serializable.h" -#include "game/rules/Faction.h" -#include "game/rules/DifficultyLevel.h" +#include "game/rules/Rules.h" namespace game { @@ -13,6 +12,8 @@ class Slot; CLASS( Player, types::Serializable ) + const static rules::Faction RANDOM_FACTION; + enum role_t { PR_NONE, PR_SINGLE, @@ -20,8 +21,9 @@ CLASS( Player, types::Serializable ) PR_PLAYER, }; - Player( types::Buffer buf ); + Player( const rules::Rules& rules, types::Buffer buf ); Player( + const rules::Rules& rules, const std::string& name, const role_t role, const rules::Faction& faction, @@ -47,6 +49,9 @@ CLASS( Player, types::Serializable ) void Unserialize( types::Buffer buf ) override; private: + + const rules::Rules& m_rules; + bool m_is_initialized = false; std::string m_name = ""; @@ -54,7 +59,7 @@ CLASS( Player, types::Serializable ) Slot* m_slot = nullptr; - rules::Faction m_faction = {}; + rules::Faction m_faction = RANDOM_FACTION; rules::DifficultyLevel m_difficulty_level = {}; }; diff --git a/src/game/Slot.cpp b/src/game/Slot.cpp index 56094ef8..2130145b 100644 --- a/src/game/Slot.cpp +++ b/src/game/Slot.cpp @@ -1,7 +1,14 @@ #include "Slot.h" +#include "State.h" + namespace game { +Slot::Slot( const State* state ) + : m_state( state ) { + +} + const Slot::slot_state_t Slot::GetState() const { return m_slot_state; } @@ -126,7 +133,7 @@ void Slot::Unserialize( types::Buffer buf ) { m_slot_state = (slot_state_t)buf.ReadInt(); if ( m_slot_state == SS_PLAYER ) { if ( !m_player_data.player ) { - m_player_data.player = new Player( buf.ReadString() ); + m_player_data.player = new Player( m_state->m_settings.global.game_rules, buf.ReadString() ); m_player_data.player->SetSlot( this ); } else { diff --git a/src/game/Slot.h b/src/game/Slot.h index 4bcbe237..0dfce03d 100644 --- a/src/game/Slot.h +++ b/src/game/Slot.h @@ -7,8 +7,12 @@ namespace game { +class State; + CLASS( Slot, types::Serializable ) + Slot( const State* state ); + enum slot_state_t { SS_CLOSED, SS_OPEN, @@ -48,6 +52,8 @@ CLASS( Slot, types::Serializable ) private: + const State* m_state; + slot_state_t m_slot_state = SS_OPEN; bool m_close_after_clear = false; struct { diff --git a/src/game/Slots.cpp b/src/game/Slots.cpp index c876ed87..8059bc37 100644 --- a/src/game/Slots.cpp +++ b/src/game/Slots.cpp @@ -2,12 +2,22 @@ namespace game { +Slots::Slots( const State* state ) + : m_state( state ) { + +} + const size_t Slots::GetCount() const { return m_slots.size(); } void Slots::Resize( const size_t size ) { - m_slots.resize( size ); + if ( m_slots.size() > size ) { + m_slots.erase( m_slots.begin() + size, m_slots.end() ); + } + while ( m_slots.size() < size ) { + m_slots.push_back( { m_state } ); + } } Slot& Slots::GetSlot( const size_t index ) { diff --git a/src/game/Slots.h b/src/game/Slots.h index fb427d73..86c76339 100644 --- a/src/game/Slots.h +++ b/src/game/Slots.h @@ -5,8 +5,12 @@ namespace game { +class State; + CLASS( Slots, types::Serializable ) + Slots( const State* state ); + const size_t GetCount() const; void Resize( const size_t size ); Slot& GetSlot( const size_t index ); @@ -17,6 +21,8 @@ CLASS( Slots, types::Serializable ) void Unserialize( types::Buffer buf ) override; private: + const State* m_state; + std::vector< Slot > m_slots = {}; }; diff --git a/src/game/State.cpp b/src/game/State.cpp index bcfbcc3d..fb3c8fd1 100644 --- a/src/game/State.cpp +++ b/src/game/State.cpp @@ -93,9 +93,14 @@ connection::Connection* State::GetConnection() const { void State::Configure() { - // TODO: reset stuff + // reset + m_settings.global.game_rules.m_factions.clear(); + // configure m_bindings->Call( ::game::bindings::Bindings::CS_ON_CONFIGURE ); + + // check + ASSERT( !m_settings.global.game_rules.m_factions.empty(), "no factions were defined" ); } void State::Reset() { diff --git a/src/game/State.h b/src/game/State.h index b039092c..796ced04 100644 --- a/src/game/State.h +++ b/src/game/State.h @@ -30,7 +30,7 @@ CLASS( State, base::Base ) Game* GetGame() const; Settings m_settings = {}; - Slots m_slots = {}; + Slots m_slots = { this }; bindings::Bindings* const m_bindings = nullptr; diff --git a/src/game/bindings/Binding.h b/src/game/bindings/Binding.h index 78872e25..2dfc59ff 100644 --- a/src/game/bindings/Binding.h +++ b/src/game/bindings/Binding.h @@ -74,6 +74,8 @@ BINDING_DEF( random ) BINDING_DEF( on ) +BINDING_DEF( factions ) + BINDING_DEF( units ) BINDING_DEF( map ) diff --git a/src/game/bindings/Bindings.cpp b/src/game/bindings/Bindings.cpp index 515b0f74..f3825d3a 100644 --- a/src/game/bindings/Bindings.cpp +++ b/src/game/bindings/Bindings.cpp @@ -27,6 +27,7 @@ Bindings::Bindings( State* state ) B( exit ), B( random ), B( on ), + B( factions ), B( units ), B( map ), }; @@ -60,8 +61,12 @@ void Bindings::Call( const callback_slot_t slot, const callback_arguments_t& arg ( (gse::type::Callable*)it->second.Get() )->Run( m_gse_context, m_si_internal, arguments ); } catch ( gse::Exception& e ) { - ASSERT_NOLOG( m_state->m_on_gse_error, "state gse error handler not set" ); - m_state->m_on_gse_error( e ); + if ( m_state->m_on_gse_error ) { + m_state->m_on_gse_error( e ); + } + else { + throw std::runtime_error( e.ToStringAndCleanup() ); + } } } } @@ -78,52 +83,6 @@ Game* Bindings::GetGame( gse::Context* ctx, const si_t& call_si ) const { return game; } -const Value Bindings::GetPlayer( const Player* player ) const { - const type::Object::properties_t properties = { - { - { - "name", - VALUE( type::String, player->GetPlayerName() ), - }, - { - "full_name", - VALUE( type::String, player->GetFullName() ), - }, - { - "faction", - GetFaction( &player->GetFaction() ), - }, - { - "difficulty", - GetDifficulty( &player->GetDifficultyLevel() ) - }, - } - }; - return VALUE( type::Object, properties ); -} - -const gse::Value Bindings::GetFaction( const rules::Faction* faction ) const { - const type::Object::properties_t properties = { - { - "name", - VALUE( type::String, faction->m_name ), - } - }; - return VALUE( type::Object, properties ); -} - -const gse::Value Bindings::GetDifficulty( const rules::DifficultyLevel* difficulty ) const { - const type::Object::properties_t properties = { - { - "name", VALUE( type::String, difficulty->m_name ), - }, - { - "value", VALUE( type::Int, difficulty->m_difficulty ), - }, - }; - return VALUE( type::Object, properties ); -} - void Bindings::SetCallback( const callback_slot_t slot, const gse::Value& callback, gse::Context* context, const si_t& si ) { if ( m_callbacks.find( slot ) != m_callbacks.end() ) { throw gse::Exception( EC.GAME_ERROR, "Callback slot already in use", context, si ); diff --git a/src/game/bindings/Bindings.h b/src/game/bindings/Bindings.h index c23f17bd..5ee7e2ef 100644 --- a/src/game/bindings/Bindings.h +++ b/src/game/bindings/Bindings.h @@ -52,11 +52,6 @@ class Bindings : public gse::Bindings { State* m_state = nullptr; - const gse::Value GetPlayer( const Player* player ) const; - const gse::Value GetFaction( const rules::Faction* faction ) const; - const gse::Value GetDifficulty( const rules::DifficultyLevel* difficulty ) const; - //const gse::Value GetTurn( const Turn* turn ) const; - const std::string m_entry_script = util::FS::GeneratePath( { "gse", // directory is expected to be in working dir diff --git a/src/game/bindings/CMakeLists.txt b/src/game/bindings/CMakeLists.txt index 5ed3daa4..fedb1917 100644 --- a/src/game/bindings/CMakeLists.txt +++ b/src/game/bindings/CMakeLists.txt @@ -6,6 +6,7 @@ SET( SRC ${SRC} ${PWD}/Exit.cpp ${PWD}/Random.cpp ${PWD}/On.cpp + ${PWD}/Factions.cpp ${PWD}/Units.cpp ${PWD}/Map.cpp diff --git a/src/game/bindings/Factions.cpp b/src/game/bindings/Factions.cpp new file mode 100644 index 00000000..fd907807 --- /dev/null +++ b/src/game/bindings/Factions.cpp @@ -0,0 +1,37 @@ +#include "Binding.h" + +#include "gse/type/Object.h" +#include "gse/type/String.h" + +#include "game/State.h" + +#include "types/Color.h" + +namespace game { +namespace bindings { + +BINDING_IMPL( factions ) { + auto& factions = m_bindings->GetState()->m_settings.global.game_rules.m_factions; + const gse::type::Object::properties_t properties = { + { + "define", + NATIVE_CALL( this, &factions ) { + N_EXPECT_ARGS( 2 ); + N_GETVALUE( id, 0, String ); + N_GETVALUE( faction_def, 1, Object ); + N_GETPROP( name, faction_def, "name", String ); + N_GETPROP_UNWRAP( color, faction_def, "color", types::Color ); + if ( factions.find( id ) != factions.end() ) { + ERROR( gse::EC.GAME_ERROR, "Faction '" + id + "' already exists" ); + } + const auto* faction = new rules::Faction( id, name, color ); + factions.insert({ id, rules::Faction{ id, name, color } }); + return VALUE( gse::type::Undefined ); + }) + }, + }; + return VALUE( gse::type::Object, properties ); +} + +} +} diff --git a/src/game/bindings/Units.cpp b/src/game/bindings/Units.cpp index 94d1a70c..d1eeaf73 100644 --- a/src/game/bindings/Units.cpp +++ b/src/game/bindings/Units.cpp @@ -22,18 +22,18 @@ BINDING_IMPL( units ) { N_EXPECT_ARGS( 2 ); N_GETVALUE( name, 0, String ); N_GETVALUE( unit_def, 1, Object ); - N_GETPROP( unit_def, unit_type, "type", String ); + N_GETPROP( unit_type, unit_def, "type", String ); if ( unit_type == "static" ) { - N_GETPROP( unit_def, render_def, "render", Object ); - N_GETPROP( render_def, render_type, "type", String ); + N_GETPROP( render_def, unit_def, "render", Object ); + N_GETPROP( render_type, render_def, "type", String ); if ( render_type == "sprite" ) { - N_GETPROP( render_def, sprite_file, "file", String ); - N_GETPROP( render_def, sprite_x, "x", Int ); - N_GETPROP( render_def, sprite_y, "y", Int ); - N_GETPROP( render_def, sprite_w, "w", Int ); - N_GETPROP( render_def, sprite_h, "h", Int ); - N_GETPROP( render_def, sprite_cx, "cx", Int ); - N_GETPROP( render_def, sprite_cy, "cy", Int ); + N_GETPROP( sprite_file, render_def, "file", String ); + N_GETPROP( sprite_x, render_def, "x", Int ); + N_GETPROP( sprite_y, render_def, "y", Int ); + N_GETPROP( sprite_w, render_def, "w", Int ); + N_GETPROP( sprite_h, render_def, "h", Int ); + N_GETPROP( sprite_cx, render_def, "cx", Int ); + N_GETPROP( sprite_cy, render_def, "cy", Int ); const auto* def = new unit::StaticDef( name, new unit::SpriteRender( sprite_file, sprite_x, sprite_y, sprite_w, sprite_h, sprite_cx, sprite_cy ) diff --git a/src/game/connection/Client.cpp b/src/game/connection/Client.cpp index 2f5ea0ef..d528cd06 100644 --- a/src/game/connection/Client.cpp +++ b/src/game/connection/Client.cpp @@ -34,6 +34,25 @@ void Client::ProcessEvent( const network::Event& event ) { m_network->MT_SendPacket( p ); break; } + case Packet::PT_PLAYERS: { + Log( "Got players list from server" ); + m_slot = packet.data.num; + m_state->m_slots.Unserialize( packet.data.str ); + for ( auto i = 0 ; i < m_state->m_slots.GetCount() ; i++ ) { + const auto& slot = m_state->m_slots.GetSlot( i ); + if ( slot.GetState() == Slot::SS_PLAYER ) { + const auto& player = slot.GetPlayer(); + m_state->AddPlayer( player ); + if ( i == m_slot ) { + m_player = player; + } + } + } + if ( m_on_players_list_update ) { + m_on_players_list_update(); + } + break; + } case Packet::PT_GLOBAL_SETTINGS: { Log( "Got global settings update from server" ); const auto& host_slot = m_state->m_slots.GetSlot( 0 ); @@ -69,25 +88,6 @@ void Client::ProcessEvent( const network::Event& event ) { m_are_global_settings_received = true; break; } - case Packet::PT_PLAYERS: { - Log( "Got players list from server" ); - m_slot = packet.data.num; - m_state->m_slots.Unserialize( packet.data.str ); - for ( auto i = 0 ; i < m_state->m_slots.GetCount() ; i++ ) { - const auto& slot = m_state->m_slots.GetSlot( i ); - if ( slot.GetState() == Slot::SS_PLAYER ) { - const auto& player = slot.GetPlayer(); - m_state->AddPlayer( player ); - if ( i == m_slot ) { - m_player = player; - } - } - } - if ( m_on_players_list_update ) { - m_on_players_list_update(); - } - break; - } case Packet::PT_SLOT_UPDATE: case Packet::PT_FLAGS_UPDATE: { const bool only_flags = packet.type == Packet::PT_FLAGS_UPDATE; diff --git a/src/game/connection/Server.cpp b/src/game/connection/Server.cpp index 92bf8b35..32247424 100644 --- a/src/game/connection/Server.cpp +++ b/src/game/connection/Server.cpp @@ -24,9 +24,10 @@ void Server::ProcessEvent( const network::Event& event ) { const auto& rules = m_state->m_settings.global.game_rules; NEW( m_player, ::game::Player, + rules, m_state->m_settings.local.player_name, ::game::Player::PR_HOST, - rules.GetDefaultFaction(), + ::game::Player::RANDOM_FACTION, rules.GetDefaultDifficultyLevel() ); m_state->AddPlayer( m_player ); @@ -175,9 +176,10 @@ void Server::ProcessEvent( const network::Event& event ) { const auto& rules = m_state->m_settings.global.game_rules; NEWV( player, ::game::Player, + rules, player_name, ::game::Player::PR_PLAYER, - rules.GetDefaultFaction(), + ::game::Player::RANDOM_FACTION, rules.GetDefaultDifficultyLevel() ); m_state->AddPlayer( player ); diff --git a/src/game/map/Tile.cpp b/src/game/map/Tile.cpp index a8bcc9d8..568b76de 100644 --- a/src/game/map/Tile.cpp +++ b/src/game/map/Tile.cpp @@ -117,7 +117,9 @@ WRAPIMPL_BEGIN( Tile, CLASS_TILE ) return VALUE( gse::type::Object, result ); } ) } -WRAPIMPL_END( Tile ) +WRAPIMPL_END_PTR( Tile ) + +UNWRAPIMPL_PTR( Tile ) } } diff --git a/src/game/map/Tile.h b/src/game/map/Tile.h index d8185809..5d49abe0 100644 --- a/src/game/map/Tile.h +++ b/src/game/map/Tile.h @@ -148,7 +148,7 @@ class Tile { const Buffer Serialize() const; void Unserialize( Buffer data ); - WRAPDEFS( Tile ); + WRAPDEFS_PTR( Tile ); }; } diff --git a/src/game/rules/Faction.cpp b/src/game/rules/Faction.cpp index f75f0301..af1d8c24 100644 --- a/src/game/rules/Faction.cpp +++ b/src/game/rules/Faction.cpp @@ -7,8 +7,9 @@ Faction::Faction() { // } -Faction::Faction( const std::string& name, const types::Color& color ) - : m_name( name ) +Faction::Faction( const std::string& id, const std::string& name, const types::Color& color ) + : m_id( id ) + , m_name( name ) , m_color( color ) { // } @@ -16,6 +17,7 @@ Faction::Faction( const std::string& name, const types::Color& color ) const types::Buffer Faction::Serialize() const { types::Buffer buf; + buf.WriteString( m_id ); buf.WriteString( m_name ); buf.WriteColor( m_color ); @@ -24,6 +26,7 @@ const types::Buffer Faction::Serialize() const { void Faction::Unserialize( types::Buffer buf ) { + m_id = buf.ReadString(); m_name = buf.ReadString(); m_color = buf.ReadColor(); diff --git a/src/game/rules/Faction.h b/src/game/rules/Faction.h index 38840d98..740abd42 100644 --- a/src/game/rules/Faction.h +++ b/src/game/rules/Faction.h @@ -12,8 +12,9 @@ namespace rules { CLASS( Faction, types::Serializable ) Faction(); - Faction( const std::string& name, const types::Color& color ); + Faction( const std::string& id, const std::string& name, const types::Color& color ); + std::string m_id = ""; std::string m_name = ""; types::Color m_color = {}; diff --git a/src/game/rules/Rules.cpp b/src/game/rules/Rules.cpp index d70e184c..cdcdabc3 100644 --- a/src/game/rules/Rules.cpp +++ b/src/game/rules/Rules.cpp @@ -15,7 +15,7 @@ const types::Buffer Rules::Serialize() const { buf.WriteInt( m_factions.size() ); for ( auto& faction : m_factions ) { - buf.WriteInt( faction.first ); + buf.WriteString( faction.first ); buf.WriteString( faction.second.Serialize().ToString() ); } @@ -33,7 +33,7 @@ void Rules::Unserialize( types::Buffer buf ) { m_factions.clear(); const size_t factions_count = buf.ReadInt(); for ( size_t i = 0 ; i < factions_count ; i++ ) { - const size_t faction_id = buf.ReadInt(); + const auto faction_id = buf.ReadString(); m_factions[ faction_id ].Unserialize( buf.ReadString() ); } diff --git a/src/game/rules/Rules.h b/src/game/rules/Rules.h index e75e011a..cac606ee 100644 --- a/src/game/rules/Rules.h +++ b/src/game/rules/Rules.h @@ -12,10 +12,11 @@ namespace rules { CLASS( Rules, types::Serializable ) - std::map< size_t, Faction > m_factions; + typedef std::unordered_map< std::string, Faction > factions_t; + + factions_t m_factions; std::map< size_t, DifficultyLevel > m_difficulty_levels; - virtual const Faction& GetDefaultFaction() const = 0; virtual const DifficultyLevel& GetDefaultDifficultyLevel() const = 0; void Initialize(); diff --git a/src/game/rules/default/Default.cpp b/src/game/rules/default/Default.cpp index acef4852..93c8ea99 100644 --- a/src/game/rules/default/Default.cpp +++ b/src/game/rules/default/Default.cpp @@ -7,118 +7,6 @@ namespace rules { void Default::InitRules() { - m_factions = { - - // SMAC - - { - FT_RANDOM, { - "Random", - Color::FromRGB( 255, 255, 255 ) - } - }, - - { - FT_GAIANS, { - "Gaians", - Color::FromRGB( 16, 228, 0 ) - } - }, - - { - FT_HIVE, { - "Hive", - Color::FromRGB( 0, 97, 235 ) - } - }, - - { - FT_UNIVERSITY, { - "University", - Color::FromRGB( 216, 224, 235 ) - } - }, - - { - FT_MORGANITES, { - "Morganites", - Color::FromRGB( 255, 255, 0 ) - } - }, - - { - FT_SPARTANS, { - "Spartans", - Color::FromRGB( 137, 166, 166 ) - } - }, - - { - FT_BELIEVERS, { - "Believers", - Color::FromRGB( 224, 156, 28 ) - } - }, - - { - FT_PEACEKEEPERS, { - "Peacekeepers", - Color::FromRGB( 164, 176, 232 ) - } - }, - - // SMACX - - { - FT_CONSCIOUSNESS, { - "Consciousness", - Color::FromRGB( 44, 128, 104 ) - } - }, - - { - FT_PIRATES, { - "Pirates", - Color::FromRGB( 0, 255, 255 ) - } - }, - - { - FT_DRONES, { - "Drones", - Color::FromRGB( 173, 196, 192 ) - } - }, - - { - FT_ANGELS, { - "Angels", - Color::FromRGB( 103, 91, 181 ) - } - }, - - { - FT_PLANETCULT, { - "Planet Cult", - Color::FromRGB( 232, 84, 84 ) - } - }, - - { - FT_CARETAKERS, { - "Caretakers", - Color::FromRGB( 116, 156, 56 ) - } - }, - - { - FT_USURPERS, { - "Usurpers", - Color::FromRGB( 212, 208, 116 ) - } - }, - }; - m_difficulty_levels = { { DT_CITIZEN, { "Citizen", -3 } }, { DT_SPECIALIST, { "Specialist", -2 } }, @@ -130,10 +18,6 @@ void Default::InitRules() { } -const Faction& Default::GetDefaultFaction() const { - return m_factions.at( FT_RANDOM ); -} - const DifficultyLevel& Default::GetDefaultDifficultyLevel() const { return m_difficulty_levels.at( DT_TRANSCEND ); } diff --git a/src/game/rules/default/Default.h b/src/game/rules/default/Default.h index 9feca93e..1737cd0a 100644 --- a/src/game/rules/default/Default.h +++ b/src/game/rules/default/Default.h @@ -7,7 +7,6 @@ namespace rules { CLASS( Default, Rules ) - const Faction& GetDefaultFaction() const override; const DifficultyLevel& GetDefaultDifficultyLevel() const override; protected: @@ -15,24 +14,6 @@ CLASS( Default, Rules ) private: - enum faction_type_t { - FT_RANDOM, - FT_GAIANS, - FT_HIVE, - FT_UNIVERSITY, - FT_MORGANITES, - FT_SPARTANS, - FT_BELIEVERS, - FT_PEACEKEEPERS, - FT_CONSCIOUSNESS, - FT_PIRATES, - FT_DRONES, - FT_ANGELS, - FT_PLANETCULT, - FT_CARETAKERS, - FT_USURPERS, - }; - enum difficulty_type_t { DT_CITIZEN, DT_SPECIALIST, diff --git a/src/game/unit/Def.cpp b/src/game/unit/Def.cpp index ecebc954..48e3a535 100644 --- a/src/game/unit/Def.cpp +++ b/src/game/unit/Def.cpp @@ -49,7 +49,9 @@ WRAPIMPL_BEGIN( Def, CLASS_UNITDEF ) "type", VALUE( gse::type::String, "static" ) // TODO }, -WRAPIMPL_END( Def ) +WRAPIMPL_END_PTR( Def ) + +UNWRAPIMPL_PTR( Def ) } } diff --git a/src/game/unit/Def.h b/src/game/unit/Def.h index e9f39fc9..73e885df 100644 --- a/src/game/unit/Def.h +++ b/src/game/unit/Def.h @@ -29,7 +29,7 @@ class Def { static const types::Buffer Serialize( const Def* def ); static Def* Unserialize( types::Buffer& buf ); - WRAPDEFS( Def ); + WRAPDEFS_PTR( Def ); }; } diff --git a/src/game/unit/Unit.cpp b/src/game/unit/Unit.cpp index c3f32190..08fc0f38 100644 --- a/src/game/unit/Unit.cpp +++ b/src/game/unit/Unit.cpp @@ -68,7 +68,9 @@ WRAPIMPL_BEGIN( Unit, CLASS_UNIT ) return m_tile->Wrap(); }) } -WRAPIMPL_END( Unit ) +WRAPIMPL_END_PTR( Unit ) + +UNWRAPIMPL_PTR( Unit ) } } diff --git a/src/game/unit/Unit.h b/src/game/unit/Unit.h index 3b387dca..f5b5f8a8 100644 --- a/src/game/unit/Unit.h +++ b/src/game/unit/Unit.h @@ -28,7 +28,7 @@ class Unit { static const types::Buffer Serialize( const Unit* unit ); static Unit* Unserialize( types::Buffer& buf ); - WRAPDEFS( Unit ); + WRAPDEFS_PTR( Unit ); }; } diff --git a/src/gse/Value.h b/src/gse/Value.h index 1cb77224..4a08f7d8 100644 --- a/src/gse/Value.h +++ b/src/gse/Value.h @@ -17,19 +17,28 @@ namespace gse { #define VALUE_SET( _type, _var, _value ) { VALUE_DATA( _type, _var )->value = _value; } #define VALUE_CLONE( _type, _var ) VALUE( _type, VALUE_GET( _type, _var ) ) -#define WRAPDEFS( _type ) \ +#define WRAPDEFS_PTR( _type ) \ static const gse::type::Object::object_class_t WRAP_CLASS; \ const gse::Value Wrap() const; \ static _type* Unwrap( const gse::Value& value ); +#define WRAPDEFS_NOPTR( _type ) \ + static const gse::type::Object::object_class_t WRAP_CLASS; \ + const gse::Value Wrap() const; \ + static _type Unwrap( const gse::Value& value ); #define WRAPIMPL_BEGIN( _type, _class ) \ const gse::type::Object::object_class_t _type::WRAP_CLASS = gse::type::Object::_class; \ const gse::Value _type::Wrap() const { \ const gse::type::Object::properties_t properties = { - -#define WRAPIMPL_END( _type ) \ +#define WRAPIMPL_END_PTR( _type ) \ }; \ return VALUE( gse::type::Object, properties, WRAP_CLASS, this ); \ -} \ +} +#define WRAPIMPL_END_NOPTR( _type ) \ + }; \ + return VALUE( gse::type::Object, properties, WRAP_CLASS ); \ +} + +#define UNWRAPIMPL_PTR( _type ) \ _type* _type::Unwrap( const gse::Value& value ) { \ ASSERT_NOLOG( value.Get()->type == gse::type::Type::T_OBJECT, "can't unwrap non-object: " + value.Dump() ); \ const auto* obj = (gse::type::Object*)value.Get(); \ @@ -38,6 +47,16 @@ _type* _type::Unwrap( const gse::Value& value ) { \ return (_type*)obj->wrapptr; \ } +#define UNWRAPIMPL_NOPTR_BEGIN( _type ) \ +_type _type::Unwrap( const gse::Value& value ) { \ + ASSERT_NOLOG( value.Get()->type == gse::type::Type::T_OBJECT, "can only unwrap objects" ); \ + const auto* obj = (gse::type::Object*)value.Get(); \ + ASSERT_NOLOG( obj->object_class == WRAP_CLASS, "can only unwrap objects of same class" ); \ + const auto& properties = obj->value; + +#define UNWRAPIMPL_NOPTR_END() \ +} + class Value { public: Value() = delete; diff --git a/src/gse/builtins/Conversions.cpp b/src/gse/builtins/Conversions.cpp index cdd6b788..44e44acf 100644 --- a/src/gse/builtins/Conversions.cpp +++ b/src/gse/builtins/Conversions.cpp @@ -5,6 +5,8 @@ #include "gse/type/Float.h" #include "gse/type/String.h" +#include "types/Color.h" + namespace gse { namespace builtins { @@ -12,6 +14,22 @@ void Conversions::AddToContext( gse::Context* ctx ) { #define CONVERSION_ERROR( _type ) throw gse::Exception( EC.CONVERSION_ERROR, "Could not convert " + v->GetTypeString(v->type) + " to " + _type + ": " + v->ToString(), ctx, call_si ); +#define CONVERT_COLOR( _type, _constructor, _min, _max ) { \ + N_GETVALUE( r, 0, _type ); \ + if ( r < _min || r > _max ) throw gse::Exception( EC.INVALID_CALL, "Red value should be between " + std::to_string( _min ) + " and " + std::to_string( _max ) + ": " + std::to_string( r ), ctx, call_si ); \ + N_GETVALUE( g, 1, _type ); \ + if ( g < _min || g > _max ) throw gse::Exception( EC.INVALID_CALL, "Green value should be between " + std::to_string( _min ) + " and " + std::to_string( _max ) + ": " + std::to_string( g ), ctx, call_si ); \ + N_GETVALUE( b, 2, _type ); \ + if ( b < _min || b > _max ) throw gse::Exception( EC.INVALID_CALL, "Blue value should be between " + std::to_string( _min ) + " and " + std::to_string( _max ) + ": " + std::to_string( b ), ctx, call_si ); \ + if ( arguments.size() == 4 ) { \ + N_GETVALUE( a, 3, _type ); \ + if ( a < _min || a > _max ) throw gse::Exception( EC.INVALID_CALL, "Alpha value should be between " + std::to_string( _min ) + " and " + std::to_string( _max ) + ": " + std::to_string( a ), ctx, call_si ); \ + return _constructor( r, g, b, a ).Wrap(); \ + } \ + return _constructor( r, g, b ).Wrap(); \ +} + + ctx->CreateBuiltin( "to_string", NATIVE_CALL() { N_EXPECT_ARGS( 1 ); N_GET( v, 0 ); @@ -76,6 +94,23 @@ void Conversions::AddToContext( gse::Context* ctx ) { return VALUE( type::Float, value ); } ) ); + ctx->CreateBuiltin( "to_color", NATIVE_CALL() { + N_EXPECT_ARGS_MIN_MAX( 3, 4 ); + const auto f_err = [ &ctx, &call_si ] () { + throw Exception( EC.INVALID_CALL, "Color can be specified either by floats (0.0 to 1.0) or by ints (0 to 255)", ctx, call_si ); + }; + + switch ( arguments.at( 0 ).Get()->type ) { + case type::Type::T_FLOAT: CONVERT_COLOR( Float, types::Color, 0.0f, 1.0f ); + case type::Type::T_INT: CONVERT_COLOR( Int, types::Color::FromRGBA, 0, 255 ); + default: + f_err(); + } + return VALUE( gse::type::Undefined ); + } ) ); + +#undef CONVERT_COLOR + #undef CONVERSION_ERROR } diff --git a/src/gse/callable/Native.h b/src/gse/callable/Native.h index 733954e5..9a13f3bb 100644 --- a/src/gse/callable/Native.h +++ b/src/gse/callable/Native.h @@ -4,6 +4,7 @@ #include "gse/type/Callable.h" #include "gse/type/Object.h" +#include "gse/type/Undefined.h" namespace gse { namespace callable { @@ -13,7 +14,8 @@ namespace callable { // TODO: refactor these #define N_ARGS \ const gse::type::Type* arg; \ - gse::type::Object::properties_t::const_iterator obj_it; + gse::type::Object::properties_t::const_iterator obj_it; \ + auto getprop_val = VALUE( gse::type::Undefined ); #define N_EXPECT_ARGS( _count ) \ if ( arguments.size() != _count ) { \ throw gse::Exception( gse::EC.INVALID_CALL, "Expected " + std::to_string( _count ) + " arguments, found " + std::to_string( arguments.size() ), ctx, call_si ); \ @@ -50,24 +52,39 @@ namespace callable { #define N_UNWRAP( _var, _index, _type ) \ N_GETOBJ( obj, _index, _type::WRAP_CLASS ); \ const auto* _var = _type::Unwrap( obj ); +#define N_CHECK_OBJECT_CLASS( _var, _class ) \ + if ( ((gse::type::Object*)_var)->object_class != _class ) { \ + throw gse::Exception( gse::EC.INVALID_CALL, "Value is expected to be object of class " + gse::type::Object::GetClassString( _class ) + ", found class: " + gse::type::Object::GetClassString( ((gse::type::Object*)_var)->object_class ), ctx, call_si ); \ + } #define N_GETOBJECT( _var, _index, _class ) \ ASSERT_NOLOG( _index < arguments.size(), "argument index overflow" ); \ arg = arguments.at( _index ).Get(); \ N_CHECKTYPE( arg, _index, Object ); \ - if ( ((gse::type::Object*)arg)->object_class != gse::type::Object::_class ) { \ - throw gse::Exception( gse::EC.INVALID_CALL, "Argument " + std::to_string( _index ) + " is expected to be object of class " + gse::type::Object::GetClassString( gse::type::Object::_class ) + ", found class: " + gse::type::Object::GetClassString( ((gse::type::Object*)arg)->object_class ), ctx, call_si ); \ - } \ + N_CHECK_OBJECT_CLASS( arg, _class ); \ const auto& _var = ((gse::type::Object*)arg)->value; -#define N_GETPROP( _obj, _var, _key, _type ) \ +#define N_GETPROP_VAL( _obj, _key, _type ) \ obj_it = _obj.find( _key ); \ if ( obj_it == _obj.end() ) { \ throw gse::Exception( gse::EC.INVALID_CALL, (std::string)"Property " + _key + " is expected but not found", ctx, call_si ); \ } \ - arg = obj_it->second.Get(); \ + getprop_val = obj_it->second; +#define N_GETPROP_ARG( _obj, _key, _type ) \ + N_GETPROP_VAL( _obj, _key, _type ); \ + arg = getprop_val.Get(); \ if ( arg->type != gse::type::_type::GetType() ) { \ throw gse::Exception( gse::EC.INVALID_CALL, (std::string)"Property '" + _key + "' is expected to be " + #_type + ", found: " + arg->GetTypeString( arg->type ), ctx, call_si ); \ - } \ + } +#define N_GETPROP_UNWRAP( _var, _obj, _key, _type ) \ + N_GETPROP_VAL( _obj, _key, Object ); \ + N_CHECK_OBJECT_CLASS( getprop_val.Get(), _type::WRAP_CLASS ); \ + const auto _var = _type::Unwrap( getprop_val ); +#define N_GETPROP( _var, _obj, _key, _type ) \ + N_GETPROP_ARG( _obj, _key, _type ); \ const auto& _var = ((gse::type::_type*)arg)->value; +#define N_GETPROP_OBJECT( _var, _obj, _key, _class ) \ + N_GETPROP_ARG( _obj, _key, Object ); \ + N_CHECK_OBJECT_CLASS( arg, gse::type::Object::_class ); \ + const auto& _var = ((gse::type::Object*)arg)->value; class Native : public type::Callable { public: diff --git a/src/gse/type/Object.cpp b/src/gse/type/Object.cpp index 70e7d450..e3c5fd38 100644 --- a/src/gse/type/Object.cpp +++ b/src/gse/type/Object.cpp @@ -21,6 +21,10 @@ static const std::unordered_map< Object::object_class_t, std::string > s_object_ Object::CLASS_EXCEPTION, "#exception" }, + { + Object::CLASS_COLOR, + "#color" + }, { Object::CLASS_TILE, "#tile" diff --git a/src/gse/type/Object.h b/src/gse/type/Object.h index d4bea97e..8e535715 100644 --- a/src/gse/type/Object.h +++ b/src/gse/type/Object.h @@ -18,6 +18,7 @@ class Object : public Type { enum object_class_t { CLASS_NONE, CLASS_EXCEPTION, + CLASS_COLOR, CLASS_TILE, CLASS_UNIT, CLASS_UNITDEF, diff --git a/src/main.cpp b/src/main.cpp index c7b8d6f3..8498b642 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -278,9 +278,10 @@ int main( const int argc, const char* argv[] ) { const auto& rules = state->m_settings.global.game_rules; NEWV( player, ::game::Player, + rules, "Player", ::game::Player::PR_HOST, - rules.GetDefaultFaction(), + ::game::Player::RANDOM_FACTION, rules.GetDefaultDifficultyLevel() ); state->AddPlayer( player ); diff --git a/src/task/game/ui/popup/PleaseDontGo.cpp b/src/task/game/ui/popup/PleaseDontGo.cpp index 51890caa..0ab73353 100644 --- a/src/task/game/ui/popup/PleaseDontGo.cpp +++ b/src/task/game/ui/popup/PleaseDontGo.cpp @@ -23,7 +23,7 @@ void PleaseDontGo::Create() { m_text->SetText( "Do you really want to quit?" ); AddChild( m_text ); - NEW( m_choices, ::ui::object::ChoiceList, "PopupButtonList" ); + NEW( m_choices, ::ui::object::NumChoiceList, "PopupButtonList" ); m_choices->SetZIndex( 0.6f ); // TODO: fix z index bugs m_choices->SetTop( 164 ); m_choices->SetMargin( 3 ); diff --git a/src/task/game/ui/popup/PleaseDontGo.h b/src/task/game/ui/popup/PleaseDontGo.h index 592f73d4..afd772ec 100644 --- a/src/task/game/ui/popup/PleaseDontGo.h +++ b/src/task/game/ui/popup/PleaseDontGo.h @@ -27,7 +27,7 @@ CLASS( PleaseDontGo, Popup ) ::ui::object::Surface* m_image = nullptr; ::ui::object::Label* m_text = nullptr; - ::ui::object::ChoiceList* m_choices = nullptr; + ::ui::object::NumChoiceList* m_choices = nullptr; ::ui::object::SoundEffect* m_sound = nullptr; void SelectChoice(); diff --git a/src/task/mainmenu/MainMenu.cpp b/src/task/mainmenu/MainMenu.cpp index f2a34440..c754c332 100644 --- a/src/task/mainmenu/MainMenu.cpp +++ b/src/task/mainmenu/MainMenu.cpp @@ -185,12 +185,13 @@ void MainMenu::InitSinglePlayer() { m_state->m_slots.Resize( 7 ); // TODO: make dynamic? const auto& rules = m_state->m_settings.global.game_rules; m_state->m_settings.local.player_name = "Player"; - NEWV( player, ::game::Player, { + NEWV( player, ::game::Player, + rules, m_state->m_settings.local.player_name, ::game::Player::PR_SINGLE, - rules.GetDefaultFaction(), // TODO: make configurable - rules.GetDefaultDifficultyLevel(), // TODO: make configurable - } ); + ::game::Player::RANDOM_FACTION, + rules.GetDefaultDifficultyLevel() // TODO: make configurable + ); m_state->AddPlayer( player ); size_t slot_num = 0; // player always has slot 0 m_state->AddCIDSlot( 0, slot_num ); // for consistency diff --git a/src/task/mainmenu/menu/HostJoin.cpp b/src/task/mainmenu/menu/HostJoin.cpp index 9ef7d8df..cea2c8dd 100644 --- a/src/task/mainmenu/menu/HostJoin.cpp +++ b/src/task/mainmenu/menu/HostJoin.cpp @@ -30,7 +30,7 @@ void HostJoin::Show() { m_section->SetTitleText( "Would you like to host a new game or join an existing one?" ); m_body->AddChild( m_section ); - NEW( m_choices, ChoiceList, "PopupButtonList" ); + NEW( m_choices, NumChoiceList, "PopupButtonList" ); m_choices->SetImmediateMode( false ); m_choices->SetMargin( 3 ); m_choices->SetChoicesV( diff --git a/src/task/mainmenu/menu/HostJoin.h b/src/task/mainmenu/menu/HostJoin.h index 463cbae0..cb84e206 100644 --- a/src/task/mainmenu/menu/HostJoin.h +++ b/src/task/mainmenu/menu/HostJoin.h @@ -31,7 +31,7 @@ CLASS( HostJoin, PopupMenu ) private: Section* m_section = nullptr; - ChoiceList* m_choices = nullptr; + NumChoiceList* m_choices = nullptr; }; diff --git a/src/task/mainmenu/menu/Multiplayer.cpp b/src/task/mainmenu/menu/Multiplayer.cpp index e539ba57..ac109c71 100644 --- a/src/task/mainmenu/menu/Multiplayer.cpp +++ b/src/task/mainmenu/menu/Multiplayer.cpp @@ -30,7 +30,7 @@ void Multiplayer::Show() { m_section->SetTitleText( "Select a service..." ); m_body->AddChild( m_section ); - NEW( m_choices, ChoiceList, "PopupButtonList" ); + NEW( m_choices, NumChoiceList, "PopupButtonList" ); m_choices->SetImmediateMode( false ); m_choices->SetMargin( 3 ); m_choices->SetChoices( diff --git a/src/task/mainmenu/menu/Multiplayer.h b/src/task/mainmenu/menu/Multiplayer.h index e179e15c..c7dd3b3d 100644 --- a/src/task/mainmenu/menu/Multiplayer.h +++ b/src/task/mainmenu/menu/Multiplayer.h @@ -24,7 +24,7 @@ CLASS( Multiplayer, PopupMenu ) private: Section* m_section = nullptr; - ChoiceList* m_choices = nullptr; + NumChoiceList* m_choices = nullptr; }; diff --git a/src/task/mainmenu/menu/lobby/GameSettingsSection.cpp b/src/task/mainmenu/menu/lobby/GameSettingsSection.cpp index a805f18f..64abccd1 100644 --- a/src/task/mainmenu/menu/lobby/GameSettingsSection.cpp +++ b/src/task/mainmenu/menu/lobby/GameSettingsSection.cpp @@ -44,7 +44,7 @@ void GameSettingsSection::Create() { } // to allow selecting Load Map repeatedly - m_element_rows[ RI_MAP_TYPE ].choices->SetMode( Dropdown::DM_MENU ); + m_element_rows[ RI_MAP_TYPE ].choices->SetMode( NumDropdown::DM_MENU ); // these two are paginated based on "Random Map"/"Load Map" choice m_element_rows[ RI_MAP_FILE ].label->SetTop( m_element_rows[ RI_PLANET_SIZE ].label->GetTop() ); @@ -102,8 +102,8 @@ void GameSettingsSection::UpdateRows() { const auto& game_rules = m_game_settings->game_rules; - ::ui::object::ChoiceList::choices_t difficulty_levels = {}; - ChoiceList::value_t current_difficulty_level = 0; + ::ui::object::NumChoiceList::choices_t difficulty_levels = {}; + NumChoiceList::value_t current_difficulty_level = 0; for ( auto& it : game_rules.m_difficulty_levels ) { if ( m_game_settings->global_difficulty.m_name == it.second.m_name ) { current_difficulty_level = it.first; @@ -129,7 +129,7 @@ void GameSettingsSection::UpdateRows() { }, 0 ); - ChoiceList::value_t game_type = 0; + NumChoiceList::value_t game_type = 0; switch ( m_game_settings->map.type ) { case game::MapSettings::MT_RANDOM: case game::MapSettings::MT_CUSTOM: { @@ -218,7 +218,7 @@ void GameSettingsSection::CreateRow( const row_id_t row_id, const std::string& l label_el->SetLeft( 6 ); label_el->SetWidth( label_width ); m_body->AddChild( label_el ); - NEWV( choices_el, ::ui::object::Dropdown, "PopupDropdown" ); + NEWV( choices_el, ::ui::object::NumDropdown, "PopupDropdown" ); choices_el->SetAlign( UIObject::ALIGN_LEFT ); choices_el->SetLeft( label_width ); choices_el->SetWidth( choices_width ); @@ -227,14 +227,14 @@ void GameSettingsSection::CreateRow( const row_id_t row_id, const std::string& l const auto& game_rules = m_game_settings->game_rules; switch ( row_id ) { case RI_DIFFICULTY_LEVEL: - m_game_settings->global_difficulty = game_rules.m_difficulty_levels.at( data->value.change.id ); + m_game_settings->global_difficulty = game_rules.m_difficulty_levels.at( data->value.change.id_num ); break; case RI_TIME_CONTROLS: break; case RI_SPACE_1: break; case RI_MAP_TYPE: - if ( data->value.change.id == 1 ) { // "load map" + if ( data->value.change.id_num == 1 ) { // "load map" //choices_el->SetValue( 0 ); // keep "random map" until map is actually selected ShowLoadMap(); } @@ -246,19 +246,19 @@ void GameSettingsSection::CreateRow( const row_id_t row_id, const std::string& l } break; case RI_PLANET_SIZE: - m_game_settings->map.size = data->value.change.id; + m_game_settings->map.size = data->value.change.id_num; break; case RI_PLANET_OCEAN: - m_game_settings->map.ocean = data->value.change.id; + m_game_settings->map.ocean = data->value.change.id_num; break; case RI_PLANET_EROSIVE: - m_game_settings->map.erosive = data->value.change.id; + m_game_settings->map.erosive = data->value.change.id_num; break; case RI_PLANET_LIFEFORMS: - m_game_settings->map.lifeforms = data->value.change.id; + m_game_settings->map.lifeforms = data->value.change.id_num; break; case RI_PLANET_CLOUDS: - m_game_settings->map.clouds = data->value.change.id; + m_game_settings->map.clouds = data->value.change.id_num; break; case RI_MAP_FILE: // nothing to do yet because it will just open file browser @@ -277,11 +277,11 @@ void GameSettingsSection::CreateRow( const row_id_t row_id, const std::string& l }; } -void GameSettingsSection::UpdateRow( const row_id_t row_id, const ::ui::object::ChoiceList::choices_t& choices, const ChoiceList::value_t default_choice ) { +void GameSettingsSection::UpdateRow( const row_id_t row_id, const ::ui::object::NumChoiceList::choices_t& choices, const NumChoiceList::value_t default_choice ) { const auto& row = m_element_rows.at( row_id ); if ( !IsLocked() && row_id != RI_MAP_FILE ) { row.choices->SetChoices( choices ); - row.choices->SetValue( default_choice ); + row.choices->SetValueByKey( default_choice ); } else { row.choices->SetChoices( {} ); diff --git a/src/task/mainmenu/menu/lobby/GameSettingsSection.h b/src/task/mainmenu/menu/lobby/GameSettingsSection.h index cb519906..732e7520 100644 --- a/src/task/mainmenu/menu/lobby/GameSettingsSection.h +++ b/src/task/mainmenu/menu/lobby/GameSettingsSection.h @@ -56,11 +56,11 @@ CLASS( GameSettingsSection, LobbySection ) void CreateRow( const row_id_t row_id, const std::string& label, const size_t label_width, const size_t choices_width ); - void UpdateRow( const row_id_t row_id, const ::ui::object::ChoiceList::choices_t& choices, const ::ui::object::ChoiceList::value_t default_choice ); + void UpdateRow( const row_id_t row_id, const ::ui::object::NumChoiceList::choices_t& choices, const ::ui::object::NumChoiceList::value_t default_choice ); typedef struct { ::ui::object::Label* label; - ::ui::object::Dropdown* choices; + ::ui::object::NumDropdown* choices; } element_row_t; std::vector< element_row_t > m_element_rows = {}; diff --git a/src/task/mainmenu/menu/lobby/PlayersSection.cpp b/src/task/mainmenu/menu/lobby/PlayersSection.cpp index d37c1ba6..dfc266e8 100644 --- a/src/task/mainmenu/menu/lobby/PlayersSection.cpp +++ b/src/task/mainmenu/menu/lobby/PlayersSection.cpp @@ -55,10 +55,17 @@ void PlayersSection::UpdateSlots( std::vector< ::game::Slot >& slots ) { const auto& game_rules = GetLobby()->GetSettings().global.game_rules; m_choices.factions.clear(); + const auto& random_faction = ::game::Player::RANDOM_FACTION; + m_choices.factions.push_back( + { + random_faction.m_id, + random_faction.m_name + } + ); for ( auto& faction : game_rules.m_factions ) { m_choices.factions.push_back( { - faction.first, + faction.second.m_id, faction.second.m_name } ); @@ -80,11 +87,11 @@ void PlayersSection::UpdateSlots( std::vector< ::game::Slot >& slots ) { } } -const ChoiceList::choices_t& PlayersSection::GetFactionChoices() { +const AssocChoiceList::choices_t& PlayersSection::GetFactionChoices() { return m_choices.factions; } -const ChoiceList::choices_t& PlayersSection::GetDifficultyLevelChoices() { +const NumChoiceList::choices_t& PlayersSection::GetDifficultyLevelChoices() { return m_choices.difficulty_levels; } diff --git a/src/task/mainmenu/menu/lobby/PlayersSection.h b/src/task/mainmenu/menu/lobby/PlayersSection.h index 061ec009..c6a59b6e 100644 --- a/src/task/mainmenu/menu/lobby/PlayersSection.h +++ b/src/task/mainmenu/menu/lobby/PlayersSection.h @@ -30,16 +30,16 @@ CLASS( PlayersSection, LobbySection ) void UpdateSlot( const size_t slot_num, ::game::Slot* slot ); void UpdateSlots( std::vector< ::game::Slot >& slots ); - const ChoiceList::choices_t& GetFactionChoices(); - const ChoiceList::choices_t& GetDifficultyLevelChoices(); + const AssocChoiceList::choices_t& GetFactionChoices(); + const NumChoiceList::choices_t& GetDifficultyLevelChoices(); private: std::vector< PlayersSectionRow* > m_slots = {}; // some caches for player rows struct { - ChoiceList::choices_t factions = {}; - ChoiceList::choices_t difficulty_levels = {}; + AssocChoiceList::choices_t factions = {}; + NumChoiceList::choices_t difficulty_levels = {}; } m_choices; }; diff --git a/src/task/mainmenu/menu/lobby/PlayersSectionRow.cpp b/src/task/mainmenu/menu/lobby/PlayersSectionRow.cpp index 98730a3b..20a9b3f8 100644 --- a/src/task/mainmenu/menu/lobby/PlayersSectionRow.cpp +++ b/src/task/mainmenu/menu/lobby/PlayersSectionRow.cpp @@ -21,7 +21,7 @@ void PlayersSectionRow::Create() { const bool am_i_host = me->GetRole() == ::game::Player::PR_HOST; const bool am_i_ready = me->GetSlot()->HasPlayerFlag( ::game::Slot::PF_READY ); - NEW( m_elements.actions, Dropdown, "PopupDropdown" ); + NEW( m_elements.actions, NumDropdown, "PopupDropdown" ); m_elements.actions->SetLeft( 36 ); m_elements.actions->SetWidth( 178 ); @@ -33,7 +33,7 @@ void PlayersSectionRow::Create() { const bool is_it_ready = m_slot->HasPlayerFlag( ::game::Slot::PF_READY ); const bool allowed_to_change = is_it_me && !am_i_ready; - m_elements.actions->SetMode( Dropdown::DM_MENU ); + m_elements.actions->SetMode( NumDropdown::DM_MENU ); if ( is_it_me ) { ui::object::UIContainer::AddStyleModifier( Style::M_HIGHLIGHT ); @@ -41,7 +41,7 @@ void PlayersSectionRow::Create() { const auto& rules = m_parent->GetLobby()->GetSettings().global.game_rules; - const auto& faction_color = player->GetFaction().m_color; + const auto& faction = player->GetFaction(); if ( is_it_ready ) { NEW( m_elements.ready, Surface ); @@ -49,39 +49,43 @@ void PlayersSectionRow::Create() { m_elements.ready->SetHeight( 16 ); m_elements.ready->SetLeft( 8 ); m_elements.ready->SetTop( 4 ); - m_elements.ready->SetTexture( g_engine->GetTextureLoader()->GetColorTexture( faction_color ) ); + m_elements.ready->SetTexture( g_engine->GetTextureLoader()->GetColorTexture( faction.m_color ) ); m_elements.ready->SetStretchTexture( true ); AddChild( m_elements.ready ); } - NEW( m_elements.faction, Dropdown, "PopupDropdown" ); + NEW( m_elements.faction, AssocDropdown, "PopupDropdown" ); if ( allowed_to_change ) { m_elements.faction->SetChoices( m_parent->GetFactionChoices() ); + m_elements.faction->SetValueByKey( faction.m_id ); } - m_elements.faction->SetValue( player->GetFaction().m_name ); - m_elements.faction->SetTextColor( faction_color ); + else { + m_elements.faction->SetValue( faction.m_name ); + } + m_elements.faction->SetTextColor( faction.m_color ); m_elements.faction->SetLeft( 218 ); m_elements.faction->SetWidth( 140 ); m_elements.faction->On( UIEvent::EV_CHANGE, EH( this, player, rules ) { - player->SetFaction( rules.m_factions.at( data->value.change.id ) ); + ASSERT( rules.m_factions.find( *data->value.change.id_str ) != rules.m_factions.end(), "faction not found: " + *data->value.change.id_str ); + player->SetFaction( rules.m_factions.at( *data->value.change.id_str ) ); m_parent->GetLobby()->UpdateSlot( m_slot_num, m_slot ); return true; } ); AddChild( m_elements.faction ); - NEW( m_elements.difficulty_level, Dropdown, "PopupDropdown" ); + NEW( m_elements.difficulty_level, NumDropdown, "PopupDropdown" ); if ( allowed_to_change ) { m_elements.difficulty_level->SetChoices( m_parent->GetDifficultyLevelChoices() ); } m_elements.difficulty_level->SetValue( player->GetDifficultyLevel().m_name ); - m_elements.difficulty_level->SetTextColor( faction_color ); + m_elements.difficulty_level->SetTextColor( faction.m_color ); m_elements.difficulty_level->SetLeft( 360 ); m_elements.difficulty_level->SetWidth( 118 ); m_elements.difficulty_level->On( UIEvent::EV_CHANGE, EH( this, player, rules ) { - player->SetDifficultyLevel( rules.m_difficulty_levels.at( data->value.change.id ) ); + player->SetDifficultyLevel( rules.m_difficulty_levels.at( data->value.change.id_num ) ); m_parent->GetLobby()->UpdateSlot( m_slot_num, m_slot ); return true; } @@ -125,13 +129,13 @@ void PlayersSectionRow::Create() { ); } m_elements.actions->SetValue( player->GetPlayerName() ); - m_elements.actions->SetTextColor( faction_color ); + m_elements.actions->SetTextColor( faction.m_color ); } else { const bool allowed_to_change = am_i_host && !am_i_ready; - m_elements.actions->SetMode( Dropdown::DM_SELECT ); + m_elements.actions->SetMode( NumDropdown::DM_SELECT ); if ( allowed_to_change ) { m_elements.actions->SetChoicesV( diff --git a/src/task/mainmenu/menu/lobby/PlayersSectionRow.h b/src/task/mainmenu/menu/lobby/PlayersSectionRow.h index e61abcb7..37a68bb6 100644 --- a/src/task/mainmenu/menu/lobby/PlayersSectionRow.h +++ b/src/task/mainmenu/menu/lobby/PlayersSectionRow.h @@ -29,9 +29,9 @@ CLASS( PlayersSectionRow, UIContainer ) struct { Surface* ready = nullptr; - Dropdown* actions = nullptr; - Dropdown* faction = nullptr; - Dropdown* difficulty_level = nullptr; + NumDropdown* actions = nullptr; + AssocDropdown* faction = nullptr; + NumDropdown* difficulty_level = nullptr; } m_elements = {}; }; diff --git a/src/types/Color.cpp b/src/types/Color.cpp index d4a99397..6aad2db3 100644 --- a/src/types/Color.cpp +++ b/src/types/Color.cpp @@ -1,5 +1,7 @@ #include "Color.h" +#include "gse/type/Float.h" + namespace types { Color::Color() { @@ -94,7 +96,7 @@ Color Color::FromRGBA( const uint8_t red, const uint8_t green, const uint8_t blu } Color Color::FromRGB( const uint8_t red, const uint8_t green, const uint8_t blue ) { - return FromRGBA( red, green, blue, 255 ); + return FromRGBA( red, green, blue ); } Color::rgba_t Color::RGBA( const uint8_t red, const uint8_t green, const uint8_t blue, const uint8_t alpha ) { @@ -109,4 +111,32 @@ const std::string Color::ToString() const { return "{" + std::to_string( value.red ) + ":" + std::to_string( value.green ) + ":" + std::to_string( value.blue ) + ":" + std::to_string( value.alpha ) + "}"; } -} \ No newline at end of file +WRAPIMPL_BEGIN( Color, CLASS_COLOR ) + { + "r", + VALUE( gse::type::Float, value.red ) + }, + { + "g", + VALUE( gse::type::Float, value.green ) + }, + { + "b", + VALUE( gse::type::Float, value.blue ) + }, + { + "a", + VALUE( gse::type::Float, value.alpha ) + }, +WRAPIMPL_END_NOPTR( Color ) + +UNWRAPIMPL_NOPTR_BEGIN( Color ) + return { + ( (gse::type::Float*)properties.at( "r" ).Get() )->value, + ( (gse::type::Float*)properties.at( "g" ).Get() )->value, + ( (gse::type::Float*)properties.at( "b" ).Get() )->value, + ( (gse::type::Float*)properties.at( "a" ).Get() )->value + }; +UNWRAPIMPL_NOPTR_END() + +} diff --git a/src/types/Color.h b/src/types/Color.h index ee01fa87..af505439 100644 --- a/src/types/Color.h +++ b/src/types/Color.h @@ -4,6 +4,9 @@ #include #include +#include "gse/Value.h" +#include "gse/type/Object.h" + namespace types { class Color { @@ -41,12 +44,14 @@ class Color { 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 ); + static Color FromRGBA( const uint8_t red, const uint8_t green, const uint8_t blue, const uint8_t alpha = 255 ); static Color FromRGB( const uint8_t red, const uint8_t green, const uint8_t blue ); static rgba_t RGBA( const uint8_t red, const uint8_t green, const uint8_t blue, const uint8_t alpha ); static rgba_t RGB( const uint8_t red, const uint8_t green, const uint8_t blue ); const std::string ToString() const; + + WRAPDEFS_NOPTR( Color ); }; } /* namespace types */ diff --git a/src/types/Packet.cpp b/src/types/Packet.cpp index 87ec34a0..a75b59cd 100644 --- a/src/types/Packet.cpp +++ b/src/types/Packet.cpp @@ -18,15 +18,15 @@ const Buffer Packet::Serialize() const { buf.WriteString( data.vec[ 1 ] ); // player name break; } - case PT_GLOBAL_SETTINGS: { - buf.WriteString( data.str ); // serialized settings - break; - } case PT_PLAYERS: { buf.WriteInt( data.num ); // assigned slot num buf.WriteString( data.str ); // serialized slots break; } + case PT_GLOBAL_SETTINGS: { + buf.WriteString( data.str ); // serialized settings + break; + } case PT_UPDATE_SLOT: { buf.WriteString( data.str ); // serialized slot break; @@ -101,15 +101,15 @@ void Packet::Unserialize( Buffer buf ) { }; break; } - case PT_GLOBAL_SETTINGS: { - data.str = buf.ReadString(); // serialized settings - break; - } case PT_PLAYERS: { data.num = buf.ReadInt(); // assigned slot num data.str = buf.ReadString(); // serialized slots break; } + case PT_GLOBAL_SETTINGS: { + data.str = buf.ReadString(); // serialized settings + break; + } case PT_UPDATE_SLOT: { data.str = buf.ReadString(); // serialized slot break; diff --git a/src/types/Packet.h b/src/types/Packet.h index 5b34ced2..8b757360 100644 --- a/src/types/Packet.h +++ b/src/types/Packet.h @@ -14,8 +14,9 @@ CLASS( Packet, Serializable ) PT_AUTH, // C->S PT_PING, // *->* PT_PONG, // *->* - PT_GLOBAL_SETTINGS, // S->C + PT_PLAYERS, // S->C + PT_GLOBAL_SETTINGS, // S->C PT_UPDATE_SLOT, // C->S PT_SLOT_UPDATE, // S->C PT_UPDATE_FLAGS, // C->S diff --git a/src/ui/event/UIEvent.h b/src/ui/event/UIEvent.h index b5901e69..85769895 100644 --- a/src/ui/event/UIEvent.h +++ b/src/ui/event/UIEvent.h @@ -96,7 +96,8 @@ CLASS( UIEvent, base::Base ) float percentage; } scroll; struct { - size_t id; + size_t id_num; + const std::string* id_str; const std::string* text; } change; } value; diff --git a/src/ui/object/ChoiceList.cpp b/src/ui/object/ChoiceList.cpp index dbcb349d..211da985 100644 --- a/src/ui/object/ChoiceList.cpp +++ b/src/ui/object/ChoiceList.cpp @@ -3,7 +3,8 @@ namespace ui { namespace object { -ChoiceList::ChoiceList( const std::string& class_name ) +template< typename KEY_TYPE > +ChoiceList< KEY_TYPE >::ChoiceList( const std::string& class_name ) : UIContainer( class_name ) { SetEventContexts( EC_KEYBOARD ); @@ -12,14 +13,16 @@ ChoiceList::ChoiceList( const std::string& class_name ) m_item_align.height = 20; } -void ChoiceList::SetImmediateMode( const bool immediate_mode ) { +template< typename KEY_TYPE > +void ChoiceList< KEY_TYPE >::SetImmediateMode( const bool immediate_mode ) { if ( m_immediate_mode != immediate_mode ) { ASSERT( m_buttons.empty(), "can't change mode after initialization" ); m_immediate_mode = immediate_mode; } } -void ChoiceList::SetChoices( const choices_t& choices ) { +template< typename KEY_TYPE > +void ChoiceList< KEY_TYPE >::SetChoices( const choices_t& choices ) { bool wasSet = !m_values.empty(); std::string oldValue = ( wasSet && @@ -44,7 +47,8 @@ void ChoiceList::SetChoices( const choices_t& choices ) { } } -void ChoiceList::SetValue( const value_t value ) { +template< typename KEY_TYPE > +void ChoiceList< KEY_TYPE >::SetValue( const value_t value ) { auto it = m_buttons.find( value ); ASSERT( it != m_buttons.end(), "value does not exist in choices" ); SetActiveButton( it->second ); @@ -53,13 +57,15 @@ void ChoiceList::SetValue( const value_t value ) { m_value_index = value_index_it - m_values.begin(); } -const ChoiceList::value_t ChoiceList::GetValue() const { +template< typename KEY_TYPE > +const typename ChoiceList< KEY_TYPE >::value_t ChoiceList< KEY_TYPE >::GetValue() const { ASSERT( !m_values.empty(), "choices are empty" ); ASSERT( m_labels.find( m_value ) != m_labels.end(), "choices value not found" ); return m_value; } -void ChoiceList::SetValueString( const std::string& choice, bool allowMissing ) { +template< typename KEY_TYPE > +void ChoiceList< KEY_TYPE >::SetValueString( const std::string& choice, bool allowMissing ) { // ugh bool found = false; for ( const auto& label : m_labels ) { @@ -74,11 +80,13 @@ void ChoiceList::SetValueString( const std::string& choice, bool allowMissing ) } } -const std::string& ChoiceList::GetValueString() const { +template< typename KEY_TYPE > +const std::string& ChoiceList< KEY_TYPE >::GetValueString() const { return m_labels.at( GetValue() ); } -void ChoiceList::SetChoicesV( const std::vector< std::string >& labels ) { +template<> +void ChoiceList< size_t >::SetChoicesV( const std::vector< std::string >& labels ) { choices_t choices = {}; size_t index = 1; for ( auto& label : labels ) { @@ -91,8 +99,13 @@ void ChoiceList::SetChoicesV( const std::vector< std::string >& labels ) { } SetChoices( choices ); } +template<> +void ChoiceList< std::string >::SetChoicesV( const std::vector< std::string >& labels ) { + THROW( "SetChoicesV not implemented for AssocChoiceList" ); +} -void ChoiceList::Create() { +template< typename KEY_TYPE > +void ChoiceList< KEY_TYPE >::Create() { UIContainer::Create(); if ( m_buttons.empty() && !m_values.empty() ) { @@ -101,7 +114,8 @@ void ChoiceList::Create() { } -void ChoiceList::Destroy() { +template< typename KEY_TYPE > +void ChoiceList< KEY_TYPE >::Destroy() { for ( auto& button : m_buttons ) { RemoveChild( button.second ); @@ -111,7 +125,8 @@ void ChoiceList::Destroy() { UIContainer::Destroy(); } -void ChoiceList::Align() { +template< typename KEY_TYPE > +void ChoiceList< KEY_TYPE >::Align() { UIContainer::Align(); if ( !m_buttons.empty() ) { @@ -127,22 +142,25 @@ void ChoiceList::Align() { } -/*void ChoiceList::OnChange( UIEventHandler::handler_function_t func ) { +/*template< typename KEY_TYPE > +void ChoiceList< KEY_TYPE >::OnChange( UIEventHandler::handler_function_t func ) { On( UIEvent::EV_CHANGE, func ); }*/ - -void ChoiceList::SetItemMargin( const coord_t item_margin ) { +template< typename KEY_TYPE > +void ChoiceList< KEY_TYPE >::SetItemMargin( const coord_t item_margin ) { m_item_align.margin = item_margin; Realign(); } -void ChoiceList::SetItemHeight( const coord_t item_height ) { +template< typename KEY_TYPE > +void ChoiceList< KEY_TYPE >::SetItemHeight( const coord_t item_height ) { m_item_align.height = item_height; Realign(); } -void ChoiceList::ApplyStyle() { +template< typename KEY_TYPE > +void ChoiceList< KEY_TYPE >::ApplyStyle() { UIContainer::ApplyStyle(); if ( Has( Style::A_ITEM_MARGIN ) ) { @@ -154,7 +172,8 @@ void ChoiceList::ApplyStyle() { } } -void ChoiceList::UpdateButtons() { +template< typename KEY_TYPE > +void ChoiceList< KEY_TYPE >::UpdateButtons() { if ( m_created ) { for ( auto& button : m_buttons ) { RemoveChild( button.second ); @@ -203,7 +222,8 @@ void ChoiceList::UpdateButtons() { } } -bool ChoiceList::OnKeyDown( const UIEvent::event_data_t* data ) { +template< typename KEY_TYPE > +bool ChoiceList< KEY_TYPE >::OnKeyDown( const UIEvent::event_data_t* data ) { switch ( data->key.code ) { case UIEvent::K_DOWN: { if ( m_value_index < m_values.size() - 1 ) { @@ -230,15 +250,18 @@ bool ChoiceList::OnKeyDown( const UIEvent::event_data_t* data ) { return true; } -bool ChoiceList::OnKeyUp( const UIEvent::event_data_t* data ) { +template< typename KEY_TYPE > +bool ChoiceList< KEY_TYPE >::OnKeyUp( const UIEvent::event_data_t* data ) { return true; } -bool ChoiceList::OnKeyPress( const UIEvent::event_data_t* data ) { +template< typename KEY_TYPE > +bool ChoiceList< KEY_TYPE >::OnKeyPress( const UIEvent::event_data_t* data ) { return true; } -void ChoiceList::SetActiveButton( Button* button ) { +template< typename KEY_TYPE > +void ChoiceList< KEY_TYPE >::SetActiveButton( Button* button ) { auto it = m_button_values.find( button ); ASSERT( it != m_button_values.end(), "button not in buttons list" ); for ( auto& b : m_buttons ) { @@ -253,14 +276,26 @@ void ChoiceList::SetActiveButton( Button* button ) { m_value = it->second; } -void ChoiceList::SelectChoice() { - if ( m_value >= 0 ) { - UIEvent::event_data_t d = {}; - d.value.change.id = m_value; - d.value.change.text = &GetValueString(); - Trigger( UIEvent::EV_SELECT, &d ); - } +template<> +void ChoiceList< size_t >::SelectChoice() { + UIEvent::event_data_t d = {}; + d.value.change.id_num = m_value; + d.value.change.text = &GetValueString(); + Trigger( UIEvent::EV_SELECT, &d ); } +template<> +void ChoiceList< std::string >::SelectChoice() { + UIEvent::event_data_t d = {}; + d.value.change.id_str = &m_value; + d.value.change.text = &GetValueString(); + Trigger( UIEvent::EV_SELECT, &d ); +} + +template +class ChoiceList< size_t >; // NumChoiceList +template +class ChoiceList< std::string >; // AssocChoiceList + } } diff --git a/src/ui/object/ChoiceList.h b/src/ui/object/ChoiceList.h index 6a824217..747814a3 100644 --- a/src/ui/object/ChoiceList.h +++ b/src/ui/object/ChoiceList.h @@ -11,9 +11,10 @@ namespace ui { namespace object { +template< typename KEY_TYPE = size_t > CLASS( ChoiceList, UIContainer ) + typedef KEY_TYPE value_t; - typedef size_t value_t; typedef std::vector< std::pair< value_t, std::string > > choices_t; ChoiceList( const std::string& class_name = "" ); @@ -57,9 +58,9 @@ CLASS( ChoiceList, UIContainer ) std::map< value_t, std::string > m_labels = {}; const std::string m_empty_choice = ""; std::unordered_map< value_t, Button* > m_buttons = {}; - ssize_t m_value = -1; + value_t m_value = {}; size_t m_value_index = 0; - std::unordered_map< Button*, size_t > m_button_values = {}; + std::unordered_map< Button*, value_t > m_button_values = {}; void SetActiveButton( Button* button ); @@ -84,5 +85,8 @@ CLASS( ChoiceList, UIContainer ) void SelectChoice(); }; +typedef ChoiceList< size_t > NumChoiceList; +typedef ChoiceList< std::string > AssocChoiceList; + } /* namespace object */ } /* namespace ui */ diff --git a/src/ui/object/Dropdown.cpp b/src/ui/object/Dropdown.cpp index 43aa334d..74817f12 100644 --- a/src/ui/object/Dropdown.cpp +++ b/src/ui/object/Dropdown.cpp @@ -7,12 +7,14 @@ namespace ui { namespace object { -Dropdown::Dropdown( const std::string& class_name ) +template< typename KEY_TYPE > +Dropdown< KEY_TYPE >::Dropdown( const std::string& class_name ) : Panel( class_name ) { } -void Dropdown::SetChoices( const ChoiceList::choices_t& choices ) { +template< typename KEY_TYPE > +void Dropdown< KEY_TYPE >::SetChoices( const typename ChoiceList< KEY_TYPE >::choices_t& choices ) { m_choices = choices; if ( m_elements.choices ) { m_elements.choices->SetChoices( m_choices ); @@ -43,7 +45,8 @@ void Dropdown::SetChoices( const ChoiceList::choices_t& choices ) { } } -void Dropdown::SetValue( const ChoiceList::value_t value ) { +template< typename KEY_TYPE > +void Dropdown< KEY_TYPE >::SetValueByKey( const typename ChoiceList< KEY_TYPE >::value_t value ) { for ( const auto& it : m_choices ) { if ( it.first == value ) { SetValue( it.second ); @@ -52,7 +55,8 @@ void Dropdown::SetValue( const ChoiceList::value_t value ) { } } -void Dropdown::SetValue( const std::string& value ) { +template< typename KEY_TYPE > +void Dropdown< KEY_TYPE >::SetValue( const std::string& value ) { if ( m_mode == DM_SELECT ) { //ASSERT( std::find( m_choices.begin(), m_choices.end(), value ) != m_choices.end(), "value '" + value + "' not found in choices" ); } @@ -62,12 +66,14 @@ void Dropdown::SetValue( const std::string& value ) { } } -void Dropdown::SetMode( const dropdown_mode_t mode ) { +template< typename KEY_TYPE > +void Dropdown< KEY_TYPE >::SetMode( const dropdown_mode_t mode ) { m_mode = mode; } -void Dropdown::SetChoicesV( const std::vector< std::string >& labels ) { - ChoiceList::choices_t choices = {}; +template<> +void Dropdown< size_t >::SetChoicesV( const std::vector< std::string >& labels ) { + NumChoiceList::choices_t choices = {}; size_t index = 1; for ( auto& label : labels ) { choices.push_back( @@ -79,8 +85,13 @@ void Dropdown::SetChoicesV( const std::vector< std::string >& labels ) { } SetChoices( choices ); } +template<> +void Dropdown< std::string >::SetChoicesV( const std::vector< std::string >& labels ) { + THROW( "SetChoicesV not implemented for AssocDropdown" ); +} -void Dropdown::SetTextColor( const Color& color ) { +template< typename KEY_TYPE > +void Dropdown< KEY_TYPE >::SetTextColor( const Color& color ) { m_custom_text_color = true; m_text_color = color; if ( m_elements.value ) { @@ -88,7 +99,8 @@ void Dropdown::SetTextColor( const Color& color ) { } } -void Dropdown::Create() { +template< typename KEY_TYPE > +void Dropdown< KEY_TYPE >::Create() { Panel::Create(); NEW( m_elements.value, Label ); @@ -127,7 +139,7 @@ void Dropdown::Create() { } AddChild( m_elements.open_close ); - NEW( m_elements.choices, ChoiceList, GetStyleClass() + "Choices" ); + NEW( m_elements.choices, ChoiceList< KEY_TYPE >, GetStyleClass() + "Choices" ); m_elements.choices->Hide(); m_elements.choices->SetImmediateMode( true ); m_elements.choices->SetChoices( m_choices ); @@ -161,7 +173,8 @@ void Dropdown::Create() { } -void Dropdown::Destroy() { +template< typename KEY_TYPE > +void Dropdown< KEY_TYPE >::Destroy() { RemoveChild( m_elements.value ); RemoveChild( m_elements.open_close ); @@ -171,7 +184,8 @@ void Dropdown::Destroy() { Panel::Destroy(); } -void Dropdown::Align() { +template< typename KEY_TYPE > +void Dropdown< KEY_TYPE >::Align() { Panel::Align(); if ( m_elements.value ) { @@ -186,19 +200,27 @@ void Dropdown::Align() { } } -const bool Dropdown::IsExpanded() const { +template< typename KEY_TYPE > +const bool Dropdown< KEY_TYPE >::IsExpanded() const { return m_elements.open_close->HasStyleModifier( Style::M_ACTIVE ); } -void Dropdown::Expand() { +template< typename KEY_TYPE > +void Dropdown< KEY_TYPE >::Expand() { m_elements.open_close->AddStyleModifier( Style::M_ACTIVE ); m_elements.choices->Show(); } -void Dropdown::Collapse() { +template< typename KEY_TYPE > +void Dropdown< KEY_TYPE >::Collapse() { m_elements.choices->Hide(); m_elements.open_close->RemoveStyleModifier( Style::M_ACTIVE ); } +template +class Dropdown< size_t >; // NumDropdown +template +class Dropdown< std::string >; // AssocDropdown + } } diff --git a/src/ui/object/Dropdown.h b/src/ui/object/Dropdown.h index db3644f5..742ad525 100644 --- a/src/ui/object/Dropdown.h +++ b/src/ui/object/Dropdown.h @@ -11,6 +11,7 @@ namespace object { /* Extendable dropdown with selection */ +template< typename KEY_TYPE > CLASS( Dropdown, Panel ) enum dropdown_mode_t { @@ -20,8 +21,8 @@ CLASS( Dropdown, Panel ) Dropdown( const std::string& class_name ); - void SetChoices( const ChoiceList::choices_t& choices ); - void SetValue( const ChoiceList::value_t value ); + void SetChoices( const typename ChoiceList< KEY_TYPE >::choices_t& choices ); + void SetValueByKey( const typename ChoiceList< KEY_TYPE >::value_t value ); void SetValue( const std::string& value ); void SetMode( const dropdown_mode_t mode ); @@ -48,13 +49,16 @@ CLASS( Dropdown, Panel ) struct { Label* value = nullptr; SimpleButton* open_close = nullptr; - ChoiceList* choices = nullptr; + ChoiceList< KEY_TYPE >* choices = nullptr; } m_elements; - ChoiceList::choices_t m_choices = {}; + typename ChoiceList< KEY_TYPE >::choices_t m_choices = {}; std::string m_value = ""; }; +typedef Dropdown< size_t > NumDropdown; +typedef Dropdown< std::string > AssocDropdown; + } } From 7d8262f3fab5afd3a9a29a126f4b8f7c21cbcaea Mon Sep 17 00:00:00 2001 From: afwbkbc Date: Sat, 17 Feb 2024 15:42:37 +0200 Subject: [PATCH 04/21] players in gse api + randomization, refactoring --- gse/default/main.gls.js | 8 ++- src/game/Game.cpp | 61 +++++++++++++--- src/game/Slot.cpp | 28 ++++++++ src/game/Slot.h | 4 ++ src/game/State.cpp | 20 ++++-- src/game/State.h | 3 +- src/game/bindings/Binding.h | 2 + src/game/bindings/Bindings.cpp | 1 + src/game/bindings/Bindings.h | 1 + src/game/bindings/CMakeLists.txt | 1 + src/game/bindings/On.cpp | 1 + src/game/bindings/Players.cpp | 35 ++++++++++ src/game/connection/Client.cpp | 9 ++- src/game/connection/Connection.cpp | 2 +- src/game/connection/Server.cpp | 24 +++++-- src/game/connection/Server.h | 2 + src/game/map/Tile.cpp | 70 ++++++++++--------- src/game/rules/Faction.cpp | 21 ++++++ src/game/rules/Faction.h | 4 ++ src/game/unit/Def.cpp | 18 ++--- src/game/unit/Unit.cpp | 50 ++++++------- src/gse/Value.h | 6 +- src/gse/builtins/Console.cpp | 1 - src/gse/type/Object.cpp | 12 +++- src/gse/type/Object.h | 4 +- src/task/game/Game.cpp | 3 + src/task/mainmenu/menu/Main.cpp | 2 + src/task/mainmenu/menu/lobby/Lobby.cpp | 1 + .../mainmenu/menu/lobby/PlayersSectionRow.cpp | 10 ++- src/types/Color.cpp | 34 ++++----- 30 files changed, 318 insertions(+), 120 deletions(-) create mode 100644 src/game/bindings/Players.cpp diff --git a/gse/default/main.gls.js b/gse/default/main.gls.js index 255c30ad..e58a6a5e 100644 --- a/gse/default/main.gls.js +++ b/gse/default/main.gls.js @@ -1,9 +1,6 @@ #game.on.configure(() => { - #print('CONFIGURE'); - const factions = #include('factions'); - #print(#to_string(factions)); let i = 0; let sz = #size(factions); while (i < sz) { @@ -55,6 +52,11 @@ } }); +#game.on.turn(() => { + #print('NEW TURN'); + #print('PLAYERS: ' + #to_string(#game.players.get_all())); +}); + #game.on.spawn_unit((unit) => { let def = unit.get_def(); if (def.name != 'MindWorms') { diff --git a/src/game/Game.cpp b/src/game/Game.cpp index 025c3da3..5a445923 100644 --- a/src/game/Game.cpp +++ b/src/game/Game.cpp @@ -272,7 +272,7 @@ void Game::Iterate() { try { // start initial turn - NextTurn(); + NEW( m_current_turn, Turn, 0 ); // start main loop m_game_state = GS_RUNNING; @@ -297,7 +297,6 @@ void Game::Iterate() { m_state->m_bindings->Call( Bindings::CS_ON_START ); } - CheckTurnComplete(); } } else { @@ -306,7 +305,10 @@ void Game::Iterate() { } } else if ( m_game_state == GS_RUNNING ) { - // TODO: iterate GSE? + if ( !m_current_turn->GetId() ) { + // start first turn + NextTurn(); + } } } @@ -1010,7 +1012,7 @@ void Game::UnserializeUnits( types::Buffer& buf ) { } void Game::AddEvent( const Event& event ) { - Log( "Sending event (type=" + std::to_string( event.type ) + ")" ); + //Log( "Sending event (type=" + std::to_string( event.type ) + ")" ); // spammy m_pending_events->push_back( event ); } @@ -1029,6 +1031,41 @@ void Game::InitGame( MT_Response& response, MT_CANCELABLE ) { m_connection = m_state->GetConnection(); + if ( m_state->IsMaster() ) { + + // assign random factions to players + const auto& factions = m_state->m_settings.global.game_rules.m_factions; + std::vector< std::string > m_available_factions = {}; + const auto& slots = m_state->m_slots.GetSlots(); + for ( const auto& slot : slots ) { + if ( slot.GetState() == ::game::Slot::SS_PLAYER ) { + auto* player = slot.GetPlayer(); + ASSERT( player, "player not set" ); + if ( player->GetFaction().m_id == ::game::Player::RANDOM_FACTION.m_id ) { + if ( m_available_factions.empty() ) { + // (re)load factions list + for ( const auto& it : factions ) { + m_available_factions.push_back( it.first ); + } + ASSERT( !m_available_factions.empty(), "no factions found" ); + } + const std::vector< std::string >::iterator it = m_available_factions.begin() + m_random->GetUInt( 0, m_available_factions.size() - 1 ); + player->SetFaction( factions.at( *it ) ); + m_available_factions.erase( it ); + } + } + } + if ( m_connection ) { + m_connection->AsServer()->SendPlayersList(); + } + + } + else { + if ( m_connection ) { + + } + } + if ( m_connection ) { m_slot_num = m_connection->GetSlotNum(); @@ -1318,18 +1355,20 @@ void Game::ResetGame() { } void Game::NextTurn() { - size_t turn_id = 1; - if ( m_current_turn ) { - Log( "turn " + std::to_string( m_current_turn->GetId() ) + " finished" ); - - turn_id = m_current_turn->GetId() + 1; + ASSERT( m_current_turn, "turn not set" ); + const auto last_turn_id = m_current_turn->GetId(); + if ( last_turn_id ) { + Log( "turn " + std::to_string( m_current_turn->GetId() ) + " finished" ); // TODO: do something? - DELETE( m_current_turn ); } - NEW( m_current_turn, Turn, turn_id ); + DELETE( m_current_turn ); + + NEW( m_current_turn, Turn, last_turn_id + 1 ); Log( "turn " + std::to_string( m_current_turn->GetId() ) + " started" ); + + m_state->m_bindings->Call( Bindings::CS_ON_TURN ); } void Game::CheckTurnComplete() { diff --git a/src/game/Slot.cpp b/src/game/Slot.cpp index 2130145b..23848be6 100644 --- a/src/game/Slot.cpp +++ b/src/game/Slot.cpp @@ -2,6 +2,9 @@ #include "State.h" +#include "gse/type/String.h" +#include "gse/type/Int.h" + namespace game { Slot::Slot( const State* state ) @@ -144,4 +147,29 @@ void Slot::Unserialize( types::Buffer buf ) { m_linked_gsid = buf.ReadString(); } +WRAPIMPL_BEGIN( Slot, CLASS_PLAYER ) + ASSERT_NOLOG( m_slot_state == SS_PLAYER, "only player slots can be wrapped for now" ); + const auto* player = m_player_data.player; + WRAPIMPL_PROPS { + { + "id", + VALUE( gse::type::Int, m_player_data.cid ) + }, + { + "type", + VALUE( gse::type::String, "human" ) + }, + { + "name", + VALUE( gse::type::String, player->GetPlayerName() ) + }, + { + "faction", + player->GetFaction().Wrap() + }, + }; +WRAPIMPL_END_PTR( Slot ) + +UNWRAPIMPL_PTR( Slot ) + } diff --git a/src/game/Slot.h b/src/game/Slot.h index 0dfce03d..96dd511f 100644 --- a/src/game/Slot.h +++ b/src/game/Slot.h @@ -5,6 +5,8 @@ #include "game/Player.h" #include "network/Network.h" +#include "gse/Value.h" + namespace game { class State; @@ -50,6 +52,8 @@ CLASS( Slot, types::Serializable ) const types::Buffer Serialize() const override; void Unserialize( types::Buffer buf ) override; + WRAPDEFS_PTR( Slot ); + private: const State* m_state; diff --git a/src/game/State.cpp b/src/game/State.cpp index fb3c8fd1..1b3a20dd 100644 --- a/src/game/State.cpp +++ b/src/game/State.cpp @@ -2,14 +2,15 @@ namespace game { -State::State() - : m_bindings( new bindings::Bindings( this ) ) { - m_bindings->RunMain(); +State::State() { + } State::~State() { Reset(); - delete m_bindings; + if ( m_bindings ) { + delete m_bindings; + } } void State::SetGame( Game* game ) { @@ -91,7 +92,18 @@ connection::Connection* State::GetConnection() const { return m_connection; } +void State::InitBindings() { + if ( !m_bindings ) { + Log( "Initializing bindings" ); + m_bindings = new bindings::Bindings( this ); + m_bindings->RunMain(); + } +} + void State::Configure() { + ASSERT( m_bindings, "bindings not initialized" ); + + Log( "Configuring state" ); // reset m_settings.global.game_rules.m_factions.clear(); diff --git a/src/game/State.h b/src/game/State.h index 796ced04..393a6e7c 100644 --- a/src/game/State.h +++ b/src/game/State.h @@ -32,7 +32,7 @@ CLASS( State, base::Base ) Settings m_settings = {}; Slots m_slots = { this }; - bindings::Bindings* const m_bindings = nullptr; + bindings::Bindings* m_bindings = nullptr; std::function< void( gse::Exception& ) > m_on_gse_error = nullptr; @@ -52,6 +52,7 @@ CLASS( State, base::Base ) void SetConnection( connection::Connection* connection ); connection::Connection* GetConnection() const; + void InitBindings(); void Configure(); void Reset(); void DetachConnection(); diff --git a/src/game/bindings/Binding.h b/src/game/bindings/Binding.h index 2dfc59ff..4f8b74dd 100644 --- a/src/game/bindings/Binding.h +++ b/src/game/bindings/Binding.h @@ -74,6 +74,8 @@ BINDING_DEF( random ) BINDING_DEF( on ) +BINDING_DEF( players ) + BINDING_DEF( factions ) BINDING_DEF( units ) diff --git a/src/game/bindings/Bindings.cpp b/src/game/bindings/Bindings.cpp index f3825d3a..1189338b 100644 --- a/src/game/bindings/Bindings.cpp +++ b/src/game/bindings/Bindings.cpp @@ -27,6 +27,7 @@ Bindings::Bindings( State* state ) B( exit ), B( random ), B( on ), + B( players ), B( factions ), B( units ), B( map ), diff --git a/src/game/bindings/Bindings.h b/src/game/bindings/Bindings.h index 5ee7e2ef..70f81ae7 100644 --- a/src/game/bindings/Bindings.h +++ b/src/game/bindings/Bindings.h @@ -31,6 +31,7 @@ class Bindings : public gse::Bindings { enum callback_slot_t { CS_ON_CONFIGURE, CS_ON_START, + CS_ON_TURN, CS_ON_SPAWN_UNIT, CS_ON_DESPAWN_UNIT, }; diff --git a/src/game/bindings/CMakeLists.txt b/src/game/bindings/CMakeLists.txt index fedb1917..f4e0754e 100644 --- a/src/game/bindings/CMakeLists.txt +++ b/src/game/bindings/CMakeLists.txt @@ -6,6 +6,7 @@ SET( SRC ${SRC} ${PWD}/Exit.cpp ${PWD}/Random.cpp ${PWD}/On.cpp + ${PWD}/Players.cpp ${PWD}/Factions.cpp ${PWD}/Units.cpp ${PWD}/Map.cpp diff --git a/src/game/bindings/On.cpp b/src/game/bindings/On.cpp index 8d954836..1b3e9a3c 100644 --- a/src/game/bindings/On.cpp +++ b/src/game/bindings/On.cpp @@ -11,6 +11,7 @@ BINDING_IMPL( on ) { const gse::type::Object::properties_t properties = { ON( "configure", CS_ON_CONFIGURE ), ON( "start", CS_ON_START ), + ON( "turn", CS_ON_TURN ), ON( "spawn_unit", CS_ON_SPAWN_UNIT ), ON( "despawn_unit", CS_ON_DESPAWN_UNIT ), }; diff --git a/src/game/bindings/Players.cpp b/src/game/bindings/Players.cpp new file mode 100644 index 00000000..c2640ff7 --- /dev/null +++ b/src/game/bindings/Players.cpp @@ -0,0 +1,35 @@ +#include "Binding.h" + +#include "game/State.h" + +#include "gse/type/Array.h" +#include "gse/type/String.h" + +namespace game { +namespace bindings { + +BINDING_IMPL( players ) { + const gse::type::Object::properties_t properties = { + { + "get_all", + NATIVE_CALL( this ) { + N_EXPECT_ARGS( 0 ); + + const auto& slots = m_bindings->GetState()->m_slots.GetSlots(); + gse::type::Array::elements_t elements = {}; + for ( const auto& slot : slots ) { + const auto state = slot.GetState(); + if ( state == Slot::SS_OPEN || state == Slot::SS_CLOSED ) { + continue; // skip + } + elements.push_back( slot.Wrap() ); + } + return VALUE( gse::type::Array, elements ); + }) + }, + }; + return VALUE( gse::type::Object, properties ); +} + +} +} diff --git a/src/game/connection/Client.cpp b/src/game/connection/Client.cpp index d528cd06..042b54ae 100644 --- a/src/game/connection/Client.cpp +++ b/src/game/connection/Client.cpp @@ -36,7 +36,14 @@ void Client::ProcessEvent( const network::Event& event ) { } case Packet::PT_PLAYERS: { Log( "Got players list from server" ); - m_slot = packet.data.num; + if ( packet.data.num ) { + // initial players list + m_slot = packet.data.num; + } + else { + // players list update (i.e. after resolving random players ) + m_state->m_slots.Clear(); + } m_state->m_slots.Unserialize( packet.data.str ); for ( auto i = 0 ; i < m_state->m_slots.GetCount() ; i++ ) { const auto& slot = m_state->m_slots.GetSlot( i ); diff --git a/src/game/connection/Connection.cpp b/src/game/connection/Connection.cpp index 0833f645..edb1a488 100644 --- a/src/game/connection/Connection.cpp +++ b/src/game/connection/Connection.cpp @@ -52,7 +52,7 @@ void Connection::Connect() { m_game_state = GS_NONE; - Log( "connecting..." ); + Log( "Connecting..." ); m_mt_ids.connect = m_network->MT_Connect( m_connection_mode, m_settings->remote_address ); diff --git a/src/game/connection/Server.cpp b/src/game/connection/Server.cpp index 32247424..9c463dbd 100644 --- a/src/game/connection/Server.cpp +++ b/src/game/connection/Server.cpp @@ -192,13 +192,7 @@ void Server::ProcessEvent( const network::Event& event ) { slot.SetLinkedGSID( gsid ); } SendGameState( event.cid ); - { - Log( "Sending players list to " + std::to_string( event.cid ) ); - Packet p( Packet::PT_PLAYERS ); - p.data.num = slot_num; - p.data.str = m_state->m_slots.Serialize().ToString(); - g_engine->GetNetwork()->MT_SendPacket( p, event.cid ); - } + SendPlayersList( event.cid, slot_num ); SendGlobalSettings( event.cid ); SendSlotUpdate( slot_num, &slot, event.cid ); // notify others @@ -405,6 +399,14 @@ void Server::SetGameState( const game_state_t game_state ) { ); } +void Server::SendPlayersList() { + Broadcast( + [ this ]( const network::cid_t cid ) -> void { + SendPlayersList( cid ); + } + ); +} + void Server::UpdateSlot( const size_t slot_num, Slot* slot, const bool only_flags ) { if ( !only_flags ) { if ( m_on_slot_update ) { @@ -488,6 +490,14 @@ void Server::SendGameState( const network::cid_t cid ) { m_network->MT_SendPacket( p, cid ); } +void Server::SendPlayersList( const network::cid_t cid, const size_t slot_num ) { + Log( "Sending players list to " + std::to_string( cid ) ); + Packet p( Packet::PT_PLAYERS ); + p.data.num = slot_num; + p.data.str = m_state->m_slots.Serialize().ToString(); + g_engine->GetNetwork()->MT_SendPacket( p, cid ); +} + void Server::SendSlotUpdate( const size_t slot_num, const Slot* slot, network::cid_t skip_cid ) { Broadcast( [ this, slot_num, slot, skip_cid ]( const network::cid_t cid ) -> void { diff --git a/src/game/connection/Server.h b/src/game/connection/Server.h index ae7cf394..60d8a10c 100644 --- a/src/game/connection/Server.h +++ b/src/game/connection/Server.h @@ -26,6 +26,7 @@ CLASS( Server, Connection ) void BanFromSlot( const size_t slot_num, const std::string& reason = "Banned by host" ); void SetGameState( const game_state_t game_state ); + void SendPlayersList(); protected: void ProcessEvent( const network::Event& event ) override; @@ -38,6 +39,7 @@ CLASS( Server, Connection ) void Error( const network::cid_t cid, const std::string& reason ); void SendGlobalSettings( const network::cid_t cid ); void SendGameState( const network::cid_t cid ); + void SendPlayersList( const network::cid_t cid, const size_t slot_num = 0 ); void SendSlotUpdate( const size_t slot_num, const Slot* slot, network::cid_t skip_cid = 0 ); void SendFlagsUpdate( const size_t slot_num, const Slot* slot, network::cid_t skip_cid = 0 ); const std::string FormatChatMessage( const Player* player, const std::string& message ) const; diff --git a/src/game/map/Tile.cpp b/src/game/map/Tile.cpp index 568b76de..165b38c8 100644 --- a/src/game/map/Tile.cpp +++ b/src/game/map/Tile.cpp @@ -83,40 +83,42 @@ void Tile::Unserialize( Buffer buf ) { } WRAPIMPL_BEGIN( Tile, CLASS_TILE ) - { - "x", - VALUE( gse::type::Int, coord.x ) - }, - { - "y", - VALUE( gse::type::Int, coord.y ) - }, - { - "is_water", - VALUE( gse::type::Bool, is_water_tile ) - }, - { - "is_land", - VALUE( gse::type::Bool, !is_water_tile ) - }, - GETN( W ), - GETN( NW ), - GETN( N ), - GETN( NE ), - GETN( E ), - GETN( SE ), - GETN( S ), - GETN( SW ), - { - "get_units", - NATIVE_CALL( this ) { - gse::type::Object::properties_t result = {}; - for ( auto& it : units ) { - result.insert_or_assign( std::to_string( it.second->m_id ), it.second->Wrap() ); - } - return VALUE( gse::type::Object, result ); - } ) - } + WRAPIMPL_PROPS { + { + "x", + VALUE( gse::type::Int, coord.x ) + }, + { + "y", + VALUE( gse::type::Int, coord.y ) + }, + { + "is_water", + VALUE( gse::type::Bool, is_water_tile ) + }, + { + "is_land", + VALUE( gse::type::Bool, !is_water_tile ) + }, + GETN( W ), + GETN( NW ), + GETN( N ), + GETN( NE ), + GETN( E ), + GETN( SE ), + GETN( S ), + GETN( SW ), + { + "get_units", + NATIVE_CALL( this ) { + gse::type::Object::properties_t result = {}; + for ( auto& it : units ) { + result.insert_or_assign( std::to_string( it.second->m_id ), it.second->Wrap() ); + } + return VALUE( gse::type::Object, result ); + } ) + } + }; WRAPIMPL_END_PTR( Tile ) UNWRAPIMPL_PTR( Tile ) diff --git a/src/game/rules/Faction.cpp b/src/game/rules/Faction.cpp index af1d8c24..bcaf1bb1 100644 --- a/src/game/rules/Faction.cpp +++ b/src/game/rules/Faction.cpp @@ -1,5 +1,7 @@ #include "Faction.h" +#include "gse/type/String.h" + namespace game { namespace rules { @@ -32,5 +34,24 @@ void Faction::Unserialize( types::Buffer buf ) { } +WRAPIMPL_BEGIN( Faction, CLASS_FACTION ) + WRAPIMPL_PROPS { + { + "id", + VALUE( gse::type::String, m_id ) + }, + { + "name", + VALUE( gse::type::String, m_name ) + }, + { + "color", + m_color.Wrap() + }, + }; +WRAPIMPL_END_PTR( Faction ) + +UNWRAPIMPL_PTR( Faction ) + } } diff --git a/src/game/rules/Faction.h b/src/game/rules/Faction.h index 740abd42..a63995bc 100644 --- a/src/game/rules/Faction.h +++ b/src/game/rules/Faction.h @@ -6,6 +6,8 @@ #include "types/Color.h" +#include "gse/Value.h" + namespace game { namespace rules { @@ -20,6 +22,8 @@ CLASS( Faction, types::Serializable ) const types::Buffer Serialize() const override; void Unserialize( types::Buffer buf ) override; + + WRAPDEFS_PTR( Faction ) }; } diff --git a/src/game/unit/Def.cpp b/src/game/unit/Def.cpp index 48e3a535..e7d2bb6e 100644 --- a/src/game/unit/Def.cpp +++ b/src/game/unit/Def.cpp @@ -41,14 +41,16 @@ Def* Def::Unserialize( types::Buffer& buf ) { } WRAPIMPL_BEGIN( Def, CLASS_UNITDEF ) - { - "name", - VALUE( gse::type::String, m_name ) - }, - { - "type", - VALUE( gse::type::String, "static" ) // TODO - }, + WRAPIMPL_PROPS { + { + "name", + VALUE( gse::type::String, m_name ) + }, + { + "type", + VALUE( gse::type::String, "static" ) // TODO + }, + }; WRAPIMPL_END_PTR( Def ) UNWRAPIMPL_PTR( Def ) diff --git a/src/game/unit/Unit.cpp b/src/game/unit/Unit.cpp index 08fc0f38..0f73cb86 100644 --- a/src/game/unit/Unit.cpp +++ b/src/game/unit/Unit.cpp @@ -44,30 +44,32 @@ Unit* Unit::Unserialize( types::Buffer& buf ) { } WRAPIMPL_BEGIN( Unit, CLASS_UNIT ) - { - "id", - VALUE( gse::type::Int, m_id ) - }, - { - "x", - VALUE( gse::type::Int, m_pos_x ) - }, - { - "y", - VALUE( gse::type::Int, m_pos_y ) - }, - { - "get_def", - NATIVE_CALL( this ) { - return m_def->Wrap(); - }) - }, - { - "get_tile", - NATIVE_CALL( this ) { - return m_tile->Wrap(); - }) - } + WRAPIMPL_PROPS { + { + "id", + VALUE( gse::type::Int, m_id ) + }, + { + "x", + VALUE( gse::type::Int, m_pos_x ) + }, + { + "y", + VALUE( gse::type::Int, m_pos_y ) + }, + { + "get_def", + NATIVE_CALL( this ) { + return m_def->Wrap(); + }) + }, + { + "get_tile", + NATIVE_CALL( this ) { + return m_tile->Wrap(); + }) + } + }; WRAPIMPL_END_PTR( Unit ) UNWRAPIMPL_PTR( Unit ) diff --git a/src/gse/Value.h b/src/gse/Value.h index 4a08f7d8..7f0cd986 100644 --- a/src/gse/Value.h +++ b/src/gse/Value.h @@ -27,14 +27,12 @@ namespace gse { static _type Unwrap( const gse::Value& value ); #define WRAPIMPL_BEGIN( _type, _class ) \ const gse::type::Object::object_class_t _type::WRAP_CLASS = gse::type::Object::_class; \ -const gse::Value _type::Wrap() const { \ - const gse::type::Object::properties_t properties = { +const gse::Value _type::Wrap() const { +#define WRAPIMPL_PROPS const gse::type::Object::properties_t properties = #define WRAPIMPL_END_PTR( _type ) \ - }; \ return VALUE( gse::type::Object, properties, WRAP_CLASS, this ); \ } #define WRAPIMPL_END_NOPTR( _type ) \ - }; \ return VALUE( gse::type::Object, properties, WRAP_CLASS ); \ } diff --git a/src/gse/builtins/Console.cpp b/src/gse/builtins/Console.cpp index f8b1e0e8..7fe81b42 100644 --- a/src/gse/builtins/Console.cpp +++ b/src/gse/builtins/Console.cpp @@ -4,7 +4,6 @@ #include "gse/callable/Native.h" -#include "gse/type/Object.h" #include "gse/type/Undefined.h" #include "logger/Stdout.h" diff --git a/src/gse/type/Object.cpp b/src/gse/type/Object.cpp index e3c5fd38..b5a23aa6 100644 --- a/src/gse/type/Object.cpp +++ b/src/gse/type/Object.cpp @@ -30,13 +30,21 @@ static const std::unordered_map< Object::object_class_t, std::string > s_object_ "#tile" }, { - Object::CLASS_UNIT, - "#unit" + Object::CLASS_PLAYER, + "#player" + }, + { + Object::CLASS_FACTION, + "#faction" }, { Object::CLASS_UNITDEF, "#unitdef" }, + { + Object::CLASS_UNIT, + "#unit" + }, }; const std::string& Object::GetClassString( const object_class_t object_class ) { const auto& it = s_object_class_str.find( object_class ); diff --git a/src/gse/type/Object.h b/src/gse/type/Object.h index 8e535715..c921c609 100644 --- a/src/gse/type/Object.h +++ b/src/gse/type/Object.h @@ -20,8 +20,10 @@ class Object : public Type { CLASS_EXCEPTION, CLASS_COLOR, CLASS_TILE, - CLASS_UNIT, + CLASS_PLAYER, + CLASS_FACTION, CLASS_UNITDEF, + CLASS_UNIT, }; static const std::string& GetClassString( const object_class_t object_class ); diff --git a/src/task/game/Game.cpp b/src/task/game/Game.cpp index daf7440d..49bf172a 100644 --- a/src/task/game/Game.cpp +++ b/src/task/game/Game.cpp @@ -1,5 +1,7 @@ #include "Game.h" +#include + #include "engine/Engine.h" #include "../mainmenu/MainMenu.h" #include "graphics/Graphics.h" @@ -52,6 +54,7 @@ void Game::Start() { m_map_data.filename = util::FS::GetBaseName( m_state->m_settings.global.map.filename ); m_map_data.last_directory = util::FS::GetDirName( m_state->m_settings.global.map.filename ); } + } ui->ShowLoader( diff --git a/src/task/mainmenu/menu/Main.cpp b/src/task/mainmenu/menu/Main.cpp index b37f9e79..5e0c74bb 100644 --- a/src/task/mainmenu/menu/Main.cpp +++ b/src/task/mainmenu/menu/Main.cpp @@ -17,6 +17,7 @@ Main::Main( MainMenu* mainmenu ) "START GAME", { CH( this ) { + m_mainmenu->m_state->InitBindings(); m_mainmenu->m_state->Configure(); m_mainmenu->m_state->m_settings.local.game_mode = game::LocalSettings::GM_SINGLEPLAYER; @@ -30,6 +31,7 @@ Main::Main( MainMenu* mainmenu ) "QUICK START", { CH( this ) { + m_mainmenu->m_state->InitBindings(); m_mainmenu->m_state->Configure(); m_mainmenu->m_state->m_settings.local.game_mode = game::LocalSettings::GM_SINGLEPLAYER; diff --git a/src/task/mainmenu/menu/lobby/Lobby.cpp b/src/task/mainmenu/menu/lobby/Lobby.cpp index 20a7a2f0..1e6ef349 100644 --- a/src/task/mainmenu/menu/lobby/Lobby.cpp +++ b/src/task/mainmenu/menu/lobby/Lobby.cpp @@ -17,6 +17,7 @@ Lobby::Lobby( MainMenu* mainmenu, Connection* connection ) , m_state( mainmenu->m_state ) { ASSERT( connection, "connection is null" ); + m_state->InitBindings(); if ( m_state->IsMaster() ) { m_state->Configure(); } // slave will receive config from master diff --git a/src/task/mainmenu/menu/lobby/PlayersSectionRow.cpp b/src/task/mainmenu/menu/lobby/PlayersSectionRow.cpp index 20a9b3f8..9777b374 100644 --- a/src/task/mainmenu/menu/lobby/PlayersSectionRow.cpp +++ b/src/task/mainmenu/menu/lobby/PlayersSectionRow.cpp @@ -67,8 +67,14 @@ void PlayersSectionRow::Create() { m_elements.faction->SetWidth( 140 ); m_elements.faction->On( UIEvent::EV_CHANGE, EH( this, player, rules ) { - ASSERT( rules.m_factions.find( *data->value.change.id_str ) != rules.m_factions.end(), "faction not found: " + *data->value.change.id_str ); - player->SetFaction( rules.m_factions.at( *data->value.change.id_str ) ); + const auto& faction_id = *data->value.change.id_str; + if ( faction_id == ::game::Player::RANDOM_FACTION.m_id ) { + player->SetFaction( ::game::Player::RANDOM_FACTION ); + } + else { + ASSERT( rules.m_factions.find( faction_id ) != rules.m_factions.end(), "faction not found: " + faction_id ); + player->SetFaction( rules.m_factions.at( faction_id ) ); + } m_parent->GetLobby()->UpdateSlot( m_slot_num, m_slot ); return true; } diff --git a/src/types/Color.cpp b/src/types/Color.cpp index 6aad2db3..1f1e5d02 100644 --- a/src/types/Color.cpp +++ b/src/types/Color.cpp @@ -112,22 +112,24 @@ const std::string Color::ToString() const { } WRAPIMPL_BEGIN( Color, CLASS_COLOR ) - { - "r", - VALUE( gse::type::Float, value.red ) - }, - { - "g", - VALUE( gse::type::Float, value.green ) - }, - { - "b", - VALUE( gse::type::Float, value.blue ) - }, - { - "a", - VALUE( gse::type::Float, value.alpha ) - }, + WRAPIMPL_PROPS { + { + "r", + VALUE( gse::type::Float, value.red ) + }, + { + "g", + VALUE( gse::type::Float, value.green ) + }, + { + "b", + VALUE( gse::type::Float, value.blue ) + }, + { + "a", + VALUE( gse::type::Float, value.alpha ) + }, + }; WRAPIMPL_END_NOPTR( Color ) UNWRAPIMPL_NOPTR_BEGIN( Color ) From 45eae89464c1966b572199e02186eddb0edb0227 Mon Sep 17 00:00:00 2001 From: afwbkbc Date: Sat, 17 Feb 2024 23:21:41 +0200 Subject: [PATCH 05/21] assign owners to units --- gse/default/main.gls.js | 21 +++++++++++++++++---- src/game/Game.cpp | 9 ++++++--- src/game/Slot.cpp | 9 +++++++-- src/game/Slot.h | 6 ++++-- src/game/Slots.cpp | 9 +++++++-- src/game/bindings/Units.cpp | 7 ++++--- src/game/event/SpawnUnit.cpp | 16 +++++++++++----- src/game/event/SpawnUnit.h | 7 ++++--- src/game/map/Tile.cpp | 4 ++++ src/game/map/Tile.h | 2 ++ src/game/unit/Unit.cpp | 34 +++++++++++++++++++--------------- src/game/unit/Unit.h | 16 +++++++++++----- src/gse/callable/Native.h | 11 ++++++----- src/task/game/Game.cpp | 2 +- 14 files changed, 103 insertions(+), 50 deletions(-) diff --git a/gse/default/main.gls.js b/gse/default/main.gls.js index e58a6a5e..5525d99c 100644 --- a/gse/default/main.gls.js +++ b/gse/default/main.gls.js @@ -13,8 +13,19 @@ }); +// will be populated on start +let players = []; +let players_sz = 0; +let get_random_player = () => { + return players[(#game.random.get_int(0, players_sz - 1))]; +}; + #game.on.start(() => { + // init players + players = #game.players.get_all(); + players_sz = #size(players); + const units = #include('units'); let i = 0; let sz = #size(units); @@ -31,17 +42,18 @@ while (x < w) { if (x % 2 == y % 2) { if (#game.random.get_int(0, 1) == 1) { + let owner = get_random_player(); let tile = #game.map.get_tile(x, y); let unit = null; if (tile.is_land) { if (#game.random.get_int(0, 2) != 1) { - unit = #game.units.spawn('MindWorms', tile); + unit = #game.units.spawn('MindWorms', owner, tile); } else { - unit = #game.units.spawn('SporeLauncher', tile); + unit = #game.units.spawn('SporeLauncher', owner, tile); } } else { if (#game.random.get_int(0, 1) == 1) { - unit = #game.units.spawn('SeaLurk', tile); + unit = #game.units.spawn('SeaLurk', owner, tile); } } } @@ -78,7 +90,8 @@ }); #game.on.despawn_unit((unit) => { + #print(unit.get_owner()); if (unit.get_def() == 'SporeLauncher') { - #game.units.spawn('MindWorms', unit.get_tile()); + #game.units.spawn('MindWorms', get_random_player(), unit.get_tile()); } }); diff --git a/src/game/Game.cpp b/src/game/Game.cpp index 5a445923..713a266e 100644 --- a/src/game/Game.cpp +++ b/src/game/Game.cpp @@ -906,11 +906,11 @@ void Game::SpawnUnit( unit::Unit* unit ) { return; } - Log( "Spawning unit ('" + unit->m_def->m_name + "') at [ " + std::to_string( unit->m_pos_x ) + " " + std::to_string( unit->m_pos_y ) + " ]" ); + Log( "Spawning unit #" + std::to_string( unit->m_id ) + " ('" + unit->m_def->m_name + "') at " + unit->m_tile->ToString() ); ASSERT( m_units.find( unit->m_id ) == m_units.end(), "duplicate unit id" ); - auto* tile = m_map->GetTile( unit->m_pos_x, unit->m_pos_y ); + auto* tile = unit->m_tile; const auto* ts = m_map->GetTileState( tile ); ASSERT( m_units.find( unit->m_id ) == m_units.end(), "duplicate unit id" ); @@ -943,10 +943,13 @@ void Game::SpawnUnit( unit::Unit* unit ) { } void Game::DespawnUnit( const size_t unit_id ) { + const auto& it = m_units.find( unit_id ); ASSERT( it != m_units.end(), "unit id not found" ); auto* unit = it->second; + Log( "Despawning unit #" + std::to_string( unit->m_id ) + " ('" + unit->m_def->m_name + "') at " + unit->m_tile->ToString() ); + auto e = Event( Event::ET_UNIT_DESPAWN ); e.data.unit_despawn.unit_id = unit_id; AddEvent( e ); @@ -1005,7 +1008,7 @@ void Game::UnserializeUnits( types::Buffer& buf ) { for ( size_t i = 0 ; i < sz ; i++ ) { const auto unit_id = buf.ReadInt(); auto b = Buffer( buf.ReadString() ); - SpawnUnit( unit::Unit::Unserialize( b ) ); + SpawnUnit( unit::Unit::Unserialize( b, this ) ); } unit::Unit::SetNextId( buf.ReadInt() ); Log( "Restored next unit id: " + std::to_string( unit::Unit::GetNextId() ) ); diff --git a/src/game/Slot.cpp b/src/game/Slot.cpp index 23848be6..6bbe9a73 100644 --- a/src/game/Slot.cpp +++ b/src/game/Slot.cpp @@ -7,8 +7,9 @@ namespace game { -Slot::Slot( const State* state ) - : m_state( state ) { +Slot::Slot( const size_t index, const State* state ) + : m_index( index ) + , m_state( state ) { } @@ -36,6 +37,10 @@ Player* Slot::GetPlayer() const { return m_player_data.player; } +const size_t Slot::GetIndex() const { + return m_index; +} + const network::cid_t Slot::GetCid() const { ASSERT( m_slot_state == SS_PLAYER, "attempted to get cid from non-player slot" ); return m_player_data.cid; diff --git a/src/game/Slot.h b/src/game/Slot.h index 96dd511f..11e18ee7 100644 --- a/src/game/Slot.h +++ b/src/game/Slot.h @@ -13,7 +13,7 @@ class State; CLASS( Slot, types::Serializable ) - Slot( const State* state ); + Slot( const size_t index, const State* state ); enum slot_state_t { SS_CLOSED, @@ -32,6 +32,7 @@ CLASS( Slot, types::Serializable ) void Close(); void SetCloseAfterClear(); + const size_t GetIndex() const; const network::cid_t GetCid() const; const std::string& GetRemoteAddress() const; void SetPlayerFlag( const player_flag_t flag ); @@ -53,9 +54,10 @@ CLASS( Slot, types::Serializable ) void Unserialize( types::Buffer buf ) override; WRAPDEFS_PTR( Slot ); - + private: + size_t m_index; const State* m_state; slot_state_t m_slot_state = SS_OPEN; diff --git a/src/game/Slots.cpp b/src/game/Slots.cpp index 8059bc37..678cb686 100644 --- a/src/game/Slots.cpp +++ b/src/game/Slots.cpp @@ -15,8 +15,13 @@ void Slots::Resize( const size_t size ) { if ( m_slots.size() > size ) { m_slots.erase( m_slots.begin() + size, m_slots.end() ); } - while ( m_slots.size() < size ) { - m_slots.push_back( { m_state } ); + for ( size_t i = m_slots.size() ; i < size ; i++ ) { + m_slots.push_back( + { + i, + m_state + } + ); } } diff --git a/src/game/bindings/Units.cpp b/src/game/bindings/Units.cpp index d1eeaf73..59f7faef 100644 --- a/src/game/bindings/Units.cpp +++ b/src/game/bindings/Units.cpp @@ -53,10 +53,11 @@ BINDING_IMPL( units ) { { "spawn", NATIVE_CALL( this ) { - N_EXPECT_ARGS( 2 ); + N_EXPECT_ARGS( 3 ); N_GETVALUE( def_name, 0, String ); - N_UNWRAP( tile, 1, map::Tile ); - return GAME->AddGameEvent( new event::SpawnUnit( def_name, tile->coord.x, tile->coord.y ), ctx, call_si ); + N_UNWRAP( owner, 1, Slot ); + N_UNWRAP( tile, 2, map::Tile ); + return GAME->AddGameEvent( new event::SpawnUnit( def_name, owner->GetIndex(), tile->coord.x, tile->coord.y ), ctx, call_si ); }) }, { diff --git a/src/game/event/SpawnUnit.cpp b/src/game/event/SpawnUnit.cpp index e89a8de1..c350f36a 100644 --- a/src/game/event/SpawnUnit.cpp +++ b/src/game/event/SpawnUnit.cpp @@ -1,13 +1,15 @@ #include "SpawnUnit.h" -#include "../Game.h" +#include "game/Game.h" +#include "game/State.h" namespace game { namespace event { -SpawnUnit::SpawnUnit( const std::string& unit_def, const size_t pos_x, const size_t pos_y ) +SpawnUnit::SpawnUnit( const std::string& unit_def, const size_t owner_index, const size_t pos_x, const size_t pos_y ) : Event( ET_UNIT_SPAWN ) , m_unit_def( unit_def ) + , m_owner_index( owner_index ) , m_pos_x( pos_x ) , m_pos_y( pos_y ) { // @@ -16,11 +18,13 @@ SpawnUnit::SpawnUnit( const std::string& unit_def, const size_t pos_x, const siz const gse::Value SpawnUnit::Apply( game::Game* game ) const { const auto* def = game->GetUnitDef( m_unit_def ); ASSERT_NOLOG( def, "unit def '" + m_unit_def + "' not found" ); + auto& owner = game->GetState()->m_slots.GetSlot( m_owner_index ); + auto* tile = game->GetMap()->GetTile( m_pos_x, m_pos_y ); auto* unit = new unit::Unit( unit::Unit::GetNextId(), def, - m_pos_x, - m_pos_y + &owner, + tile ); game->SpawnUnit( unit @@ -30,15 +34,17 @@ const gse::Value SpawnUnit::Apply( game::Game* game ) const { void SpawnUnit::Serialize( types::Buffer& buf, const SpawnUnit* event ) { buf.WriteString( event->m_unit_def ); + buf.WriteInt( event->m_owner_index ); buf.WriteInt( event->m_pos_x ); buf.WriteInt( event->m_pos_y ); } SpawnUnit* SpawnUnit::Unserialize( types::Buffer& buf ) { const auto unit_def = buf.ReadString(); + const auto owner_index = buf.ReadInt(); const auto pos_x = buf.ReadInt(); const auto pos_y = buf.ReadInt(); - return new SpawnUnit( unit_def, pos_x, pos_y ); + return new SpawnUnit( unit_def, owner_index, pos_x, pos_y ); } } diff --git a/src/game/event/SpawnUnit.h b/src/game/event/SpawnUnit.h index 16e0cde2..b93af1ec 100644 --- a/src/game/event/SpawnUnit.h +++ b/src/game/event/SpawnUnit.h @@ -7,7 +7,7 @@ namespace event { class SpawnUnit : public Event { public: - SpawnUnit( const std::string& unit_def, const size_t pos_x, const size_t pos_y ); + SpawnUnit( const std::string& unit_def, const size_t owner_index, const size_t pos_x, const size_t pos_y ); const gse::Value Apply( game::Game* game ) const override; @@ -19,8 +19,9 @@ class SpawnUnit : public Event { private: std::string m_unit_def; - size_t m_pos_x; - size_t m_pos_y; + const size_t m_owner_index; + const size_t m_pos_x; + const size_t m_pos_y; }; } diff --git a/src/game/map/Tile.cpp b/src/game/map/Tile.cpp index 165b38c8..ffef5e60 100644 --- a/src/game/map/Tile.cpp +++ b/src/game/map/Tile.cpp @@ -76,6 +76,10 @@ void Tile::Unserialize( Buffer buf ) { Update(); } +const std::string Tile::ToString() const { + return "@[ " + std::to_string( coord.x ) + " " + std::to_string( coord.y ) + " ]"; +} + #define GETN( _n ) \ { \ "get_" #_n, \ diff --git a/src/game/map/Tile.h b/src/game/map/Tile.h index 5d49abe0..bb08f616 100644 --- a/src/game/map/Tile.h +++ b/src/game/map/Tile.h @@ -148,6 +148,8 @@ class Tile { const Buffer Serialize() const; void Unserialize( Buffer data ); + const std::string ToString() const; + WRAPDEFS_PTR( Tile ); }; diff --git a/src/game/unit/Unit.cpp b/src/game/unit/Unit.cpp index 0f73cb86..8d5a50bb 100644 --- a/src/game/unit/Unit.cpp +++ b/src/game/unit/Unit.cpp @@ -4,6 +4,9 @@ #include "gse/type/Int.h" #include "gse/callable/Native.h" +#include "game/Game.h" +#include "game/State.h" + namespace game { namespace unit { @@ -15,11 +18,11 @@ const void Unit::SetNextId( const size_t id ) { next_id = id; } -Unit::Unit( const size_t id, const Def* def, const size_t pos_x, const size_t pos_y ) +Unit::Unit( const size_t id, const Def* def, Slot* owner, map::Tile* tile ) : m_id( id ) , m_def( def ) - , m_pos_x( pos_x ) - , m_pos_y( pos_y ) { + , m_owner( owner ) + , m_tile( tile ) { if ( next_id <= id ) { next_id = id + 1; } @@ -29,18 +32,21 @@ const types::Buffer Unit::Serialize( const Unit* unit ) { types::Buffer buf; buf.WriteInt( unit->m_id ); buf.WriteString( Def::Serialize( unit->m_def ).ToString() ); - buf.WriteInt( unit->m_pos_x ); - buf.WriteInt( unit->m_pos_y ); + buf.WriteInt( unit->m_owner->GetIndex() ); + buf.WriteInt( unit->m_tile->coord.x ); + buf.WriteInt( unit->m_tile->coord.y ); return buf; } -Unit* Unit::Unserialize( types::Buffer& buf ) { +Unit* Unit::Unserialize( types::Buffer& buf, const Game* game ) { const auto id = buf.ReadInt(); auto defbuf = types::Buffer( buf.ReadString() ); const auto* def = Def::Unserialize( defbuf ); + auto* slot = game ? &game->GetState()->m_slots.GetSlot( buf.ReadInt() ) : nullptr; const auto pos_x = buf.ReadInt(); const auto pos_y = buf.ReadInt(); - return new Unit( id, def, pos_x, pos_y ); + auto* tile = game ? game->GetMap()->GetTile( pos_x, pos_y ) : nullptr; + return new Unit( id, def, slot, tile ); } WRAPIMPL_BEGIN( Unit, CLASS_UNIT ) @@ -49,20 +55,18 @@ WRAPIMPL_BEGIN( Unit, CLASS_UNIT ) "id", VALUE( gse::type::Int, m_id ) }, - { - "x", - VALUE( gse::type::Int, m_pos_x ) - }, - { - "y", - VALUE( gse::type::Int, m_pos_y ) - }, { "get_def", NATIVE_CALL( this ) { return m_def->Wrap(); }) }, + { + "get_owner", + NATIVE_CALL( this ) { + return m_owner->Wrap(); + }) + }, { "get_tile", NATIVE_CALL( this ) { diff --git a/src/game/unit/Unit.h b/src/game/unit/Unit.h index f5b5f8a8..a1a37ec1 100644 --- a/src/game/unit/Unit.h +++ b/src/game/unit/Unit.h @@ -6,7 +6,15 @@ #include "gse/Value.h" +#include "game/map/Tile.h" +#include "game/Slot.h" + namespace game { + +class Game; + +class Slot; + namespace unit { class Unit { @@ -15,18 +23,16 @@ class Unit { static const size_t GetNextId(); static const void SetNextId( const size_t id ); - Unit( const size_t id, const Def* def, const size_t pos_x, const size_t pos_y ); + Unit( const size_t id, const Def* def, Slot* owner, map::Tile* tile ); virtual ~Unit() = default; const size_t m_id; const Def* m_def; - - size_t m_pos_x; - size_t m_pos_y; + Slot* m_owner; map::Tile* m_tile = nullptr; static const types::Buffer Serialize( const Unit* unit ); - static Unit* Unserialize( types::Buffer& buf ); + static Unit* Unserialize( types::Buffer& buf, const Game* game ); WRAPDEFS_PTR( Unit ); }; diff --git a/src/gse/callable/Native.h b/src/gse/callable/Native.h index 9a13f3bb..8709b2f6 100644 --- a/src/gse/callable/Native.h +++ b/src/gse/callable/Native.h @@ -15,7 +15,8 @@ namespace callable { #define N_ARGS \ const gse::type::Type* arg; \ gse::type::Object::properties_t::const_iterator obj_it; \ - auto getprop_val = VALUE( gse::type::Undefined ); + auto getprop_val = VALUE( gse::type::Undefined ); \ + auto obj_val = VALUE( gse::type::Undefined ); #define N_EXPECT_ARGS( _count ) \ if ( arguments.size() != _count ) { \ throw gse::Exception( gse::EC.INVALID_CALL, "Expected " + std::to_string( _count ) + " arguments, found " + std::to_string( arguments.size() ), ctx, call_si ); \ @@ -41,17 +42,17 @@ namespace callable { arg = arguments.at( _index ).Get(); \ N_CHECKTYPE( arg, _index, _type ); \ const auto& _var = ((gse::type::_type*)arg)->value; -#define N_GETOBJ( _var, _index, _class ) \ +#define N_GETOBJ( _index, _class ) \ ASSERT_NOLOG( _index < arguments.size(), "argument index overflow" ); \ arg = arguments.at( _index ).Get(); \ N_CHECKTYPE( arg, _index, Object ); \ if ( ((gse::type::Object*)arg)->object_class != _class ) { \ throw gse::Exception( gse::EC.INVALID_CALL, "Argument " + std::to_string( _index ) + " is expected to be object of class " + gse::type::Object::GetClassString( _class ) + ", found class: " + gse::type::Object::GetClassString( ((gse::type::Object*)arg)->object_class ), ctx, call_si ); \ } \ - const auto& _var = arguments.at( _index ); + obj_val = arguments.at( _index ); #define N_UNWRAP( _var, _index, _type ) \ - N_GETOBJ( obj, _index, _type::WRAP_CLASS ); \ - const auto* _var = _type::Unwrap( obj ); + N_GETOBJ( _index, _type::WRAP_CLASS ); \ + const auto* _var = _type::Unwrap( obj_val ); #define N_CHECK_OBJECT_CLASS( _var, _class ) \ if ( ((gse::type::Object*)_var)->object_class != _class ) { \ throw gse::Exception( gse::EC.INVALID_CALL, "Value is expected to be object of class " + gse::type::Object::GetClassString( _class ) + ", found class: " + gse::type::Object::GetClassString( ((gse::type::Object*)_var)->object_class ), ctx, call_si ); \ diff --git a/src/task/game/Game.cpp b/src/task/game/Game.cpp index 49bf172a..969e56f1 100644 --- a/src/task/game/Game.cpp +++ b/src/task/game/Game.cpp @@ -913,7 +913,7 @@ void Game::ProcessEvent( const ::game::Event& event ) { } case ::game::Event::ET_UNIT_SPAWN: { types::Buffer buf( *event.data.unit_spawn.serialized_unit ); - const auto* unit = ::game::unit::Unit::Unserialize( buf ); + const auto* unit = ::game::unit::Unit::Unserialize( buf, nullptr ); const auto c = event.data.unit_spawn.coords; SpawnUnit( unit, c.x, c.y, c.z ); delete unit->m_def; From 0c48b8a89a53c284d825fa0280e77b9ba8946032 Mon Sep 17 00:00:00 2001 From: afwbkbc Date: Sat, 17 Feb 2024 23:24:49 +0200 Subject: [PATCH 06/21] fixed quickstart --- gse/default/main.gls.js | 1 - src/main.cpp | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/gse/default/main.gls.js b/gse/default/main.gls.js index 5525d99c..d2c51247 100644 --- a/gse/default/main.gls.js +++ b/gse/default/main.gls.js @@ -90,7 +90,6 @@ let get_random_player = () => { }); #game.on.despawn_unit((unit) => { - #print(unit.get_owner()); if (unit.get_def() == 'SporeLauncher') { #game.units.spawn('MindWorms', get_random_player(), unit.get_tile()); } diff --git a/src/main.cpp b/src/main.cpp index 8498b642..0944aa7c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -275,6 +275,8 @@ int main( const int argc, const char* argv[] ) { if ( config.HasDebugFlag( config::Config::DF_QUICKSTART ) ) { NEWV( state, game::State ); // TODO: initialize settings randomly state->m_settings.global.game_rules.Initialize(); + state->InitBindings(); + state->Configure(); const auto& rules = state->m_settings.global.game_rules; NEWV( player, ::game::Player, From e09dd751f89107d7f7304e5da12cc5f95faf4268 Mon Sep 17 00:00:00 2001 From: afwbkbc Date: Sat, 17 Feb 2024 23:53:46 +0200 Subject: [PATCH 07/21] optimized unit spawn event --- src/game/Event.cpp | 13 +++- src/game/Event.h | 15 ++-- src/game/Game.cpp | 8 ++- src/task/game/Game.cpp | 154 ++++++++++++++++++++++------------------- src/task/game/Game.h | 24 ++++--- 5 files changed, 124 insertions(+), 90 deletions(-) diff --git a/src/game/Event.cpp b/src/game/Event.cpp index fbecf463..f36ee2fd 100644 --- a/src/game/Event.cpp +++ b/src/game/Event.cpp @@ -31,8 +31,13 @@ Event::Event( const Event& other ) data.turn_complete_status.is_turn_complete = other.data.turn_complete_status.is_turn_complete; break; } + case ET_UNIT_DEFINE: { + NEW( data.unit_define.serialized_unitdef, std::string, *other.data.unit_define.serialized_unitdef ); + break; + } case ET_UNIT_SPAWN: { - NEW( data.unit_spawn.serialized_unit, std::string, *other.data.unit_spawn.serialized_unit ); + data.unit_spawn.unit_id = other.data.unit_spawn.unit_id; + NEW( data.unit_spawn.unitdef_name, std::string, *other.data.unit_spawn.unitdef_name ); data.unit_spawn.coords = other.data.unit_spawn.coords; break; } @@ -61,8 +66,12 @@ Event::~Event() { DELETE( data.global_message.message ); break; } + case ET_UNIT_DEFINE: { + DELETE( data.unit_define.serialized_unitdef ); + break; + } case ET_UNIT_SPAWN: { - DELETE( data.unit_spawn.serialized_unit ); + DELETE( data.unit_spawn.unitdef_name ); break; } default: { diff --git a/src/game/Event.h b/src/game/Event.h index 9fb841e9..b75abb73 100644 --- a/src/game/Event.h +++ b/src/game/Event.h @@ -16,6 +16,7 @@ class Event { ET_ERROR, ET_GLOBAL_MESSAGE, ET_TURN_COMPLETE_STATUS, + ET_UNIT_DEFINE, ET_UNIT_SPAWN, ET_UNIT_DESPAWN, }; @@ -27,20 +28,24 @@ class Event { union { struct { - std::string* reason; + const std::string* reason; } quit; struct { - std::string* what; - std::string* stacktrace; + const std::string* what; + const std::string* stacktrace; } error; struct { - std::string* message; + const std::string* message; } global_message; struct { bool is_turn_complete; } turn_complete_status; struct { - std::string* serialized_unit; + const std::string* serialized_unitdef; + } unit_define; + struct { + size_t unit_id; + const std::string* unitdef_name; struct { float x; float y; diff --git a/src/game/Game.cpp b/src/game/Game.cpp index 713a266e..b2a183a9 100644 --- a/src/game/Game.cpp +++ b/src/game/Game.cpp @@ -898,6 +898,11 @@ void Game::DefineUnit( const unit::Def* def ) { ASSERT( m_unit_defs.find( def->m_name ) == m_unit_defs.end(), "Unit definition '" + def->m_name + "' already exists" ); m_unit_defs.insert_or_assign( def->m_name, def ); + + // notify frontend + auto e = Event( Event::ET_UNIT_DEFINE ); + NEW( e.data.unit_define.serialized_unitdef, std::string, unit::Def::Serialize( def ).ToString() ); + AddEvent( e ); } void Game::SpawnUnit( unit::Unit* unit ) { @@ -921,7 +926,8 @@ void Game::SpawnUnit( unit::Unit* unit ) { // notify frontend auto e = Event( Event::ET_UNIT_SPAWN ); - NEW( e.data.unit_spawn.serialized_unit, std::string, unit::Unit::Serialize( unit ).ToString() ); + e.data.unit_spawn.unit_id = unit->m_id; + NEW( e.data.unit_spawn.unitdef_name, std::string, unit->m_def->m_name ); const auto l = tile->is_water_tile ? map::TileState::LAYER_WATER : map::TileState::LAYER_LAND; diff --git a/src/task/game/Game.cpp b/src/task/game/Game.cpp index 969e56f1..44460c77 100644 --- a/src/task/game/Game.cpp +++ b/src/task/game/Game.cpp @@ -777,78 +777,82 @@ const ::game::map_editor::MapEditor::brush_type_t Game::GetEditorBrush() const { return m_editor_brush; } -void Game::SpawnUnit( const ::game::unit::Unit* unit, const float x, const float y, const float z ) { - - // unitdef - auto unitdef_it = m_unitdef_states.find( unit->m_def->m_name ); - if ( unitdef_it == m_unitdef_states.end() ) { - Log( "Initializing unitdef state: " + unit->m_def->m_name ); - unitdef_state_t unitdef_state = {}; - switch ( unit->m_def->m_type ) { - case ::game::unit::Def::DT_STATIC: { - const auto* def = (::game::unit::StaticDef*)unit->m_def; - switch ( def->m_render->m_type ) { - - case ::game::unit::Render::RT_SPRITE: { - const auto* render = (::game::unit::SpriteRender*)def->m_render; - - unitdef_state.render.is_sprite = true; - unitdef_state.render.sprite.instanced_sprite = &GetInstancedSprite( - "Unit_" + def->m_name, render->m_file, { - render->m_x, - render->m_y, - }, - { - render->m_w, - render->m_h, - }, - { - render->m_cx, - render->m_cy, - }, - 0.5f - ); +void Game::DefineUnit( const ::game::unit::Def* unitdef ) { + auto unitdef_it = m_unitdef_states.find( unitdef->m_name ); + ASSERT( unitdef_it == m_unitdef_states.end(), "unit def already exists" ); - break; - } - default: - THROW( "unknown unit render type: " + std::to_string( def->m_render->m_type ) ); + Log( "Initializing unitdef state: " + unitdef->m_name ); + unitdef_state_t unitdef_state = {}; + unitdef_state.m_type = unitdef->m_type; + + switch ( unitdef->m_type ) { + case ::game::unit::Def::DT_STATIC: { + const auto* def = (::game::unit::StaticDef*)unitdef; + + switch ( def->m_render->m_type ) { + + case ::game::unit::Render::RT_SPRITE: { + const auto* render = (::game::unit::SpriteRender*)def->m_render; + + unitdef_state.static_.render.is_sprite = true; + unitdef_state.static_.render.sprite.instanced_sprite = &GetInstancedSprite( + "Unit_" + def->m_name, render->m_file, { + render->m_x, + render->m_y, + }, + { + render->m_w, + render->m_h, + }, + { + render->m_cx, + render->m_cy, + }, + 0.5f + ); + + break; } - break; + default: + THROW( "unknown unit render type: " + std::to_string( def->m_render->m_type ) ); } - default: - THROW( "unknown unit def type: " + std::to_string( unit->m_def->m_type ) ); + break; } - unitdef_it = m_unitdef_states.insert( - { - unit->m_def->m_name, - unitdef_state - } - ).first; + default: + THROW( "unknown unit def type: " + std::to_string( unitdef->m_type ) ); } - auto& unitdef_state = unitdef_it->second; + m_unitdef_states.insert( + { + unitdef->m_name, + unitdef_state + } + ); +} + +void Game::SpawnUnit( const size_t unit_id, const std::string& unitdef_name, const float x, const float y, const float z ) { - // unit - ASSERT( m_unit_states.find( unit->m_id ) == m_unit_states.end(), "unit id already exists" ); + ASSERT( m_unitdef_states.find( unitdef_name ) != m_unitdef_states.end(), "unitdef not found" ); + ASSERT( m_unit_states.find( unit_id ) == m_unit_states.end(), "unit id already exists" ); + + auto& unitdef_state = m_unitdef_states.at( unitdef_name ); unit_state_t unit_state = { &unitdef_state, }; - if ( unitdef_state.render.is_sprite ) { - unit_state.instance_id = unitdef_state.render.sprite.next_instance_id++; - unitdef_state.render.sprite.instanced_sprite->actor->SetInstance( - unit_state.instance_id, { - x, - y, - z - } - ); - } - else { - THROW( "only sprite units are supported for now" ); - } + + ASSERT( unitdef_state.m_type == ::game::unit::Def::DT_STATIC, "only static unitdefs are supported for now" ); + ASSERT( unitdef_state.static_.render.is_sprite, "only sprite unitdefs are supported for now" ); + + unit_state.instance_id = unitdef_state.static_.render.sprite.next_instance_id++; + unitdef_state.static_.render.sprite.instanced_sprite->actor->SetInstance( + unit_state.instance_id, { + x, + y, + z + } + ); m_unit_states.insert( { - unit->m_id, + unit_id, unit_state } ); @@ -857,14 +861,14 @@ void Game::SpawnUnit( const ::game::unit::Unit* unit, const float x, const float void Game::DespawnUnit( const size_t unit_id ) { const auto& it = m_unit_states.find( unit_id ); ASSERT( it != m_unit_states.end(), "unit id not found" ); + const auto& unit_state = it->second; const auto& unitdef_state = unit_state.def; - if ( unitdef_state->render.is_sprite ) { - unitdef_state->render.sprite.instanced_sprite->actor->RemoveInstance( unit_state.instance_id ); - } - else { - THROW( "only sprite units are supported for now" ); - } + + ASSERT( unitdef_state->m_type == ::game::unit::Def::DT_STATIC, "only static unitdefs are supported for now" ); + ASSERT( unitdef_state->static_.render.is_sprite, "only sprite unitdefs are supported for now" ); + + unitdef_state->static_.render.sprite.instanced_sprite->actor->RemoveInstance( unit_state.instance_id ); m_unit_states.erase( it ); } @@ -911,13 +915,17 @@ void Game::ProcessEvent( const ::game::Event& event ) { m_ui.bottom_bar->SetTurnCompleteStatus( event.data.turn_complete_status.is_turn_complete ); break; } + case ::game::Event::ET_UNIT_DEFINE: { + types::Buffer buf( *event.data.unit_define.serialized_unitdef ); + const auto* unitdef = ::game::unit::Def::Unserialize( buf ); + DefineUnit( unitdef ); + delete unitdef; + break; + } case ::game::Event::ET_UNIT_SPAWN: { - types::Buffer buf( *event.data.unit_spawn.serialized_unit ); - const auto* unit = ::game::unit::Unit::Unserialize( buf, nullptr ); - const auto c = event.data.unit_spawn.coords; - SpawnUnit( unit, c.x, c.y, c.z ); - delete unit->m_def; - delete unit; + const auto& d = event.data.unit_spawn; + const auto& c = d.coords; + SpawnUnit( d.unit_id, *d.unitdef_name, c.x, c.y, c.z ); break; } case ::game::Event::ET_UNIT_DESPAWN: { diff --git a/src/task/game/Game.h b/src/task/game/Game.h index a373b77d..ecf0d449 100644 --- a/src/task/game/Game.h +++ b/src/task/game/Game.h @@ -150,7 +150,8 @@ CLASS( Game, base::Task ) void UpdateMapData( const types::Vec2< size_t >& map_size ); - void SpawnUnit( const ::game::unit::Unit* unit, const float x, const float y, const float z ); + void DefineUnit( const ::game::unit::Def* unitdef ); + void SpawnUnit( const size_t unit_id, const std::string& unitdef_name, const float x, const float y, const float z ); void DespawnUnit( const size_t unit_id ); void ProcessEvent( const ::game::Event& event ); @@ -349,15 +350,20 @@ CLASS( Game, base::Task ) } m_mt_ids = {}; struct unitdef_state_t { - struct { - bool is_sprite; - union { + ::game::unit::Def::def_type_t m_type; + union { + struct { struct { - Game::instanced_sprite_t* instanced_sprite = nullptr; - size_t next_instance_id = 1; - } sprite; - }; - } render; + bool is_sprite; + union { + struct { + Game::instanced_sprite_t* instanced_sprite = nullptr; + size_t next_instance_id = 1; + } sprite; + }; + } render; + } static_; + }; }; std::unordered_map< std::string, unitdef_state_t > m_unitdef_states = {}; From c5c878e7d25aed6d57e68be7d699211a88430595 Mon Sep 17 00:00:00 2001 From: afwbkbc Date: Sun, 18 Feb 2024 00:34:51 +0200 Subject: [PATCH 08/21] pushing slot info to frontend --- src/game/Event.cpp | 9 +++++++++ src/game/Event.h | 19 ++++++++++++++++++- src/game/Game.cpp | 34 +++++++++++++++++++++++++++++----- src/task/game/Game.cpp | 27 +++++++++++++++++++++++++-- src/task/game/Game.h | 9 ++++++++- 5 files changed, 89 insertions(+), 9 deletions(-) diff --git a/src/game/Event.cpp b/src/game/Event.cpp index f36ee2fd..c44f5f50 100644 --- a/src/game/Event.cpp +++ b/src/game/Event.cpp @@ -31,6 +31,10 @@ Event::Event( const Event& other ) data.turn_complete_status.is_turn_complete = other.data.turn_complete_status.is_turn_complete; break; } + case ET_SLOT_DEFINE: { + NEW( data.slot_define.slotdefs, slot_defines_t, *other.data.slot_define.slotdefs ); + break; + } case ET_UNIT_DEFINE: { NEW( data.unit_define.serialized_unitdef, std::string, *other.data.unit_define.serialized_unitdef ); break; @@ -38,6 +42,7 @@ Event::Event( const Event& other ) case ET_UNIT_SPAWN: { data.unit_spawn.unit_id = other.data.unit_spawn.unit_id; NEW( data.unit_spawn.unitdef_name, std::string, *other.data.unit_spawn.unitdef_name ); + data.unit_spawn.slot_index = other.data.unit_spawn.slot_index; data.unit_spawn.coords = other.data.unit_spawn.coords; break; } @@ -66,6 +71,10 @@ Event::~Event() { DELETE( data.global_message.message ); break; } + case ET_SLOT_DEFINE: { + DELETE( data.slot_define.slotdefs ); + break; + } case ET_UNIT_DEFINE: { DELETE( data.unit_define.serialized_unitdef ); break; diff --git a/src/game/Event.h b/src/game/Event.h index b75abb73..058e194e 100644 --- a/src/game/Event.h +++ b/src/game/Event.h @@ -16,6 +16,7 @@ class Event { ET_ERROR, ET_GLOBAL_MESSAGE, ET_TURN_COMPLETE_STATUS, + ET_SLOT_DEFINE, ET_UNIT_DEFINE, ET_UNIT_SPAWN, ET_UNIT_DESPAWN, @@ -26,6 +27,18 @@ class Event { const event_type_t type = ET_NONE; + struct slot_define_t { + size_t slot_index; + // TODO: name etc + struct { + float r; + float g; + float b; + float a; + } color; + }; + typedef std::vector< slot_define_t > slot_defines_t; + union { struct { const std::string* reason; @@ -41,11 +54,15 @@ class Event { bool is_turn_complete; } turn_complete_status; struct { - const std::string* serialized_unitdef; + slot_defines_t* slotdefs; + } slot_define; + struct { + const std::string* serialized_unitdef; // can be optimized } unit_define; struct { size_t unit_id; const std::string* unitdef_name; + size_t slot_index; struct { float x; float y; diff --git a/src/game/Game.cpp b/src/game/Game.cpp index b2a183a9..e4a0a016 100644 --- a/src/game/Game.cpp +++ b/src/game/Game.cpp @@ -268,6 +268,34 @@ void Game::Iterate() { m_connection->UpdateSlot( m_slot_num, m_slot, true ); } + // notify frontend + const auto& slots = m_state->m_slots.GetSlots(); + auto* slot_defines = new Event::slot_defines_t(); + for ( const auto& slot : slots ) { + if ( slot.GetState() == Slot::SS_OPEN || slot.GetState() == Slot::SS_CLOSED ) { + continue; + } + ASSERT( slot.GetState() == Slot::SS_PLAYER, "unknown slot state: " + std::to_string( slot.GetState() ) ); + const auto* player = slot.GetPlayer(); + ASSERT( player, "slot player not set" ); + const auto& faction = player->GetFaction(); + const auto& c = faction.m_color.value; + slot_defines->push_back( + Event::slot_define_t{ + slot.GetIndex(), + { + c.red, + c.green, + c.blue, + c.alpha + } + } + ); + } + auto e = Event( Event::ET_SLOT_DEFINE ); + e.data.slot_define.slotdefs = slot_defines; + AddEvent( e ); + ASSERT( !m_current_turn, "turn is already initialized" ); try { @@ -928,6 +956,7 @@ void Game::SpawnUnit( unit::Unit* unit ) { auto e = Event( Event::ET_UNIT_SPAWN ); e.data.unit_spawn.unit_id = unit->m_id; NEW( e.data.unit_spawn.unitdef_name, std::string, unit->m_def->m_name ); + e.data.unit_spawn.slot_index = unit->m_owner->GetIndex(); const auto l = tile->is_water_tile ? map::TileState::LAYER_WATER : map::TileState::LAYER_LAND; @@ -1069,11 +1098,6 @@ void Game::InitGame( MT_Response& response, MT_CANCELABLE ) { } } - else { - if ( m_connection ) { - - } - } if ( m_connection ) { diff --git a/src/task/game/Game.cpp b/src/task/game/Game.cpp index 44460c77..47770646 100644 --- a/src/task/game/Game.cpp +++ b/src/task/game/Game.cpp @@ -777,6 +777,20 @@ const ::game::map_editor::MapEditor::brush_type_t Game::GetEditorBrush() const { return m_editor_brush; } +void Game::DefineSlot( const size_t slot_index, const types::Color& color ) { + auto slot_it = m_slot_states.find( slot_index ); + if ( slot_it == m_slot_states.end() ) { + Log( "Initializing slot state: " + std::to_string( slot_index ) ); + slot_it = m_slot_states.insert( + { + slot_index, + {} + } + ).first; + } + slot_it->second.color = color; +} + void Game::DefineUnit( const ::game::unit::Def* unitdef ) { auto unitdef_it = m_unitdef_states.find( unitdef->m_name ); ASSERT( unitdef_it == m_unitdef_states.end(), "unit def already exists" ); @@ -829,14 +843,16 @@ void Game::DefineUnit( const ::game::unit::Def* unitdef ) { ); } -void Game::SpawnUnit( const size_t unit_id, const std::string& unitdef_name, const float x, const float y, const float z ) { +void Game::SpawnUnit( const size_t unit_id, const std::string& unitdef_name, const size_t slot_index, const float x, const float y, const float z ) { ASSERT( m_unitdef_states.find( unitdef_name ) != m_unitdef_states.end(), "unitdef not found" ); + ASSERT( m_slot_states.find( slot_index ) != m_slot_states.end(), "slot not found" ); ASSERT( m_unit_states.find( unit_id ) == m_unit_states.end(), "unit id already exists" ); auto& unitdef_state = m_unitdef_states.at( unitdef_name ); unit_state_t unit_state = { &unitdef_state, + &m_slot_states.at( slot_index ) }; ASSERT( unitdef_state.m_type == ::game::unit::Def::DT_STATIC, "only static unitdefs are supported for now" ); @@ -915,6 +931,13 @@ void Game::ProcessEvent( const ::game::Event& event ) { m_ui.bottom_bar->SetTurnCompleteStatus( event.data.turn_complete_status.is_turn_complete ); break; } + case ::game::Event::ET_SLOT_DEFINE: { + for ( const auto& d : *event.data.slot_define.slotdefs ) { + const auto& c = d.color; + DefineSlot( d.slot_index, types::Color( c.r, c.g, c.b, c.a ) ); + } + break; + } case ::game::Event::ET_UNIT_DEFINE: { types::Buffer buf( *event.data.unit_define.serialized_unitdef ); const auto* unitdef = ::game::unit::Def::Unserialize( buf ); @@ -925,7 +948,7 @@ void Game::ProcessEvent( const ::game::Event& event ) { case ::game::Event::ET_UNIT_SPAWN: { const auto& d = event.data.unit_spawn; const auto& c = d.coords; - SpawnUnit( d.unit_id, *d.unitdef_name, c.x, c.y, c.z ); + SpawnUnit( d.unit_id, *d.unitdef_name, d.slot_index, c.x, c.y, c.z ); break; } case ::game::Event::ET_UNIT_DESPAWN: { diff --git a/src/task/game/Game.h b/src/task/game/Game.h index ecf0d449..9bb40880 100644 --- a/src/task/game/Game.h +++ b/src/task/game/Game.h @@ -150,8 +150,9 @@ CLASS( Game, base::Task ) void UpdateMapData( const types::Vec2< size_t >& map_size ); + void DefineSlot( const size_t slot_index, const types::Color& color ); void DefineUnit( const ::game::unit::Def* unitdef ); - void SpawnUnit( const size_t unit_id, const std::string& unitdef_name, const float x, const float y, const float z ); + void SpawnUnit( const size_t unit_id, const std::string& unitdef_name, const size_t slot_index, const float x, const float y, const float z ); void DespawnUnit( const size_t unit_id ); void ProcessEvent( const ::game::Event& event ); @@ -349,6 +350,11 @@ CLASS( Game, base::Task ) #endif } m_mt_ids = {}; + struct slot_state_t { + types::Color color; + }; + std::unordered_map< size_t, slot_state_t > m_slot_states = {}; + struct unitdef_state_t { ::game::unit::Def::def_type_t m_type; union { @@ -369,6 +375,7 @@ CLASS( Game, base::Task ) struct unit_state_t { unitdef_state_t* def = nullptr; + slot_state_t* slot = nullptr; size_t instance_id = 0; }; std::unordered_map< size_t, unit_state_t > m_unit_states = {}; From a283136738344066c66ff2d006f5b2060d52325b Mon Sep 17 00:00:00 2001 From: afwbkbc Date: Sun, 18 Feb 2024 01:43:24 +0200 Subject: [PATCH 09/21] basic unit badges --- src/game/map/Consts.h | 4 +- src/task/game/Game.cpp | 92 +++++++++++++++++++++++++++++++++--------- src/task/game/Game.h | 21 ++++++++-- 3 files changed, 94 insertions(+), 23 deletions(-) diff --git a/src/game/map/Consts.h b/src/game/map/Consts.h index cccffe14..553f2767 100644 --- a/src/game/map/Consts.h +++ b/src/game/map/Consts.h @@ -223,8 +223,8 @@ struct Consts { const struct { const Vec2< uint32_t > dimensions = { - 100, - 62 + 95, + 57 }; // some coordinates were altered to fix alignment diff --git a/src/task/game/Game.cpp b/src/task/game/Game.cpp index 47770646..9f007fda 100644 --- a/src/task/game/Game.cpp +++ b/src/task/game/Game.cpp @@ -8,7 +8,6 @@ #include "util/FS.h" #include "ui/popup/PleaseDontGo.h" -#include "game/unit/Unit.h" #include "game/unit/StaticDef.h" #include "game/unit/SpriteRender.h" @@ -19,6 +18,11 @@ namespace game { const Game::consts_t Game::s_consts = {}; +const float Game::unitbadge_def_t::SCALE_X = 0.25f; +const float Game::unitbadge_def_t::SCALE_Y = 0.5f; +const float Game::unitbadge_def_t::OFFSET_X = -0.25f; +const float Game::unitbadge_def_t::OFFSET_Y = -0.5f; + Game::Game( ::game::State* state, ui_handler_t on_start, ui_handler_t on_cancel ) : m_state( state ) , m_on_start( on_start ) @@ -250,8 +254,12 @@ void Game::Iterate() { actor.tex_coords, ::game::map::s_consts.tc.ter1_pcx.dimensions, { // TODO - ::game::map::s_consts.tc.ter1_pcx.dimensions.x / 2, - ::game::map::s_consts.tc.ter1_pcx.dimensions.y / 2 + ::game::map::s_consts.tc.ter1_pcx.dimensions.x / 2 - 5, + ::game::map::s_consts.tc.ter1_pcx.dimensions.y / 2 - 5 + }, + { + ::game::map::s_consts.tile.scale.x, + ::game::map::s_consts.tile.scale.y * ::game::map::s_consts.sprite.y_scale }, actor.z_index ); @@ -416,6 +424,7 @@ types::Texture* Game::GetSourceTexture( const std::string& name ) { Color::RGB( 100, 16, 156 ), // remove second transparency color Color::RGB( 24, 184, 228 ), // remove frame Color::RGB( 253, 189, 118 ), // remove drawn shadows too (we'll have our own) + Color::RGB( 124, 124, 124 ), // remove badges background // TODO: use per-file transparency selection } ); ASSERT( texture, "texture not loaded" ); @@ -426,13 +435,14 @@ types::Texture* Game::GetSourceTexture( const std::string& name ) { Game::instanced_sprite_t& Game::GetInstancedSprite( const std::string& name, const std::string& tex_file, - const ::game::map::Consts::pcx_texture_coordinates_t& xy, - const ::game::map::Consts::pcx_texture_coordinates_t& wh, - const ::game::map::Consts::pcx_texture_coordinates_t& cxy, + const ::game::map::Consts::pcx_texture_coordinates_t& src_xy, + const ::game::map::Consts::pcx_texture_coordinates_t& src_wh, + const ::game::map::Consts::pcx_texture_coordinates_t& src_cxy, + const types::Vec2< float > dst_xy, const float z_index ) { - const auto key = name + " " + tex_file + " " + xy.ToString() + " " + wh.ToString(); + const auto key = name + " " + tex_file + " " + src_xy.ToString() + " " + src_wh.ToString(); const auto& it = m_instanced_sprites.find( key ); if ( it == m_instanced_sprites.end() ) { @@ -446,18 +456,18 @@ Game::instanced_sprite_t& Game::GetInstancedSprite( scene::actor::Sprite, name, { - ::game::map::s_consts.tile.scale.x, - ::game::map::s_consts.tile.scale.y * ::game::map::s_consts.sprite.y_scale + dst_xy.x, + dst_xy.y }, pcx, { { - (float)1.0f / pcx->m_width * ( xy.x + 1 ), - (float)1.0f / pcx->m_height * ( xy.y + 1 ) + (float)1.0f / pcx->m_width * ( src_xy.x ), + (float)1.0f / pcx->m_height * ( src_xy.y ) }, { - (float)1.0f / pcx->m_width * ( xy.x + wh.x - 4 ), - (float)1.0f / pcx->m_height * ( xy.y + wh.y - 4 ) + (float)1.0f / pcx->m_width * ( src_xy.x + src_wh.x ), + (float)1.0f / pcx->m_height * ( src_xy.y + src_wh.y ) } } ); @@ -466,9 +476,9 @@ Game::instanced_sprite_t& Game::GetInstancedSprite( m_world_scene->AddActor( instanced ); m_instanced_sprites[ key ] = { name, - xy, - wh, - cxy, + src_xy, + src_wh, + src_cxy, instanced }; return m_instanced_sprites.at( key ); @@ -822,6 +832,10 @@ void Game::DefineUnit( const ::game::unit::Def* unitdef ) { render->m_cx, render->m_cy, }, + { + ::game::map::s_consts.tile.scale.x, + ::game::map::s_consts.tile.scale.y * ::game::map::s_consts.sprite.y_scale + }, 0.5f ); @@ -858,6 +872,7 @@ void Game::SpawnUnit( const size_t unit_id, const std::string& unitdef_name, con ASSERT( unitdef_state.m_type == ::game::unit::Def::DT_STATIC, "only static unitdefs are supported for now" ); ASSERT( unitdef_state.static_.render.is_sprite, "only sprite unitdefs are supported for now" ); + // add render unit_state.instance_id = unitdef_state.static_.render.sprite.next_instance_id++; unitdef_state.static_.render.sprite.instanced_sprite->actor->SetInstance( unit_state.instance_id, { @@ -866,12 +881,26 @@ void Game::SpawnUnit( const size_t unit_id, const std::string& unitdef_name, con z } ); + + // add badge render + unit_state.badge_def = &m_unitbadge_default; // TODO: variations + unit_state.badge_instance_id = unit_state.badge_def->next_instance_id++; + unit_state.badge_def->instanced_sprite->actor->SetInstance( + unit_state.badge_instance_id, { + x + ::game::map::s_consts.tile.scale.x * unitbadge_def_t::OFFSET_X, + y - ::game::map::s_consts.tile.scale.y * ::game::map::s_consts.sprite.y_scale * unitbadge_def_t::OFFSET_Y, + z + } + ); + + // save m_unit_states.insert( { unit_id, unit_state } ); + } void Game::DespawnUnit( const size_t unit_id ) { @@ -885,6 +914,7 @@ void Game::DespawnUnit( const size_t unit_id ) { ASSERT( unitdef_state->static_.render.is_sprite, "only sprite unitdefs are supported for now" ); unitdef_state->static_.render.sprite.instanced_sprite->actor->RemoveInstance( unit_state.instance_id ); + unit_state.badge_def->instanced_sprite->actor->RemoveInstance( unit_state.badge_instance_id ); m_unit_states.erase( it ); } @@ -1075,8 +1105,12 @@ void Game::Initialize( a.second.tex_coords, ::game::map::s_consts.tc.ter1_pcx.dimensions, { - ::game::map::s_consts.tc.ter1_pcx.dimensions.x / 2, - ::game::map::s_consts.tc.ter1_pcx.dimensions.y / 2 + ::game::map::s_consts.tc.ter1_pcx.dimensions.x / 2 - 5, + ::game::map::s_consts.tc.ter1_pcx.dimensions.y / 2 - 5 + }, + { + ::game::map::s_consts.tile.scale.x, + ::game::map::s_consts.tile.scale.y * ::game::map::s_consts.sprite.y_scale }, a.second.z_index ); @@ -1088,6 +1122,28 @@ void Game::Initialize( actor->SetInstance( instance.first, instance.second.second ); } + // Predefined sprites + // TODO: variations + m_unitbadge_default.instanced_sprite = &GetInstancedSprite( + "Badge_DEFAULT", "flags.pcx", { + 49, + 63, + }, + { + 23, + 30, + }, + { + 61, + 78, + }, + { + ::game::map::s_consts.tile.scale.x * unitbadge_def_t::SCALE_X, + ::game::map::s_consts.tile.scale.y * ::game::map::s_consts.sprite.y_scale * unitbadge_def_t::SCALE_Y + }, + 0.52f + ); + // UI ui->AddTheme( &m_ui.theme ); NEW( m_ui.bottom_bar, ui::BottomBar, this ); diff --git a/src/task/game/Game.h b/src/task/game/Game.h index 9bb40880..8b9a7101 100644 --- a/src/task/game/Game.h +++ b/src/task/game/Game.h @@ -101,9 +101,10 @@ CLASS( Game, base::Task ) instanced_sprite_t& GetInstancedSprite( const std::string& name, const std::string& tex_file, - const ::game::map::Consts::pcx_texture_coordinates_t& xy, - const ::game::map::Consts::pcx_texture_coordinates_t& wh, - const ::game::map::Consts::pcx_texture_coordinates_t& cxy, + const ::game::map::Consts::pcx_texture_coordinates_t& src_xy, + const ::game::map::Consts::pcx_texture_coordinates_t& src_wh, + const ::game::map::Consts::pcx_texture_coordinates_t& src_cxy, + const types::Vec2< float > dst_xy, const float z_index ); instanced_sprite_t& GetInstancedSpriteByKey( const std::string& key ); // actor must already exist @@ -373,10 +374,23 @@ CLASS( Game, base::Task ) }; std::unordered_map< std::string, unitdef_state_t > m_unitdef_states = {}; + struct unitbadge_def_t { + static const float SCALE_X; + static const float SCALE_Y; + static const float OFFSET_X; + static const float OFFSET_Y; + Game::instanced_sprite_t* instanced_sprite = nullptr; + size_t next_instance_id = 1; + size_t instance_id; + }; + unitbadge_def_t m_unitbadge_default = {}; // TODO: variations + struct unit_state_t { unitdef_state_t* def = nullptr; slot_state_t* slot = nullptr; size_t instance_id = 0; + unitbadge_def_t* badge_def = nullptr; + size_t badge_instance_id = 0; }; std::unordered_map< size_t, unit_state_t > m_unit_states = {}; @@ -384,6 +398,7 @@ CLASS( Game, base::Task ) void CancelRequests(); void CancelGame(); + }; } From b97b3a9fced86c7c212d0747457c6f6ca9e1cc8b Mon Sep 17 00:00:00 2001 From: afwbkbc Date: Sun, 18 Feb 2024 01:46:46 +0200 Subject: [PATCH 10/21] fixed release build --- src/game/event/Event.h | 2 ++ src/game/rules/Rules.h | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/game/event/Event.h b/src/game/event/Event.h index 4c98acff..673b9075 100644 --- a/src/game/event/Event.h +++ b/src/game/event/Event.h @@ -1,5 +1,7 @@ #pragma once +#include + #include "types/Buffer.h" #include "gse/Value.h" diff --git a/src/game/rules/Rules.h b/src/game/rules/Rules.h index cac606ee..3a8fa252 100644 --- a/src/game/rules/Rules.h +++ b/src/game/rules/Rules.h @@ -1,6 +1,6 @@ #pragma once -#include +#include #include "types/Serializable.h" From ba5df242f41e937c69da61074bbe7cf2b66f9b72 Mon Sep 17 00:00:00 2001 From: afwbkbc Date: Sun, 18 Feb 2024 10:32:57 +0200 Subject: [PATCH 11/21] refactored/fixed some terrain sprites --- src/game/map/Consts.h | 16 +++---- src/game/map/Map.cpp | 48 ++++++++++++++------ src/game/map/module/Sprites.cpp | 11 +++-- src/task/game/Game.cpp | 77 +++++++++++++++------------------ src/task/game/Game.h | 1 + 5 files changed, 86 insertions(+), 67 deletions(-) diff --git a/src/game/map/Consts.h b/src/game/map/Consts.h index 553f2767..ef1c1002 100644 --- a/src/game/map/Consts.h +++ b/src/game/map/Consts.h @@ -16,7 +16,7 @@ namespace map { struct Consts { - // coordinates of textures (x1 and y1) in texture.pcx + // coordinates of textures (x1 and y1) in pcx file typedef Vec2< uint32_t > pcx_texture_coordinates_t; const struct { @@ -223,18 +223,19 @@ struct Consts { const struct { const Vec2< uint32_t > dimensions = { - 95, - 57 + 100, + 62 + }; + const Vec2< uint32_t > center = { + 50, + 25 }; - // some coordinates were altered to fix alignment const pcx_texture_coordinates_t nutrient_bonus_sea[2] = { - //{ 4, 257 }, { 106, 257 }, { 1, 253 }, - { 103, 253 }, + { 102, 253 }, }; const pcx_texture_coordinates_t nutrient_bonus_land[2] = { - //{ 207, 257 }, { 308, 257 }, { 203, 253 }, { 304, 253 }, }; @@ -245,7 +246,6 @@ struct Consts { const pcx_texture_coordinates_t minerals_bonus_land[2] = { { 203, 316 }, { 304, 316 }, - //{ 203, 316 }, { 304, 316 }, }; const pcx_texture_coordinates_t energy_bonus_sea[2] = { { 1, 379 }, diff --git a/src/game/map/Map.cpp b/src/game/map/Map.cpp index daba6ef4..003bf03a 100644 --- a/src/game/map/Map.cpp +++ b/src/game/map/Map.cpp @@ -546,19 +546,29 @@ Tiles* Map::GetTilesPtr() const { } const std::string Map::GetTerrainSpriteActor( const std::string& name, const Consts::pcx_texture_coordinates_t& tex_coords, const float z_index ) { - const auto key = name + " ter1.pcx " + tex_coords.ToString() + " " + ::game::map::s_consts.tc.ter1_pcx.dimensions.ToString(); + const auto key = "Terrain_" + name + " ter1.pcx " + tex_coords.ToString() + " " + ::game::map::s_consts.tc.ter1_pcx.dimensions.ToString(); - const auto& it = m_sprite_actors.find( key ); + auto it = m_sprite_actors.find( key ); if ( it == m_sprite_actors.end() ) { - m_sprite_actors[ key ] = { - name, - tex_coords, - z_index - }; + it = m_sprite_actors.insert( + { + key, + { + name, + tex_coords, + z_index + } + } + ).first; } if ( m_sprite_actors_to_add.find( key ) == m_sprite_actors_to_add.end() ) { - m_sprite_actors_to_add[ key ] = m_sprite_actors.at( key ); + m_sprite_actors_to_add.insert( + { + key, + it->second + } + ); } return key; @@ -566,18 +576,28 @@ const std::string Map::GetTerrainSpriteActor( const std::string& name, const Con const size_t Map::AddTerrainSpriteActorInstance( const std::string& key, const Vec3& coords ) { ASSERT( m_sprite_actors.find( key ) != m_sprite_actors.end(), "actor not found" ); - m_sprite_instances[ m_next_sprite_instance_id ] = { - key, - coords - }; - m_sprite_instances_to_add[ m_next_sprite_instance_id ] = m_sprite_instances.at( m_next_sprite_instance_id ); + const auto it = m_sprite_instances.insert( + { + m_next_sprite_instance_id, + { + key, + coords + } + } + ).first->second; + m_sprite_instances_to_add[ m_next_sprite_instance_id ] = it; return m_next_sprite_instance_id++; } void Map::RemoveTerrainSpriteActorInstance( const std::string& key, const size_t instance_id ) { ASSERT( m_sprite_actors.find( key ) != m_sprite_actors.end(), "actor not found" ); ASSERT( m_sprite_instances_to_remove.find( instance_id ) == m_sprite_instances_to_remove.end(), "instance already pending removal" ); - m_sprite_instances_to_remove[ instance_id ] = key; + m_sprite_instances_to_remove.insert( + { + instance_id, + key + } + ); } const Map::error_code_t Map::Generate( diff --git a/src/game/map/module/Sprites.cpp b/src/game/map/module/Sprites.cpp index a37a68d3..fa93542a 100644 --- a/src/game/map/module/Sprites.cpp +++ b/src/game/map/module/Sprites.cpp @@ -155,11 +155,16 @@ void Sprites::GenerateSprite( const Tile* tile, TileState* ts, const std::string sprite.name = name; sprite.tex_coords = tex_coords; sprite.actor = m_map->GetTerrainSpriteActor( name, sprite.tex_coords, z_index ); + const types::Vec3 center = { + ( coords.left.x + coords.right.x ) / 2, + ( coords.top.y + coords.bottom.y ) / 2, + ( coords.left.z + coords.top.z + coords.right.z + coords.bottom.z ) / 4 + }; sprite.instance = m_map->AddTerrainSpriteActorInstance( sprite.actor, { - coords.center.x, - -coords.center.y, // TODO: fix y inversion - coords.center.z + center.x, + -center.y, // TODO: fix y inversion + center.z } ); diff --git a/src/task/game/Game.cpp b/src/task/game/Game.cpp index 9f007fda..c807d270 100644 --- a/src/task/game/Game.cpp +++ b/src/task/game/Game.cpp @@ -247,22 +247,7 @@ void Game::Iterate() { if ( response.data.edit_map.sprites.actors_to_add ) { Log( "Need to add " + std::to_string( response.data.edit_map.sprites.actors_to_add->size() ) + " actors" ); for ( auto& a : *response.data.edit_map.sprites.actors_to_add ) { - const auto& actor = a.second; - GetInstancedSprite( - actor.name, - "ter1.pcx", - actor.tex_coords, - ::game::map::s_consts.tc.ter1_pcx.dimensions, - { // TODO - ::game::map::s_consts.tc.ter1_pcx.dimensions.x / 2 - 5, - ::game::map::s_consts.tc.ter1_pcx.dimensions.y / 2 - 5 - }, - { - ::game::map::s_consts.tile.scale.x, - ::game::map::s_consts.tile.scale.y * ::game::map::s_consts.sprite.y_scale - }, - actor.z_index - ); + GetTerrainInstancedSprite( a.second ); } } @@ -438,7 +423,7 @@ Game::instanced_sprite_t& Game::GetInstancedSprite( const ::game::map::Consts::pcx_texture_coordinates_t& src_xy, const ::game::map::Consts::pcx_texture_coordinates_t& src_wh, const ::game::map::Consts::pcx_texture_coordinates_t& src_cxy, - const types::Vec2< float > dst_xy, + const types::Vec2< float > dst_wh, const float z_index ) { @@ -456,8 +441,8 @@ Game::instanced_sprite_t& Game::GetInstancedSprite( scene::actor::Sprite, name, { - dst_xy.x, - dst_xy.y + dst_wh.x, + dst_wh.y }, pcx, { @@ -474,14 +459,18 @@ Game::instanced_sprite_t& Game::GetInstancedSprite( NEWV( instanced, scene::actor::Instanced, sprite ); instanced->SetZIndex( z_index ); // needs to be higher than map terrain z position m_world_scene->AddActor( instanced ); - m_instanced_sprites[ key ] = { - name, - src_xy, - src_wh, - src_cxy, - instanced - }; - return m_instanced_sprites.at( key ); + return m_instanced_sprites.insert( + { + key, + { + name, + src_xy, + src_wh, + src_cxy, + instanced + } + } + ).first->second; } else { return it->second; @@ -494,6 +483,24 @@ Game::instanced_sprite_t& Game::GetInstancedSpriteByKey( const std::string& key return it->second; } +Game::instanced_sprite_t& Game::GetTerrainInstancedSprite( const ::game::map::Map::sprite_actor_t& actor ) { + return GetInstancedSprite( + "Terrain_" + actor.name, + "ter1.pcx", + actor.tex_coords, + ::game::map::s_consts.tc.ter1_pcx.dimensions, + { + actor.tex_coords.x + ::game::map::s_consts.tc.ter1_pcx.center.x, + actor.tex_coords.y + ::game::map::s_consts.tc.ter1_pcx.center.y + }, + { + ::game::map::s_consts.tile.scale.x, + ::game::map::s_consts.tile.scale.y * ::game::map::s_consts.sprite.y_scale + }, + actor.z_index + ); +} + void Game::CenterAtCoordinatePercents( const Vec2< float > position_percents ) { const Vec2< float > position = { m_map_data.range.percent_to_absolute.x.Clamp( position_percents.x ), @@ -1099,21 +1106,7 @@ void Game::Initialize( Log( "Sprites count: " + std::to_string( sprite_actors.size() ) ); Log( "Sprites instances: " + std::to_string( sprite_instances.size() ) ); for ( auto& a : sprite_actors ) { - GetInstancedSprite( - a.second.name, - "ter1.pcx", - a.second.tex_coords, - ::game::map::s_consts.tc.ter1_pcx.dimensions, - { - ::game::map::s_consts.tc.ter1_pcx.dimensions.x / 2 - 5, - ::game::map::s_consts.tc.ter1_pcx.dimensions.y / 2 - 5 - }, - { - ::game::map::s_consts.tile.scale.x, - ::game::map::s_consts.tile.scale.y * ::game::map::s_consts.sprite.y_scale - }, - a.second.z_index - ); + GetTerrainInstancedSprite( a.second ); } for ( auto& instance : sprite_instances ) { auto* actor = GetInstancedSpriteByKey( instance.second.first ).actor; diff --git a/src/task/game/Game.h b/src/task/game/Game.h index 8b9a7101..53c135fe 100644 --- a/src/task/game/Game.h +++ b/src/task/game/Game.h @@ -108,6 +108,7 @@ CLASS( Game, base::Task ) const float z_index ); instanced_sprite_t& GetInstancedSpriteByKey( const std::string& key ); // actor must already exist + instanced_sprite_t& GetTerrainInstancedSprite( const ::game::map::Map::sprite_actor_t& actor ); void CenterAtCoordinatePercents( const Vec2< float > position_percents ); From 5295e4e9f604e7fd9433893da755875c377f1611 Mon Sep 17 00:00:00 2001 From: afwbkbc Date: Sun, 18 Feb 2024 11:35:18 +0200 Subject: [PATCH 12/21] refactored texture tcs --- src/game/map/Map.cpp | 11 +- src/loader/texture/Null.h | 5 +- src/loader/texture/SDL2.cpp | 6 +- src/loader/texture/SDL2.h | 7 +- src/loader/texture/TextureLoader.cpp | 122 +++++- src/loader/texture/TextureLoader.h | 20 +- src/main.cpp | 1 - src/task/common/ui/style/Popup.cpp | 504 +++++++++++++----------- src/task/common/ui/style/Scrollbars.cpp | 19 +- src/task/game/Game.cpp | 10 +- src/task/game/ui/style/BottomBar.cpp | 10 +- src/task/game/ui/style/Popup.cpp | 2 +- src/ui/theme/Style.cpp | 12 - src/ui/theme/Style.h | 5 +- 14 files changed, 404 insertions(+), 330 deletions(-) diff --git a/src/game/map/Map.cpp b/src/game/map/Map.cpp index 003bf03a..addd3886 100644 --- a/src/game/map/Map.cpp +++ b/src/game/map/Map.cpp @@ -83,15 +83,8 @@ Map::Map( Game* game ) ); // main source textures - m_textures.source.texture_pcx = g_engine->GetTextureLoader()->LoadTextureTC( "texture.pcx", Color::RGB( 125, 0, 128 ) ); - m_textures.source.ter1_pcx = g_engine->GetTextureLoader()->LoadTextureTCs( - "ter1.pcx", { - Color::RGB( 152, 24, 228 ), // remove transparency color - Color::RGB( 100, 16, 156 ), // remove second transparency color - Color::RGB( 24, 184, 228 ), // remove frame - Color::RGB( 253, 189, 118 ) // remove drawn shadows too (we'll have our own) - } - ); + m_textures.source.texture_pcx = g_engine->GetTextureLoader()->LoadTexture( "texture.pcx" ); + m_textures.source.ter1_pcx = g_engine->GetTextureLoader()->LoadTexture( "ter1.pcx" ); // add map modules // order of passes is important diff --git a/src/loader/texture/Null.h b/src/loader/texture/Null.h index 66187a22..1856c21b 100644 --- a/src/loader/texture/Null.h +++ b/src/loader/texture/Null.h @@ -10,9 +10,8 @@ namespace loader { namespace texture { CLASS( Null, TextureLoader ) - - Texture* LoadTexture( const std::string& name ) override { return nullptr; } - Texture* LoadTexture( const std::string& name, const size_t x1, const size_t y1, const size_t x2, const size_t y2, const uint8_t flags = LT_NONE, const float value = 1.0 ) override { return nullptr; } + Texture* LoadTextureImpl( const std::string& name ) override { return nullptr; } + Texture* LoadTextureImpl( const std::string& name, const size_t x1, const size_t y1, const size_t x2, const size_t y2, const uint8_t flags = LT_NONE, const float value = 1.0 ) override { return nullptr; } }; } diff --git a/src/loader/texture/SDL2.cpp b/src/loader/texture/SDL2.cpp index 4642ece1..9ddcf4c8 100644 --- a/src/loader/texture/SDL2.cpp +++ b/src/loader/texture/SDL2.cpp @@ -28,7 +28,7 @@ void SDL2::Iterate() { } -Texture* SDL2::LoadTexture( const std::string& name ) { +Texture* SDL2::LoadTextureImpl( const std::string& name ) { texture_map_t::iterator it = m_textures.find( name ); if ( it != m_textures.end() ) { @@ -74,7 +74,7 @@ Texture* SDL2::LoadTexture( const std::string& name ) { } -Texture* SDL2::LoadTexture( const std::string& name, const size_t x1, const size_t y1, const size_t x2, const size_t y2, const uint8_t flags, const float value ) { +Texture* SDL2::LoadTextureImpl( const std::string& name, const size_t x1, const size_t y1, const size_t x2, const size_t y2, const uint8_t flags, const float value ) { ASSERT( x1 <= x2, "LoadTexture x overflow ( " + std::to_string( x1 ) + " > " + std::to_string( x2 ) + " )" ); ASSERT( y1 <= y2, "LoadTexture y overflow ( " + std::to_string( y1 ) + " > " + std::to_string( y2 ) + " )" ); @@ -125,7 +125,7 @@ Texture* SDL2::LoadTexture( const std::string& name, const size_t x1, const size } void SDL2::FixTransparency( Texture* texture ) const { - if ( m_is_transparent_color_set ) { + if ( !m_transparent_colors.empty() ) { void* at = nullptr; for ( size_t i = 0 ; i < texture->m_bitmap_size ; i += texture->m_bpp ) { at = ptr( texture->m_bitmap, i, texture->m_bpp ); diff --git a/src/loader/texture/SDL2.h b/src/loader/texture/SDL2.h index e321bb12..4e4cefe1 100644 --- a/src/loader/texture/SDL2.h +++ b/src/loader/texture/SDL2.h @@ -15,10 +15,11 @@ CLASS( SDL2, TextureLoader ) void Stop() override; void Iterate() override; - Texture* LoadTexture( const std::string& name ) override; - Texture* LoadTexture( const std::string& name, const size_t x1, const size_t y1, const size_t x2, const size_t y2, const uint8_t flags, const float value = 1.0 ) override; - protected: + + Texture* LoadTextureImpl( const std::string& name ) override; + Texture* LoadTextureImpl( const std::string& name, const size_t x1, const size_t y1, const size_t x2, const size_t y2, const uint8_t flags, const float value ) override; + // cache all textures for future use typedef std::unordered_map< std::string, Texture* > texture_map_t; texture_map_t m_textures = {}; diff --git a/src/loader/texture/TextureLoader.cpp b/src/loader/texture/TextureLoader.cpp index 518f7f6b..04f995be 100644 --- a/src/loader/texture/TextureLoader.cpp +++ b/src/loader/texture/TextureLoader.cpp @@ -3,28 +3,117 @@ namespace loader { namespace texture { -Texture* TextureLoader::LoadTextureTCs( const std::string& name, const transparent_colors_t transparent_colors ) { - const transparent_colors_t colors_old = m_transparent_colors; - m_transparent_colors = transparent_colors; - Texture* result = LoadTexture( name ); - m_transparent_colors = colors_old; - return result; -} +// different files use different color for transparency +static const auto s_tc_pink = Color::RGB( 255, 0, 255 ); +static const auto s_tc_purple = Color::RGB( 152, 24, 228 ); +static const auto s_tc_darkpurple = Color::RGB( 100, 16, 156 ); +static const auto s_tc_aqua = Color::RGB( 24, 184, 228 ); +static const std::unordered_map< std::string, TextureLoader::transparent_colors_t > s_tcs = { + { + "texture.pcx", + { + Color::RGB( 125, 0, 128 ), + } + }, + { + "interface.pcx", + { + s_tc_pink, + } + }, + { + "moon1.pcx", + { + s_tc_pink, + } + }, + { + "moon2.pcx", + { + s_tc_pink, + } + }, + { + "moon3.pcx", + { + s_tc_pink, + } + }, + { + "Icons.pcx", + { + s_tc_purple, + } + }, + { + "console2_A.pcx", + { + s_tc_darkpurple, + } + }, + { + "console_x2_a.pcx", + { + s_tc_pink, + } + }, + { + "Jackal.pcx", + { + s_tc_darkpurple, + } + }, + { + "ter1.pcx", + { + s_tc_purple, + s_tc_darkpurple, // tile markings + s_tc_aqua, // borders + Color::RGB( 253, 189, 118 ), // shadows + } + }, + { + "Units.pcx", + { + s_tc_purple, + s_tc_darkpurple, // tile markings + s_tc_aqua, // borders + } + }, + { + "flags.pcx", + { + Color::RGB( 124, 124, 124 ), + s_tc_aqua, // borders + } + } +}; +static const TextureLoader::transparent_colors_t s_no_transparent_colors = {}; -Texture* TextureLoader::LoadTextureTC( const std::string& name, const Color::rgba_t transparent_color ) { - return LoadTextureTCs( name, { transparent_color } ); +const TextureLoader::transparent_colors_t& TextureLoader::GetTCs( const std::string& name ) { + const auto& transparent_colors_it = s_tcs.find( name ); + if ( transparent_colors_it != s_tcs.end() ) { + return transparent_colors_it->second; + } + else { + return s_no_transparent_colors; + } } -Texture* TextureLoader::LoadTextureTCs( const std::string& name, const size_t x1, const size_t y1, const size_t x2, const size_t y2, const transparent_colors_t transparent_colors, const uint8_t flags, const float value ) { +Texture* TextureLoader::LoadTexture( const std::string& name ) { const transparent_colors_t colors_old = m_transparent_colors; - m_transparent_colors = transparent_colors; - Texture* result = LoadTexture( name, x1, y1, x2, y2, flags, value ); + m_transparent_colors = GetTCs( name ); + Texture* result = LoadTextureImpl( name ); m_transparent_colors = colors_old; return result; } -Texture* TextureLoader::LoadTextureTC( const std::string& name, const size_t x1, const size_t y1, const size_t x2, const size_t y2, const Color::rgba_t transparent_color, const uint8_t flags, const float value ) { - return LoadTextureTCs( name, x1, y1, x2, y2, { transparent_color }, flags, value ); +Texture* TextureLoader::LoadTexture( const std::string& name, 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( name ); + Texture* result = LoadTextureImpl( name, x1, y1, x2, y2, flags, value ); + m_transparent_colors = colors_old; + return result; } Texture* TextureLoader::GetColorTexture( const Color& color ) { @@ -38,10 +127,5 @@ Texture* TextureLoader::GetColorTexture( const Color& color ) { return texture; } -void TextureLoader::SetTransparentColor( const Color::rgba_t rgba ) { - m_transparent_colors = { rgba }; - m_is_transparent_color_set = true; -} - } } diff --git a/src/loader/texture/TextureLoader.h b/src/loader/texture/TextureLoader.h index 1ccfc54c..b5d09622 100644 --- a/src/loader/texture/TextureLoader.h +++ b/src/loader/texture/TextureLoader.h @@ -28,27 +28,27 @@ CLASS( TextureLoader, Loader ) typedef std::unordered_set< Color::rgba_t > transparent_colors_t; // load full texture - virtual Texture* LoadTexture( const std::string& name ) = 0; - Texture* LoadTextureTCs( const std::string& name, const transparent_colors_t transparent_colors ); - Texture* LoadTextureTC( const std::string& name, const Color::rgba_t transparent_color ); + Texture* LoadTexture( const std::string& name ); // load part of texture - virtual Texture* LoadTexture( const std::string& name, const size_t x1, const size_t y1, const size_t x2, const size_t y2, const uint8_t flags = LT_NONE, const float value = 1.0 ) = 0; - Texture* LoadTextureTCs( const std::string& name, const size_t x1, const size_t y1, const size_t x2, const size_t y2, const transparent_colors_t transparent_colors, const uint8_t flags = LT_NONE, const float value = 1.0 ); - Texture* LoadTextureTC( const std::string& name, const size_t x1, const size_t y1, const size_t x2, const size_t y2, const Color::rgba_t transparent_color, const uint8_t flags, const float value ); + Texture* LoadTexture( const std::string& name, const size_t x1, const size_t y1, const size_t x2, const size_t y2, const uint8_t flags = LT_NONE, const float value = 1.0 ); + // create texture of solid color Texture* GetColorTexture( const Color& color ); - // treat specific color as transparent - void SetTransparentColor( const Color::rgba_t rgba ); - protected: + + virtual Texture* LoadTextureImpl( const std::string& name ) = 0; + virtual Texture* LoadTextureImpl( const std::string& name, const size_t x1, const size_t y1, const size_t x2, const size_t y2, const uint8_t flags, const float value ) = 0; + transparent_colors_t m_transparent_colors; - bool m_is_transparent_color_set = false; typedef std::unordered_map< Color::rgba_t, Texture* > color_texture_map_t; color_texture_map_t m_color_textures = {}; +private: + const transparent_colors_t& GetTCs( const std::string& name ); + }; } /* namespace texture */ diff --git a/src/main.cpp b/src/main.cpp index 0944aa7c..2dd2a67e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -216,7 +216,6 @@ int main( const int argc, const char* argv[] ) { loader::font::FreeType font_loader; loader::texture::SDL2 texture_loader; - texture_loader.SetTransparentColor( types::Color::RGBA( 255, 0, 255, 255 ) ); loader::sound::SDL2 sound_loader; diff --git a/src/task/common/ui/style/Popup.cpp b/src/task/common/ui/style/Popup.cpp index 77f91061..c75e56b2 100644 --- a/src/task/common/ui/style/Popup.cpp +++ b/src/task/common/ui/style/Popup.cpp @@ -7,190 +7,201 @@ void Popup::AddStyles() { // TODO: multilevel includes AddStyle( - "PopupBorder", SH() { - // frame - { - const std::unordered_map< Style::attribute_type_t, std::vector< size_t > > textures = { - { Style::A_TEXTURE_BACK, { 86, 353, 109, 376 } }, - { Style::A_TEXTURE_LEFT, { 79, 431, 79, 454 } }, - { Style::A_TEXTURE_TOP, { 86, 307, 109, 307 } }, - { Style::A_TEXTURE_RIGHT, { 116, 431, 116, 454 } }, - { Style::A_TEXTURE_BOTTOM, { 86, 461, 109, 461 } }, - }; - for ( auto& texture : textures ) { - s->SetTexture( texture.first, "interface.pcx", texture.second[ 0 ], texture.second[ 1 ], texture.second[ 2 ], texture.second[ 3 ] ); - } + "PopupBorder", SH() + { + // frame + { + const std::unordered_map< Style::attribute_type_t, std::vector< size_t > > textures = { + { Style::A_TEXTURE_BACK, { 86, 353, 109, 376 } }, + { Style::A_TEXTURE_LEFT, { 79, 431, 79, 454 } }, + { Style::A_TEXTURE_TOP, { 86, 307, 109, 307 } }, + { Style::A_TEXTURE_RIGHT, { 116, 431, 116, 454 } }, + { Style::A_TEXTURE_BOTTOM, { 86, 461, 109, 461 } }, + }; + for ( auto& texture : textures ) { + s->SetTexture( texture.first, "interface.pcx", texture.second[ 0 ], texture.second[ 1 ], texture.second[ 2 ], texture.second[ 3 ] ); } } + } ); AddStyle( - "PopupFrame", SH() { - - // frame - { - const std::unordered_map< Style::attribute_type_t, std::vector< size_t > > textures = ( - s->Is( Style::M_HIGHLIGHT ) - ? - std::unordered_map< Style::attribute_type_t, std::vector< size_t > >{ - { Style::A_TEXTURE_BACK, { 86, 353, 109, 376 } }, - { Style::A_TEXTURE_LEFT, { 40, 626, 40, 649 } }, - { Style::A_TEXTURE_TOP, { 47, 619, 70, 619 } }, - { Style::A_TEXTURE_RIGHT, { 77, 626, 77, 649 } }, - { Style::A_TEXTURE_BOTTOM, { 47, 656, 70, 656 } }, - } - : - std::unordered_map< Style::attribute_type_t, std::vector< size_t > >{ - { Style::A_TEXTURE_BACK, { 86, 353, 109, 376 } }, - { Style::A_TEXTURE_LEFT, { 79, 431, 79, 454 } }, - { Style::A_TEXTURE_TOP, { 86, 307, 109, 307 } }, - { Style::A_TEXTURE_RIGHT, { 116, 431, 116, 454 } }, - { Style::A_TEXTURE_BOTTOM, { 86, 461, 109, 461 } }, - } - ); - for ( auto& texture : textures ) { - s->SetTexture( texture.first, "interface.pcx", texture.second[ 0 ], texture.second[ 1 ], texture.second[ 2 ], texture.second[ 3 ] ); - } + "PopupFrame", SH() + { + + // frame + { + const std::unordered_map< Style::attribute_type_t, std::vector< size_t > > textures = ( + s->Is( Style::M_HIGHLIGHT ) + ? + std::unordered_map< Style::attribute_type_t, std::vector< size_t > >{ + { Style::A_TEXTURE_BACK, { 86, 353, 109, 376 } }, + { Style::A_TEXTURE_LEFT, { 40, 626, 40, 649 } }, + { Style::A_TEXTURE_TOP, { 47, 619, 70, 619 } }, + { Style::A_TEXTURE_RIGHT, { 77, 626, 77, 649 } }, + { Style::A_TEXTURE_BOTTOM, { 47, 656, 70, 656 } }, + } + : + std::unordered_map< Style::attribute_type_t, std::vector< size_t > >{ + { Style::A_TEXTURE_BACK, { 86, 353, 109, 376 } }, + { Style::A_TEXTURE_LEFT, { 79, 431, 79, 454 } }, + { Style::A_TEXTURE_TOP, { 86, 307, 109, 307 } }, + { Style::A_TEXTURE_RIGHT, { 116, 431, 116, 454 } }, + { Style::A_TEXTURE_BOTTOM, { 86, 461, 109, 461 } }, + } + ); + for ( auto& texture : textures ) { + s->SetTexture( texture.first, "interface.pcx", texture.second[ 0 ], texture.second[ 1 ], texture.second[ 2 ], texture.second[ 3 ] ); } + } - // header - { - const std::unordered_map< Style::attribute_type_t, std::vector< size_t > > textures = { - { Style::A_HEADER_TEXTURE_BACK, { 86, 314, 109, 337 } }, - { Style::A_HEADER_TEXTURE_LEFT, { 79, 431, 79, 454 } }, - { Style::A_HEADER_TEXTURE_TOP, { 86, 307, 109, 307 } }, - { Style::A_HEADER_TEXTURE_RIGHT, { 116, 431, 116, 454 } }, - { Style::A_HEADER_TEXTURE_BOTTOM, { 86, 461, 109, 461 } }, - }; - for ( auto& texture : textures ) { - s->SetTexture( texture.first, "interface.pcx", texture.second[ 0 ], texture.second[ 1 ], texture.second[ 2 ], texture.second[ 3 ] ); - } + // header + { + const std::unordered_map< Style::attribute_type_t, std::vector< size_t > > textures = { + { Style::A_HEADER_TEXTURE_BACK, { 86, 314, 109, 337 } }, + { Style::A_HEADER_TEXTURE_LEFT, { 79, 431, 79, 454 } }, + { Style::A_HEADER_TEXTURE_TOP, { 86, 307, 109, 307 } }, + { Style::A_HEADER_TEXTURE_RIGHT, { 116, 431, 116, 454 } }, + { Style::A_HEADER_TEXTURE_BOTTOM, { 86, 461, 109, 461 } }, + }; + for ( auto& texture : textures ) { + s->SetTexture( texture.first, "interface.pcx", texture.second[ 0 ], texture.second[ 1 ], texture.second[ 2 ], texture.second[ 3 ] ); } - s->SetColor( Style::A_HEADER_TEXTCOLOR, Color::FromRGB( 109, 126, 178 ) ); } + s->SetColor( Style::A_HEADER_TEXTCOLOR, Color::FromRGB( 109, 126, 178 ) ); + } ); AddStyle( - "PopupWindow", { "PopupFrame" }, SH() { - s->SetFont( Style::A_HEADER_FONT, "arialnb.ttf", 18 ); - s->Set( Style::A_HEADER_HEIGHT, 22 ); - } + "PopupWindow", { "PopupFrame" }, SH() + { + s->SetFont( Style::A_HEADER_FONT, "arialnb.ttf", 18 ); + s->Set( Style::A_HEADER_HEIGHT, 22 ); + } ); AddStyle( - "PopupSection", { "PopupFrame" }, SH() { - s->SetFont( Style::A_HEADER_FONT, "arialnb.ttf", 16 ); - s->Set( Style::A_HEADER_HEIGHT, 20 ); - } + "PopupSection", { "PopupFrame" }, SH() + { + s->SetFont( Style::A_HEADER_FONT, "arialnb.ttf", 16 ); + s->Set( Style::A_HEADER_HEIGHT, 20 ); + } ); AddStyle( - "PopupLabel", SH() { - s->SetColor( Style::A_TEXT_COLOR, Color::FromRGB( 109, 126, 178 ) ); - s->SetFont( Style::A_FONT, "arialnb.ttf", 18 ); - } + "PopupLabel", SH() + { + s->SetColor( Style::A_TEXT_COLOR, Color::FromRGB( 109, 126, 178 ) ); + s->SetFont( Style::A_FONT, "arialnb.ttf", 18 ); + } ); AddStyle( "PopupInput", { "PopupBorder" - }, SH() { - s->SetColor( Style::A_TEXT_COLOR, Color::FromRGB( 159, 196, 198 ) ); - s->SetFont( Style::A_FONT, "arialnb.ttf", 18 ); - s->Set( Style::A_HEIGHT, 22 ); - } + }, SH() + { + s->SetColor( Style::A_TEXT_COLOR, Color::FromRGB( 159, 196, 198 ) ); + s->SetFont( Style::A_FONT, "arialnb.ttf", 18 ); + s->Set( Style::A_HEIGHT, 22 ); + } ); AddStyle( - "PopupButton", SH() { - std::unordered_map< Style::attribute_type_t, std::vector< size_t > > textures = {}; - - // borders - if ( s->Is( Style::M_ACTIVE ) || s->Is( Style::M_SELECTED ) ) { + "PopupButton", SH() + { + std::unordered_map< Style::attribute_type_t, std::vector< size_t > > textures = {}; + + // borders + if ( s->Is( Style::M_ACTIVE ) || s->Is( Style::M_SELECTED ) ) { + textures = { + { Style::A_TEXTURE_BACK, { 68, 170, 134, 187 } }, + { Style::A_TEXTURE_LEFT, { 64, 170, 64, 187 } }, + { Style::A_TEXTURE_TOP, { 68, 169, 134, 169 } }, + { Style::A_TEXTURE_RIGHT, { 138, 170, 138, 187 } }, + { Style::A_TEXTURE_BOTTOM, { 68, 188, 134, 188 } }, + }; + } + else { + if ( s->Is( Style::M_HOVER ) ) { textures = { - { Style::A_TEXTURE_BACK, { 68, 170, 134, 187 } }, - { Style::A_TEXTURE_LEFT, { 64, 170, 64, 187 } }, - { Style::A_TEXTURE_TOP, { 68, 169, 134, 169 } }, - { Style::A_TEXTURE_RIGHT, { 138, 170, 138, 187 } }, - { Style::A_TEXTURE_BOTTOM, { 68, 188, 134, 188 } }, + { Style::A_TEXTURE_LEFT, { 64, 149, 64, 166 } }, + { Style::A_TEXTURE_TOP, { 68, 148, 134, 148 } }, + { Style::A_TEXTURE_RIGHT, { 138, 149, 138, 166 } }, + { Style::A_TEXTURE_BOTTOM, { 68, 167, 134, 167 } }, }; } else { - if ( s->Is( Style::M_HOVER ) ) { - textures = { - { Style::A_TEXTURE_LEFT, { 64, 149, 64, 166 } }, - { Style::A_TEXTURE_TOP, { 68, 148, 134, 148 } }, - { Style::A_TEXTURE_RIGHT, { 138, 149, 138, 166 } }, - { Style::A_TEXTURE_BOTTOM, { 68, 167, 134, 167 } }, - }; - } - else { - textures = { - { Style::A_TEXTURE_LEFT, { 64, 128, 64, 145 } }, - { Style::A_TEXTURE_TOP, { 68, 127, 134, 127 } }, - { Style::A_TEXTURE_RIGHT, { 138, 128, 138, 145 } }, - { Style::A_TEXTURE_BOTTOM, { 68, 146, 134, 146 } }, - }; - } - textures[ Style::A_TEXTURE_BACK ] = { - 68, - 149, - 134, - 166 + textures = { + { Style::A_TEXTURE_LEFT, { 64, 128, 64, 145 } }, + { Style::A_TEXTURE_TOP, { 68, 127, 134, 127 } }, + { Style::A_TEXTURE_RIGHT, { 138, 128, 138, 145 } }, + { Style::A_TEXTURE_BOTTOM, { 68, 146, 134, 146 } }, }; } + textures[ Style::A_TEXTURE_BACK ] = { + 68, + 149, + 134, + 166 + }; + } - for ( auto& texture : textures ) { - s->SetTexture( texture.first, "interface.pcx", texture.second[ 0 ], texture.second[ 1 ], texture.second[ 2 ], texture.second[ 3 ] ); - } + for ( auto& texture : textures ) { + s->SetTexture( texture.first, "interface.pcx", texture.second[ 0 ], texture.second[ 1 ], texture.second[ 2 ], texture.second[ 3 ] ); + } - if ( s->Is( Style::M_HOVER ) && !s->Is( Style::M_ACTIVE ) && !s->Is( Style::M_SELECTED ) ) { - s->SetColor( Style::A_TEXT_COLOR, Color::FromRGB( 164, 176, 232 ) ); - } - else { - s->SetColor( Style::A_TEXT_COLOR, Color::FromRGB( 13, 23, 36 ) ); - } + if ( s->Is( Style::M_HOVER ) && !s->Is( Style::M_ACTIVE ) && !s->Is( Style::M_SELECTED ) ) { + s->SetColor( Style::A_TEXT_COLOR, Color::FromRGB( 164, 176, 232 ) ); + } + else { + s->SetColor( Style::A_TEXT_COLOR, Color::FromRGB( 13, 23, 36 ) ); } + } ); AddStyle( "PopupButtonList", { "PopupButton" - }, SH() { - s->Set( Style::A_LEFT, 3 ); - s->Set( Style::A_RIGHT, 3 ); - //s->Set( Style::A_TOP, 3 ); - s->Set( Style::A_BOTTOM, 3 ); - - s->SetFont( Style::A_FONT, "arialnb.ttf", 16 ); - s->Set( Style::A_TEXT_ALIGN, UIObject::ALIGN_LEFT ); - } + }, SH() + { + s->Set( Style::A_LEFT, 3 ); + s->Set( Style::A_RIGHT, 3 ); + //s->Set( Style::A_TOP, 3 ); + s->Set( Style::A_BOTTOM, 3 ); + + s->SetFont( Style::A_FONT, "arialnb.ttf", 16 ); + s->Set( Style::A_TEXT_ALIGN, UIObject::ALIGN_LEFT ); + } ); AddStyle( "PopupButtonOkCancel", { "PopupButton" - }, SH() { - s->Set( Style::A_WIDTH, 230 ); - s->Set( Style::A_HEIGHT, 20 ); - - s->SetFont( Style::A_FONT, "arialnb.ttf", 18 ); - s->SetSound( Style::A_BUTTON_CLICK_SOUND, "ok.wav" ); - s->Set( Style::A_SOUND_VOLUME, 0.5 ); - } + }, SH() + { + s->Set( Style::A_WIDTH, 230 ); + s->Set( Style::A_HEIGHT, 20 ); + + s->SetFont( Style::A_FONT, "arialnb.ttf", 18 ); + s->SetSound( Style::A_BUTTON_CLICK_SOUND, "ok.wav" ); + s->Set( Style::A_SOUND_VOLUME, 0.5 ); + } ); AddStyle( - "PopupText", SH() { - s->SetFont( Style::A_FONT, "arialnb.ttf", 18 ); - s->SetColor( Style::A_TEXT_COLOR, Color::FromRGB( 129, 146, 198 ) ); - } + "PopupText", SH() + { + s->SetFont( Style::A_FONT, "arialnb.ttf", 18 ); + s->SetColor( Style::A_TEXT_COLOR, Color::FromRGB( 129, 146, 198 ) ); + } ); AddStyle( - "Popup", { "PopupFrame" }, SH() { - s->Set( Style::A_Z_INDEX, 0.8f ); - } + "Popup", { "PopupFrame" }, SH() + { + s->Set( Style::A_Z_INDEX, 0.8f ); + } ); // TODO: fix overlay fbo blending @@ -200,164 +211,175 @@ void Popup::AddStyles() { });*/ AddStyle( - "PopupTextList", { "PopupText" }, SH() { - s->Set( Style::A_ITEM_HEIGHT, 17 ); // TODO: auto-size? - s->Set( Style::A_ITEM_MARGIN, 0 ); // TODO: fix alignment if non-zero - s->Set( Style::A_TEXT_ALIGN, UIObject::ALIGN_LEFT ); - } + "PopupTextList", { "PopupText" }, SH() + { + s->Set( Style::A_ITEM_HEIGHT, 17 ); // TODO: auto-size? + s->Set( Style::A_ITEM_MARGIN, 0 ); // TODO: fix alignment if non-zero + s->Set( Style::A_TEXT_ALIGN, UIObject::ALIGN_LEFT ); + } ); AddStyle( - "PopupFileList", SH() { - s->Set( Style::A_RIGHT, 4 ); - s->Set( Style::A_BOTTOM, 30 ); // to fit text input - s->SetFont( ::Style::A_FONT, "arialn.ttf", 16 ); - s->Set( Style::A_ITEM_HEIGHT, 17 ); // TODO: auto-size? - } + "PopupFileList", SH() + { + s->Set( Style::A_RIGHT, 4 ); + s->Set( Style::A_BOTTOM, 30 ); // to fit text input + s->SetFont( ::Style::A_FONT, "arialn.ttf", 16 ); + s->Set( Style::A_ITEM_HEIGHT, 17 ); // TODO: auto-size? + } ); AddStyle( - "PopupFileListInput", { "PopupBorder" }, SH() { - s->SetColor( Style::A_TEXT_COLOR, Color::FromRGB( 92, 124, 188 ) ); - s->SetColor( Style::A_HINT_COLOR, Color::FromRGBA( 92, 124, 188, 127 ) ); - s->SetFont( Style::A_FONT, "arialn.ttf", 18 ); - s->Set( Style::A_HEIGHT, 22 ); - s->Set( Style::A_ALIGN, UIObject::ALIGN_HCENTER | UIObject::ALIGN_BOTTOM ); - s->Set( Style::A_BOTTOM, 2 ); - s->Set( Style::A_LEFT, 4 ); - s->Set( Style::A_RIGHT, 4 ); - } + "PopupFileListInput", { "PopupBorder" }, SH() + { + s->SetColor( Style::A_TEXT_COLOR, Color::FromRGB( 92, 124, 188 ) ); + s->SetColor( Style::A_HINT_COLOR, Color::FromRGBA( 92, 124, 188, 127 ) ); + s->SetFont( Style::A_FONT, "arialn.ttf", 18 ); + s->Set( Style::A_HEIGHT, 22 ); + s->Set( Style::A_ALIGN, UIObject::ALIGN_HCENTER | UIObject::ALIGN_BOTTOM ); + s->Set( Style::A_BOTTOM, 2 ); + s->Set( Style::A_LEFT, 4 ); + s->Set( Style::A_RIGHT, 4 ); + } ); AddStyle( - "PopupFileListItem", SH() { - s->Set( Style::A_TEXT_ALIGN, UIObject::ALIGN_LEFT | UIObject::ALIGN_VCENTER ); - s->Set( Style::A_TEXT_LEFT, 20 ); - - if ( s->Is( Style::M_SELECTED ) ) { - s->SetTexture( Style::A_ITEM_TEXTURE, "interface.pcx", 68, 169, 134, 188 ); - s->SetColor( ::Style::A_TEXT_COLOR, Color::FromRGB( 13, 23, 36 ) ); - } - else if ( s->Is( Style::M_HOVER ) ) { - s->SetTexture( Style::A_ITEM_TEXTURE, "interface.pcx", 68, 149, 134, 166, TextureLoader::LT_CONTRAST, 0.7f ); - s->SetColor( ::Style::A_TEXT_COLOR, Color::FromRGB( 120, 164, 212 ) ); - } - else { - s->SetColor( ::Style::A_TEXT_COLOR, Color::FromRGB( 92, 124, 188 ) ); - - // TODO: fix styles where texture is absent in normal modifier and remove this hack - s->SetColorTexture( Style::A_ITEM_TEXTURE, Color( 0.0f, 0.0f, 0.0f, 0.0f ) ); - } + "PopupFileListItem", SH() + { + s->Set( Style::A_TEXT_ALIGN, UIObject::ALIGN_LEFT | UIObject::ALIGN_VCENTER ); + s->Set( Style::A_TEXT_LEFT, 20 ); + + if ( s->Is( Style::M_SELECTED ) ) { + s->SetTexture( Style::A_ITEM_TEXTURE, "interface.pcx", 68, 169, 134, 188 ); + s->SetColor( ::Style::A_TEXT_COLOR, Color::FromRGB( 13, 23, 36 ) ); + } + else if ( s->Is( Style::M_HOVER ) ) { + s->SetTexture( Style::A_ITEM_TEXTURE, "interface.pcx", 68, 149, 134, 166, TextureLoader::LT_CONTRAST, 0.7f ); + s->SetColor( ::Style::A_TEXT_COLOR, Color::FromRGB( 120, 164, 212 ) ); + } + else { + s->SetColor( ::Style::A_TEXT_COLOR, Color::FromRGB( 92, 124, 188 ) ); - s->Set( Style::A_ITEM_ICON_WIDTH, 16 ); + // TODO: fix styles where texture is absent in normal modifier and remove this hack + s->SetColorTexture( Style::A_ITEM_TEXTURE, Color( 0.0f, 0.0f, 0.0f, 0.0f ) ); } - ); - const auto tc = Color::RGB( 100, 16, 156 ); + s->Set( Style::A_ITEM_ICON_WIDTH, 16 ); + } + ); AddStyle( - "PopupFileListItemFile", { "PopupFileListItem" }, SH( tc ) { - s->SetTextureTC( Style::A_ITEM_ICON_TEXTURE, "Jackal.pcx", 18, 1, 33, 16, tc ); - } + "PopupFileListItemFile", { "PopupFileListItem" }, SH() + { + s->SetTexture( Style::A_ITEM_ICON_TEXTURE, "Jackal.pcx", 18, 1, 33, 16 ); + } ); AddStyle( - "PopupFileListItemDir", { "PopupFileListItem" }, SH( tc ) { - s->SetTextureTC( Style::A_ITEM_ICON_TEXTURE, "Jackal.pcx", 1, 1, 16, 16, tc ); - } + "PopupFileListItemDir", { "PopupFileListItem" }, SH() + { + s->SetTexture( Style::A_ITEM_ICON_TEXTURE, "Jackal.pcx", 1, 1, 16, 16 ); + } ); AddStyle( - "PopupFileListItemDirUp", { "PopupFileListItem" }, SH( tc ) { - s->SetTextureTC( Style::A_ITEM_ICON_TEXTURE, "Jackal.pcx", 35, 1, 50, 16, tc ); - } + "PopupFileListItemDirUp", { "PopupFileListItem" }, SH() + { + s->SetTexture( Style::A_ITEM_ICON_TEXTURE, "Jackal.pcx", 35, 1, 50, 16 ); + } ); // TODO: multilevel includes AddStyle( "DefaultPopupFrame", { "PopupFrame" - }, SH() { - s->SetFont( Style::A_HEADER_FONT, "arialnb.ttf", 18 ); - s->Set( Style::A_HEADER_HEIGHT, 22 ); - s->SetColor( Style::A_TEXT_COLOR, Color::FromRGB( 109, 126, 178 ) ); - s->SetFont( Style::A_FONT, "arialnb.ttf", 20 ); - } + }, SH() + { + s->SetFont( Style::A_HEADER_FONT, "arialnb.ttf", 18 ); + s->Set( Style::A_HEADER_HEIGHT, 22 ); + s->SetColor( Style::A_TEXT_COLOR, Color::FromRGB( 109, 126, 178 ) ); + s->SetFont( Style::A_FONT, "arialnb.ttf", 20 ); + } ); AddStyle( - "DefaultPopupLabel", SH() { - s->SetColor( Style::A_TEXT_COLOR, Color::FromRGB( 109, 126, 178 ) ); - s->SetFont( Style::A_FONT, "arialnb.ttf", 20 ); - } + "DefaultPopupLabel", SH() + { + s->SetColor( Style::A_TEXT_COLOR, Color::FromRGB( 109, 126, 178 ) ); + s->SetFont( Style::A_FONT, "arialnb.ttf", 20 ); + } ); AddStyle( "DefaultPopupButton", { "PopupButton" - }, SH() { - s->Set( Style::A_WIDTH, 234 ); - s->Set( Style::A_HEIGHT, 20 ); - - s->SetFont( Style::A_FONT, "arialnb.ttf", 18 ); - s->SetSound( Style::A_BUTTON_CLICK_SOUND, "ok.wav" ); - s->Set( Style::A_SOUND_VOLUME, 0.5 ); - } + }, SH() + { + s->Set( Style::A_WIDTH, 234 ); + s->Set( Style::A_HEIGHT, 20 ); + + s->SetFont( Style::A_FONT, "arialnb.ttf", 18 ); + s->SetSound( Style::A_BUTTON_CLICK_SOUND, "ok.wav" ); + s->Set( Style::A_SOUND_VOLUME, 0.5 ); + } ); AddStyle( "PopupDropdown", { "PopupFrame" - }, SH() { - s->Set( Style::A_HEIGHT, 24 ); - s->SetFont( Style::A_FONT, "arialnb.ttf", 18 ); - s->SetColor( Style::A_TEXT_COLOR, Color::FromRGB( 255, 255, 255 ) ); - s->Set( Style::A_TEXT_ALIGN, UIObject::ALIGN_LEFT | UIObject::ALIGN_TOP ); - s->Set( Style::A_TEXT_LEFT, 4 ); - s->Set( Style::A_PADDING, 2 ); - - } + }, SH() + { + s->Set( Style::A_HEIGHT, 24 ); + s->SetFont( Style::A_FONT, "arialnb.ttf", 18 ); + s->SetColor( Style::A_TEXT_COLOR, Color::FromRGB( 255, 255, 255 ) ); + s->Set( Style::A_TEXT_ALIGN, UIObject::ALIGN_LEFT | UIObject::ALIGN_TOP ); + s->Set( Style::A_TEXT_LEFT, 4 ); + s->Set( Style::A_PADDING, 2 ); + + } ); AddStyle( - "PopupDropdownOpenClose", SH() { - s->Set( Style::A_WIDTH, 7 ); - s->Set( Style::A_HEIGHT, 13 ); - s->Set( Style::A_ALIGN, Style::A_RIGHT ); - s->Set( Style::A_TOP, 3 ); - s->Set( Style::A_RIGHT, 3 ); - - if ( s->Is( Style::M_ACTIVE ) ) { - s->SetTexture( Style::A_TEXTURE, "interface.pcx", 550, 537, 556, 549 ); - } - else if ( s->Is( Style::M_HOVER ) ) { - s->SetTexture( Style::A_TEXTURE, "interface.pcx", 542, 537, 548, 549 ); - } - else { - s->SetTexture( Style::A_TEXTURE, "interface.pcx", 534, 537, 540, 549 ); - } + "PopupDropdownOpenClose", SH() + { + s->Set( Style::A_WIDTH, 7 ); + s->Set( Style::A_HEIGHT, 13 ); + s->Set( Style::A_ALIGN, Style::A_RIGHT ); + s->Set( Style::A_TOP, 3 ); + s->Set( Style::A_RIGHT, 3 ); + + if ( s->Is( Style::M_ACTIVE ) ) { + s->SetTexture( Style::A_TEXTURE, "interface.pcx", 550, 537, 556, 549 ); + } + else if ( s->Is( Style::M_HOVER ) ) { + s->SetTexture( Style::A_TEXTURE, "interface.pcx", 542, 537, 548, 549 ); } + else { + s->SetTexture( Style::A_TEXTURE, "interface.pcx", 534, 537, 540, 549 ); + } + } ); AddStyle( - "PopupDropdownChoices", SH() { + "PopupDropdownChoices", SH() + { - s->Set( Style::A_ITEM_MARGIN, 0 ); - s->Set( Style::A_ITEM_HEIGHT, 19 ); - s->Set( Style::A_BORDER_SIZE, 0 ); + s->Set( Style::A_ITEM_MARGIN, 0 ); + s->Set( Style::A_ITEM_HEIGHT, 19 ); + s->Set( Style::A_BORDER_SIZE, 0 ); - s->SetFont( Style::A_FONT, "arialnb.ttf", 17 ); - s->Set( Style::A_TEXT_ALIGN, UIObject::ALIGN_LEFT | UIObject::ALIGN_TOP ); + s->SetFont( Style::A_FONT, "arialnb.ttf", 17 ); + s->Set( Style::A_TEXT_ALIGN, UIObject::ALIGN_LEFT | UIObject::ALIGN_TOP ); - if ( s->Is( Style::M_HOVER ) ) { - s->SetColor( Style::A_TEXT_COLOR, Color::FromRGB( 53, 61, 115 ) ); - s->SetTexture( Style::A_TEXTURE_BACK, "interface.pcx", 68, 169, 134, 188 ); - } - else { - s->SetColor( Style::A_TEXT_COLOR, Color::FromRGB( 176, 212, 235 ) ); - s->SetTexture( Style::A_TEXTURE_BACK, "interface.pcx", 68, 127, 134, 146 ); - } + if ( s->Is( Style::M_HOVER ) ) { + s->SetColor( Style::A_TEXT_COLOR, Color::FromRGB( 53, 61, 115 ) ); + s->SetTexture( Style::A_TEXTURE_BACK, "interface.pcx", 68, 169, 134, 188 ); + } + else { + s->SetColor( Style::A_TEXT_COLOR, Color::FromRGB( 176, 212, 235 ) ); + s->SetTexture( Style::A_TEXTURE_BACK, "interface.pcx", 68, 127, 134, 146 ); } + } ); } diff --git a/src/task/common/ui/style/Scrollbars.cpp b/src/task/common/ui/style/Scrollbars.cpp index 434c1c8f..228bfdce 100644 --- a/src/task/common/ui/style/Scrollbars.cpp +++ b/src/task/common/ui/style/Scrollbars.cpp @@ -15,23 +15,22 @@ void Scrollbars::AddStyles() { AddStyle( "VerticalThick", { "Vertical" }, SH() { - const auto tc = Color::RGB( 152, 24, 228 ); s->Set( Style::A_WIDTH, 18 ); if ( s->Is( Style::M_ACTIVE ) ) { - s->SetTextureTC( Style::A_TEXTURE_TOP, "Icons.pcx", 157, 4, 174, 21, tc ); - s->SetTextureTC( Style::A_TEXTURE_CENTER, "Icons.pcx", 104, 101, 121, 118, tc, TextureLoader::LT_CONTRAST, 2.0f ); - s->SetTextureTC( Style::A_TEXTURE_BOTTOM, "Icons.pcx", 157, 29, 174, 46, tc ); + s->SetTexture( Style::A_TEXTURE_TOP, "Icons.pcx", 157, 4, 174, 21 ); + s->SetTexture( Style::A_TEXTURE_CENTER, "Icons.pcx", 104, 101, 121, 118, TextureLoader::LT_CONTRAST, 2.0f ); + s->SetTexture( Style::A_TEXTURE_BOTTOM, "Icons.pcx", 157, 29, 174, 46 ); } else if ( s->Is( Style::M_HOVER ) ) { - s->SetTextureTC( Style::A_TEXTURE_TOP, "Icons.pcx", 132, 4, 149, 21, tc ); - s->SetTextureTC( Style::A_TEXTURE_CENTER, "Icons.pcx", 104, 101, 121, 118, tc, TextureLoader::LT_CONTRAST, 1.5f ); - s->SetTextureTC( Style::A_TEXTURE_BOTTOM, "Icons.pcx", 132, 29, 149, 46, tc ); + s->SetTexture( Style::A_TEXTURE_TOP, "Icons.pcx", 132, 4, 149, 21 ); + s->SetTexture( Style::A_TEXTURE_CENTER, "Icons.pcx", 104, 101, 121, 118, TextureLoader::LT_CONTRAST, 1.5f ); + s->SetTexture( Style::A_TEXTURE_BOTTOM, "Icons.pcx", 132, 29, 149, 46 ); } else { - s->SetTextureTC( Style::A_TEXTURE_TOP, "Icons.pcx", 107, 4, 124, 21, tc ); - s->SetTextureTC( Style::A_TEXTURE_CENTER, "Icons.pcx", 104, 101, 121, 118, tc ); - s->SetTextureTC( Style::A_TEXTURE_BOTTOM, "Icons.pcx", 107, 29, 124, 46, tc ); + s->SetTexture( Style::A_TEXTURE_TOP, "Icons.pcx", 107, 4, 124, 21 ); + s->SetTexture( Style::A_TEXTURE_CENTER, "Icons.pcx", 104, 101, 121, 118 ); + s->SetTexture( Style::A_TEXTURE_BOTTOM, "Icons.pcx", 107, 29, 124, 46 ); } } ); diff --git a/src/task/game/Game.cpp b/src/task/game/Game.cpp index c807d270..1eee01e6 100644 --- a/src/task/game/Game.cpp +++ b/src/task/game/Game.cpp @@ -403,15 +403,7 @@ types::Texture* Game::GetSourceTexture( const std::string& name ) { if ( it != m_textures.source.end() ) { return it->second; } - auto* texture = g_engine->GetTextureLoader()->LoadTextureTCs( - name, { - Color::RGB( 152, 24, 228 ), // remove transparency color - Color::RGB( 100, 16, 156 ), // remove second transparency color - Color::RGB( 24, 184, 228 ), // remove frame - Color::RGB( 253, 189, 118 ), // remove drawn shadows too (we'll have our own) - Color::RGB( 124, 124, 124 ), // remove badges background // TODO: use per-file transparency selection - } - ); + auto* texture = g_engine->GetTextureLoader()->LoadTexture( name ); ASSERT( texture, "texture not loaded" ); m_textures.source.insert_or_assign( name, texture ); return texture; diff --git a/src/task/game/ui/style/BottomBar.cpp b/src/task/game/ui/style/BottomBar.cpp index e3c35315..e9aff52f 100644 --- a/src/task/game/ui/style/BottomBar.cpp +++ b/src/task/game/ui/style/BottomBar.cpp @@ -29,7 +29,7 @@ void BottomBar::AddStyles() { "FrameLeft", { "Frame" }, SH() { s->Set( ::Style::A_ALIGN, UIObject::ALIGN_BOTTOM | UIObject::ALIGN_LEFT ); s->Set( ::Style::A_WIDTH, 356 ); - s->SetTextureTC( ::Style::A_TEXTURE, "console2_A.pcx", 0, 0, 356, 256, Color::RGB( 100, 16, 156 ) ); + s->SetTexture( ::Style::A_TEXTURE, "console2_A.pcx", 0, 0, 356, 256 ); } ); @@ -38,7 +38,7 @@ void BottomBar::AddStyles() { s->Set( ::Style::A_ALIGN, UIObject::ALIGN_BOTTOM ); s->Set( ::Style::A_LEFT, 356 ); s->Set( ::Style::A_RIGHT, 520 ); - s->SetTextureTC( ::Style::A_TEXTURE, "console2_A.pcx", 357, 0, ( 1024 - 521 ), 256, Color::RGB( 100, 16, 156 ) ); + s->SetTexture( ::Style::A_TEXTURE, "console2_A.pcx", 357, 0, ( 1024 - 521 ), 256 ); } ); @@ -46,7 +46,7 @@ void BottomBar::AddStyles() { "FrameRight", { "Frame" }, SH() { s->Set( ::Style::A_ALIGN, UIObject::ALIGN_BOTTOM | UIObject::ALIGN_RIGHT ); s->Set( ::Style::A_WIDTH, 520 ); - s->SetTextureTC( ::Style::A_TEXTURE, "console2_A.pcx", ( 1024 - 520 ), 0, 1023, 256, Color::RGB( 100, 16, 156 ) ); + s->SetTexture( ::Style::A_TEXTURE, "console2_A.pcx", ( 1024 - 520 ), 0, 1023, 256 ); } ); @@ -638,7 +638,7 @@ void BottomBar::AddStyles() { s->Set( ::Style::A_WIDTH, 139 ); s->Set( ::Style::A_ALIGN, UIObject::ALIGN_TOP ); s->Set( ::Style::A_TOP, -4 ); - s->SetTextureTC( ::Style::A_TEXTURE, "console2_A.pcx", 0, 0, 138, 4, Color::RGB( 100, 16, 156 ) ); + s->SetTexture( ::Style::A_TEXTURE, "console2_A.pcx", 0, 0, 138, 4 ); } ); @@ -648,7 +648,7 @@ void BottomBar::AddStyles() { s->Set( ::Style::A_WIDTH, 139 ); s->Set( ::Style::A_ALIGN, UIObject::ALIGN_TOP | UIObject::ALIGN_RIGHT ); s->Set( ::Style::A_TOP, -5 ); - s->SetTextureTC( ::Style::A_TEXTURE, "console2_A.pcx", 885, 1, 1023, 6, Color::RGB( 100, 16, 156 ) ); + s->SetTexture( ::Style::A_TEXTURE, "console2_A.pcx", 885, 1, 1023, 6 ); } ); diff --git a/src/task/game/ui/style/Popup.cpp b/src/task/game/ui/style/Popup.cpp index 8d5caf9f..b6d33639 100644 --- a/src/task/game/ui/style/Popup.cpp +++ b/src/task/game/ui/style/Popup.cpp @@ -131,7 +131,7 @@ void Popup::AddStyles() { s->Set( Style::A_LEFT, 8 ); s->Set( Style::A_TOP, 36 ); - s->SetTextureTC( Style::A_TEXTURE, "space_sm.pcx", Color::RGB( 100, 16, 156 ) ); + s->SetTexture( Style::A_TEXTURE, "space_sm.pcx" ); } ); diff --git a/src/ui/theme/Style.cpp b/src/ui/theme/Style.cpp index 1bdd2e5d..51447db2 100644 --- a/src/ui/theme/Style.cpp +++ b/src/ui/theme/Style.cpp @@ -61,22 +61,10 @@ void Style::SetTexture( const attribute_type_t attribute_type, const std::string SetObject( attribute_type, g_engine->GetTextureLoader()->LoadTexture( name ) ); } -void Style::SetTextureTC( const attribute_type_t attribute_type, const std::string& name, const Color::rgba_t transparent_color ) { - SetObject( attribute_type, g_engine->GetTextureLoader()->LoadTextureTC( name, transparent_color ) ); -} - void Style::SetTexture( const attribute_type_t attribute_type, const std::string& name, const size_t x1, const size_t y1, const size_t x2, const size_t y2, const uint8_t flags, const float value ) { SetObject( attribute_type, g_engine->GetTextureLoader()->LoadTexture( name, x1, y1, x2, y2, flags, value ) ); } -void Style::SetTextureTC( const attribute_type_t attribute_type, const std::string& name, const size_t x1, const size_t y1, const size_t x2, const size_t y2, const Color::rgba_t transparent_color, const uint8_t flags, const float value ) { - SetObject( attribute_type, g_engine->GetTextureLoader()->LoadTextureTC( name, x1, y1, x2, y2, transparent_color, flags, value ) ); -} - -void Style::SetTextureTCs( const attribute_type_t attribute_type, const std::string& name, const size_t x1, const size_t y1, const size_t x2, const size_t y2, const TextureLoader::transparent_colors_t& transparent_colors, const uint8_t flags, const float value ) { - SetObject( attribute_type, g_engine->GetTextureLoader()->LoadTextureTCs( name, x1, y1, x2, y2, transparent_colors, flags, value ) ); -} - void Style::SetColorTexture( const attribute_type_t attribute_type, const Color& color ) { SetObject( attribute_type, g_engine->GetTextureLoader()->GetColorTexture( color ) ); } diff --git a/src/ui/theme/Style.h b/src/ui/theme/Style.h index 26178bfa..48b0c13a 100644 --- a/src/ui/theme/Style.h +++ b/src/ui/theme/Style.h @@ -109,7 +109,7 @@ CLASS( Style, base::Base ) A_HEADER_TEXTURE_TOP, A_HEADER_TEXTURE_RIGHT, A_HEADER_TEXTURE_BOTTOM, - + // lists etc A_ITEM_WIDTH, A_ITEM_HEIGHT, @@ -141,10 +141,7 @@ CLASS( Style, base::Base ) void SetObject( const attribute_type_t attribute_type, const void* value ); // convenience setters // TODO: improve SetTexture* API void SetTexture( const attribute_type_t attribute_type, const std::string& name ); - void SetTextureTC( const attribute_type_t attribute_type, const std::string& name, const Color::rgba_t transparent_color ); void SetTexture( const attribute_type_t attribute_type, const std::string& name, const size_t x1, const size_t y1, const size_t x2, const size_t y2, const uint8_t flags = TextureLoader::LT_NONE, const float value = 1.0 ); - void SetTextureTC( const attribute_type_t attribute_type, const std::string& name, const size_t x1, const size_t y1, const size_t x2, const size_t y2, const Color::rgba_t transparent_color, const uint8_t flags = TextureLoader::LT_NONE, const float value = 1.0 ); - void SetTextureTCs( const attribute_type_t attribute_type, const std::string& name, const size_t x1, const size_t y1, const size_t x2, const size_t y2, const TextureLoader::transparent_colors_t& transparent_colors, const uint8_t flags = TextureLoader::LT_NONE, const float value = 1.0 ); void SetColorTexture( const attribute_type_t attribute_type, const Color& color ); void SetFont( const attribute_type_t attribute_type, const std::string& name, const unsigned char size ); void SetSound( const attribute_type_t attribute_type, const std::string& name ); From c868ed099b64ecb521f2d2a423916dec0452847a Mon Sep 17 00:00:00 2001 From: afwbkbc Date: Sun, 18 Feb 2024 13:38:24 +0200 Subject: [PATCH 13/21] colored unit badges --- src/task/game/Game.cpp | 136 +++++++++++++++++++++++++++++++++++------ src/task/game/Game.h | 19 ++++-- src/types/Texture.cpp | 32 +++++++++- src/types/Texture.h | 9 ++- 4 files changed, 168 insertions(+), 28 deletions(-) diff --git a/src/task/game/Game.cpp b/src/task/game/Game.cpp index 1eee01e6..0d0d119e 100644 --- a/src/task/game/Game.cpp +++ b/src/task/game/Game.cpp @@ -405,7 +405,28 @@ types::Texture* Game::GetSourceTexture( const std::string& name ) { } auto* texture = g_engine->GetTextureLoader()->LoadTexture( name ); ASSERT( texture, "texture not loaded" ); - m_textures.source.insert_or_assign( name, texture ); + m_textures.source.insert( + { + name, + texture + } + ); + return texture; +} + +types::Texture* Game::GetRepaintedSourceTexture( const std::string& name, const types::Texture* original, const types::Texture::repaint_rules_t& rules ) { + const auto it = m_textures.repainted_source.find( name ); + if ( it != m_textures.repainted_source.end() ) { + return it->second; + } + NEWV( texture, types::Texture, original->m_name, original->m_width, original->m_height ); + texture->RepaintFrom( original, rules ); + m_textures.repainted_source.insert( + { + name, + texture + } + ); return texture; } @@ -421,12 +442,12 @@ Game::instanced_sprite_t& Game::GetInstancedSprite( const auto key = name + " " + tex_file + " " + src_xy.ToString() + " " + src_wh.ToString(); - const auto& it = m_instanced_sprites.find( key ); + auto it = m_instanced_sprites.find( key ); if ( it == m_instanced_sprites.end() ) { - Log( "Creating instanced sprite actor: " + key ); + Log( "Creating instanced sprite: " + key ); - auto* pcx = GetSourceTexture( tex_file ); + auto* texture = GetSourceTexture( tex_file ); NEWV( sprite, @@ -436,25 +457,26 @@ Game::instanced_sprite_t& Game::GetInstancedSprite( dst_wh.x, dst_wh.y }, - pcx, + texture, { { - (float)1.0f / pcx->m_width * ( src_xy.x ), - (float)1.0f / pcx->m_height * ( src_xy.y ) + (float)1.0f / texture->m_width * ( src_xy.x ), + (float)1.0f / texture->m_height * ( src_xy.y ) }, { - (float)1.0f / pcx->m_width * ( src_xy.x + src_wh.x ), - (float)1.0f / pcx->m_height * ( src_xy.y + src_wh.y ) + (float)1.0f / texture->m_width * ( src_xy.x + src_wh.x ), + (float)1.0f / texture->m_height * ( src_xy.y + src_wh.y ) } } ); NEWV( instanced, scene::actor::Instanced, sprite ); instanced->SetZIndex( z_index ); // needs to be higher than map terrain z position m_world_scene->AddActor( instanced ); - return m_instanced_sprites.insert( + it = m_instanced_sprites.insert( { key, { + key, name, src_xy, src_wh, @@ -462,11 +484,9 @@ Game::instanced_sprite_t& Game::GetInstancedSprite( instanced } } - ).first->second; - } - else { - return it->second; + ).first; } + return it->second; } Game::instanced_sprite_t& Game::GetInstancedSpriteByKey( const std::string& key ) { @@ -493,6 +513,55 @@ Game::instanced_sprite_t& Game::GetTerrainInstancedSprite( const ::game::map::Ma ); } +Game::instanced_sprite_t* Game::GetRepaintedInstancedSprite( const std::string& name, const Game::instanced_sprite_t& original, const types::Texture::repaint_rules_t& rules ) { + const auto& key = name; + + auto it = m_instanced_sprites.find( key ); + if ( it == m_instanced_sprites.end() ) { + + Log( "Creating repainted instanced sprite: " + key ); + + const auto* original_sprite = original.actor->GetSpriteActor(); + auto* texture = GetRepaintedSourceTexture( name, original_sprite->GetTexture(), rules ); + + NEWV( + sprite, + scene::actor::Sprite, + name, + original_sprite->GetDimensions(), + texture, + original_sprite->GetTexCoords() + ); + NEWV( instanced, scene::actor::Instanced, sprite ); + instanced->SetZIndex( original.actor->GetZIndex() ); + m_world_scene->AddActor( instanced ); + it = m_instanced_sprites.insert( + { + key, + { + key, + name, + original.xy, + original.wh, + original.cxy, + instanced + } + } + ).first; + + } + return &it->second; +} + +void Game::RemoveInstancedSpriteByKey( const std::string& key ) { + const auto& it = m_instanced_sprites.find( key ); + ASSERT( it != m_instanced_sprites.end(), "instanced sprite not found: " + key ); + Log( "Removing instanced sprite: " + key ); + const auto& sprite = it->second; + m_world_scene->RemoveActor( sprite.actor ); + m_instanced_sprites.erase( it ); +} + void Game::CenterAtCoordinatePercents( const Vec2< float > position_percents ) { const Vec2< float > position = { m_map_data.range.percent_to_absolute.x.Clamp( position_percents.x ), @@ -797,7 +866,28 @@ void Game::DefineSlot( const size_t slot_index, const types::Color& color ) { } ).first; } - slot_it->second.color = color; + auto& slot = slot_it->second; + if ( slot.color != color || !slot.badge.instanced_sprite ) { + const auto badges_key = "Badges-" + std::to_string( slot_index ); + if ( slot.badge.instanced_sprite ) { + RemoveInstancedSpriteByKey( badges_key ); + } + const auto& c = color.value; + slot.badge.instanced_sprite = GetRepaintedInstancedSprite( + badges_key, *m_unitbadge_default.instanced_sprite, { + { // active + types::Color::RGB( 252, 0, 0 ), + types::Color( c.red, c.green, c.blue ).GetRGBA() + }, + { // greyed out + types::Color::RGB( 125, 0, 0 ), + types::Color( c.red / 2, c.green / 2, c.blue / 2 ).GetRGBA() + } + } + ); + slot.badge.next_instance_id = 1; + } + slot.color = color; } void Game::DefineUnit( const ::game::unit::Def* unitdef ) { @@ -863,9 +953,10 @@ void Game::SpawnUnit( const size_t unit_id, const std::string& unitdef_name, con ASSERT( m_unit_states.find( unit_id ) == m_unit_states.end(), "unit id already exists" ); auto& unitdef_state = m_unitdef_states.at( unitdef_name ); + auto& slot_state = m_slot_states.at( slot_index ); unit_state_t unit_state = { &unitdef_state, - &m_slot_states.at( slot_index ) + &slot_state }; ASSERT( unitdef_state.m_type == ::game::unit::Def::DT_STATIC, "only static unitdefs are supported for now" ); @@ -882,7 +973,8 @@ void Game::SpawnUnit( const size_t unit_id, const std::string& unitdef_name, con ); // add badge render - unit_state.badge_def = &m_unitbadge_default; // TODO: variations + unit_state.badge_def = &slot_state.badge; // TODO: variations + unit_state.badge_instance_id = unit_state.badge_def->next_instance_id++; unit_state.badge_def->instanced_sprite->actor->SetInstance( unit_state.badge_instance_id, { @@ -1110,7 +1202,7 @@ void Game::Initialize( // Predefined sprites // TODO: variations m_unitbadge_default.instanced_sprite = &GetInstancedSprite( - "Badge_DEFAULT", "flags.pcx", { + "Badges", "flags.pcx", { 49, 63, }, @@ -1491,7 +1583,15 @@ void Game::Deinitialize() { if ( m_textures.terrain ) { DELETE( m_textures.terrain ); + m_textures.terrain = nullptr; + } + + m_slot_states.clear(); + + for ( const auto& it : m_textures.repainted_source ) { + DELETE( it.second ); } + m_textures.repainted_source.clear(); for ( auto& it : m_actors_map ) { m_world_scene->RemoveActor( it.second ); diff --git a/src/task/game/Game.h b/src/task/game/Game.h index 53c135fe..bc2d4ad0 100644 --- a/src/task/game/Game.h +++ b/src/task/game/Game.h @@ -91,7 +91,10 @@ CLASS( Game, base::Task ) types::Texture* GetSourceTexture( const std::string& name ); + types::Texture* GetRepaintedSourceTexture( const std::string& name, const types::Texture* original, const types::Texture::repaint_rules_t& rules ); + struct instanced_sprite_t { + std::string key; std::string name; ::game::map::Consts::pcx_texture_coordinates_t xy; ::game::map::Consts::pcx_texture_coordinates_t wh; @@ -109,6 +112,9 @@ CLASS( Game, base::Task ) ); instanced_sprite_t& GetInstancedSpriteByKey( const std::string& key ); // actor must already exist instanced_sprite_t& GetTerrainInstancedSprite( const ::game::map::Map::sprite_actor_t& actor ); + instanced_sprite_t* GetRepaintedInstancedSprite( const std::string& name, const instanced_sprite_t& original, const types::Texture::repaint_rules_t& rules ); + + void RemoveInstancedSpriteByKey( const std::string& key ); void CenterAtCoordinatePercents( const Vec2< float > position_percents ); @@ -293,6 +299,7 @@ CLASS( Game, base::Task ) struct { std::unordered_map< std::string, types::Texture* > source; + std::unordered_map< std::string, types::Texture* > repainted_source; types::Texture* terrain = nullptr; } m_textures; @@ -352,11 +359,6 @@ CLASS( Game, base::Task ) #endif } m_mt_ids = {}; - struct slot_state_t { - types::Color color; - }; - std::unordered_map< size_t, slot_state_t > m_slot_states = {}; - struct unitdef_state_t { ::game::unit::Def::def_type_t m_type; union { @@ -382,10 +384,15 @@ CLASS( Game, base::Task ) static const float OFFSET_Y; Game::instanced_sprite_t* instanced_sprite = nullptr; size_t next_instance_id = 1; - size_t instance_id; }; unitbadge_def_t m_unitbadge_default = {}; // TODO: variations + struct slot_state_t { + types::Color color; + unitbadge_def_t badge; + }; + std::unordered_map< size_t, slot_state_t > m_slot_states = {}; + struct unit_state_t { unitdef_state_t* def = nullptr; slot_state_t* slot = nullptr; diff --git a/src/types/Texture.cpp b/src/types/Texture.cpp index 23ad91fd..78b0ce53 100644 --- a/src/types/Texture.cpp +++ b/src/types/Texture.cpp @@ -12,10 +12,12 @@ using namespace game::map; namespace types { +Texture::Texture() { + // nothing +} + Texture::Texture( const std::string& name, const size_t width, const size_t height ) : m_name( name ) { - m_bpp = 4; // always RGBA format - if ( width > 0 && height > 0 ) { Resize( width, height ); } @@ -33,6 +35,10 @@ Texture::~Texture() { } } +const bool Texture::IsEmpty() const { + return m_width == 0 && m_height == 0; +} + void Texture::Resize( const size_t width, const size_t height ) { if ( m_width != width || m_height != height ) { @@ -701,6 +707,28 @@ void Texture::AddFrom( const types::Texture* source, add_flag_t flags, const siz ); } +void Texture::RepaintFrom( const types::Texture* original, const repaint_rules_t& rules ) { + 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 ); + if ( ( rule_it = rules.find( rgba ) ) != rules.end() ) { + rgba = rule_it->second; + } + 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.h b/src/types/Texture.h index 39176c59..61f09b85 100644 --- a/src/types/Texture.h +++ b/src/types/Texture.h @@ -13,14 +13,15 @@ namespace types { CLASS( Texture, Serializable ) + Texture(); Texture( const std::string& name, const size_t width, const size_t height ); virtual ~Texture(); - std::string m_name; + std::string m_name = ""; size_t m_width = 0; size_t m_height = 0; float m_aspect_ratio = 0; - unsigned char m_bpp = 0; + unsigned char m_bpp = 4; // always RGBA format unsigned char* m_bitmap = nullptr; size_t m_bitmap_size = 0; @@ -28,6 +29,7 @@ CLASS( Texture, Serializable ) base::ObjectLink* m_graphics_object = nullptr; + const bool IsEmpty() const; void Resize( const size_t width, const size_t height ); // these methods won't update counter because it would happen too often (and is bad for performance) @@ -108,6 +110,9 @@ CLASS( Texture, Serializable ) */ void AddFrom( const types::Texture* source, add_flag_t flags, const size_t x1, const size_t y1, const size_t x2, const size_t y2, const size_t dest_x = 0, const size_t dest_y = 0, const rotate_t rotate = 0, const float alpha = 1.0f, util::Random* rng = nullptr, util::Perlin* perlin = nullptr ); + typedef std::unordered_map< types::Color::rgba_t, types::Color::rgba_t > repaint_rules_t; + void RepaintFrom( const types::Texture* original, const repaint_rules_t& rules ); + void Rotate(); void FlipV(); //void FlipH(); // TODO From 67c3f6dd4e8a58a4fc98b87eec39c8f5a76534d5 Mon Sep 17 00:00:00 2001 From: afwbkbc Date: Sun, 18 Feb 2024 15:09:04 +0200 Subject: [PATCH 14/21] import factions pcx, fixed unit badge grey colors --- gse/default/factions.gls.js | 56 ++++++++++++++----- src/game/Game.cpp | 2 +- src/game/Player.cpp | 5 +- src/game/bindings/Bindings.h | 2 - src/game/bindings/Factions.cpp | 12 ++-- src/game/rules/Faction.cpp | 33 ++++++++--- src/game/rules/Faction.h | 11 +++- src/gse/callable/Native.h | 2 +- src/task/game/Game.cpp | 2 +- .../mainmenu/menu/lobby/PlayersSectionRow.cpp | 12 ++-- src/types/Color.cpp | 8 +-- 11 files changed, 103 insertions(+), 42 deletions(-) diff --git a/gse/default/factions.gls.js b/gse/default/factions.gls.js index 93597fa5..d3019c54 100644 --- a/gse/default/factions.gls.js +++ b/gse/default/factions.gls.js @@ -3,60 +3,88 @@ return [ // SMAC ['GAIANS', { name: 'Gaians', - color: #to_color(16, 228, 0) + files: { + pcx: 'Gaians.pcx' + } }], ['HIVE', { name: 'Hive', - color: #to_color(0, 97, 235) + files: { + pcx: 'hive.pcx' + } }], ['UNIVERSITY', { name: 'University', - color: #to_color(216, 224, 235) + files: { + pcx: 'univ.pcx' + } }], ['MORGANITES', { name: 'Morganites', - color: #to_color(255, 255, 0) + files: { + pcx: 'morgan.pcx' + } }], ['SPARTANS', { name: 'Spartans', - color: #to_color(137, 166, 166) + files: { + pcx: 'spartans.pcx' + } }], ['BELIEVERS', { name: 'Believers', - color: #to_color(224, 156, 28) + files: { + pcx: 'believe.pcx' + } }], ['PEACEKEEPERS', { name: 'Peacekeepers', - color: #to_color(164, 176, 232) + files: { + pcx: 'peace.pcx' + } }], // SMACX ['CONSCIOUSNESS', { name: 'Consciousness', - color: #to_color(44, 128, 104) + files: { + pcx: 'cyborg.pcx' + } }], ['PIRATES', { name: 'Pirates', - color: #to_color(0, 255, 255) + files: { + pcx: 'pirates.pcx' + } }], ['DRONES', { name: 'Drones', - color: #to_color(173, 196, 192) + files: { + pcx: 'drone.pcx' + } }], ['ANGELS', { name: 'Angels', - color: #to_color(103, 91, 181) + files: { + pcx: 'angels.pcx' + } }], ['PLANETCULT', { name: 'Planet Cult', - color: #to_color(232, 84, 84) + files: { + pcx: 'fungboy.pcx' + } }], ['CARETAKERS', { name: 'Caretakers', - color: #to_color(116, 156, 56) + files: { + pcx: 'caretake.pcx' + } }], ['USURPERS', { name: 'Usurpers', - color: #to_color(212, 208, 116) + files: { + pcx: 'usurper.pcx' + } }], ]; diff --git a/src/game/Game.cpp b/src/game/Game.cpp index e4a0a016..ccebe7ad 100644 --- a/src/game/Game.cpp +++ b/src/game/Game.cpp @@ -279,7 +279,7 @@ void Game::Iterate() { const auto* player = slot.GetPlayer(); ASSERT( player, "slot player not set" ); const auto& faction = player->GetFaction(); - const auto& c = faction.m_color.value; + const auto& c = faction.m_colors.border.value; slot_defines->push_back( Event::slot_define_t{ slot.GetIndex(), diff --git a/src/game/Player.cpp b/src/game/Player.cpp index 37e3eab7..b289888b 100644 --- a/src/game/Player.cpp +++ b/src/game/Player.cpp @@ -5,7 +5,10 @@ using namespace types; namespace game { // builtin -const rules::Faction Player::RANDOM_FACTION( "RANDOM", "Random", types::Color( 1.0f, 1.0f, 1.0f ) ); +const rules::Faction Player::RANDOM_FACTION{ + "RANDOM", + "Random" +}; Player::Player( const rules::Rules& rules, Buffer buf ) : m_rules( rules ) { diff --git a/src/game/bindings/Bindings.h b/src/game/bindings/Bindings.h index 70f81ae7..72820823 100644 --- a/src/game/bindings/Bindings.h +++ b/src/game/bindings/Bindings.h @@ -7,8 +7,6 @@ #include "gse/GlobalContext.h" #include "gse/type/Callable.h" #include "game/Player.h" -#include "game/rules/Faction.h" -#include "game/rules/DifficultyLevel.h" #include "Binding.h" diff --git a/src/game/bindings/Factions.cpp b/src/game/bindings/Factions.cpp index fd907807..af9066b1 100644 --- a/src/game/bindings/Factions.cpp +++ b/src/game/bindings/Factions.cpp @@ -18,14 +18,16 @@ BINDING_IMPL( factions ) { NATIVE_CALL( this, &factions ) { N_EXPECT_ARGS( 2 ); N_GETVALUE( id, 0, String ); - N_GETVALUE( faction_def, 1, Object ); - N_GETPROP( name, faction_def, "name", String ); - N_GETPROP_UNWRAP( color, faction_def, "color", types::Color ); if ( factions.find( id ) != factions.end() ) { ERROR( gse::EC.GAME_ERROR, "Faction '" + id + "' already exists" ); } - const auto* faction = new rules::Faction( id, name, color ); - factions.insert({ id, rules::Faction{ id, name, color } }); + N_GETVALUE( faction_def, 1, Object ); + N_GETPROP( name, faction_def, "name", String ); + N_GETPROP( files, faction_def, "files", Object ); + N_GETPROP( pcx_file, files, "pcx", String ); + rules::Faction faction = { id, name }; + faction.ImportPCX( pcx_file ); + factions.insert({ id, faction }); return VALUE( gse::type::Undefined ); }) }, diff --git a/src/game/rules/Faction.cpp b/src/game/rules/Faction.cpp index bcaf1bb1..0e101fbf 100644 --- a/src/game/rules/Faction.cpp +++ b/src/game/rules/Faction.cpp @@ -2,6 +2,8 @@ #include "gse/type/String.h" +#include "engine/Engine.h" + namespace game { namespace rules { @@ -9,19 +11,25 @@ Faction::Faction() { // } -Faction::Faction( const std::string& id, const std::string& name, const types::Color& color ) +Faction::Faction( const std::string& id, const std::string& name ) : m_id( id ) - , m_name( name ) - , m_color( color ) { + , m_name( name ) { // } +void Faction::ImportPCX( const std::string& pcx_file ) { + const auto* texture = g_engine->GetTextureLoader()->LoadTexture( pcx_file ); + m_colors.text = types::Color::FromRGBA( texture->GetPixel( 5, 755 ) ); + m_colors.border = types::Color::FromRGBA( texture->GetPixel( 162, 750 ) ); +} + const types::Buffer Faction::Serialize() const { types::Buffer buf; buf.WriteString( m_id ); buf.WriteString( m_name ); - buf.WriteColor( m_color ); + buf.WriteColor( m_colors.text ); + buf.WriteColor( m_colors.border ); return buf; } @@ -30,11 +38,22 @@ void Faction::Unserialize( types::Buffer buf ) { m_id = buf.ReadString(); m_name = buf.ReadString(); - m_color = buf.ReadColor(); + m_colors.text = buf.ReadColor(); + m_colors.border = buf.ReadColor(); } WRAPIMPL_BEGIN( Faction, CLASS_FACTION ) + gse::type::Object::properties_t color_obj = { + { + "text", + m_colors.text.Wrap() + }, + { + "border", + m_colors.border.Wrap() + } + }; WRAPIMPL_PROPS { { "id", @@ -45,8 +64,8 @@ WRAPIMPL_BEGIN( Faction, CLASS_FACTION ) VALUE( gse::type::String, m_name ) }, { - "color", - m_color.Wrap() + "colors", + VALUE( gse::type::Object, color_obj ) }, }; WRAPIMPL_END_PTR( Faction ) diff --git a/src/game/rules/Faction.h b/src/game/rules/Faction.h index a63995bc..130f2a17 100644 --- a/src/game/rules/Faction.h +++ b/src/game/rules/Faction.h @@ -14,16 +14,23 @@ namespace rules { CLASS( Faction, types::Serializable ) Faction(); - Faction( const std::string& id, const std::string& name, const types::Color& color ); + Faction( const std::string& id, const std::string& name ); std::string m_id = ""; std::string m_name = ""; - types::Color m_color = {}; + + struct { + types::Color text = {}; + types::Color border = {}; + } m_colors = {}; + + void ImportPCX( const std::string& pcx_file ); const types::Buffer Serialize() const override; void Unserialize( types::Buffer buf ) override; WRAPDEFS_PTR( Faction ) + }; } diff --git a/src/gse/callable/Native.h b/src/gse/callable/Native.h index 8709b2f6..d1a9ba29 100644 --- a/src/gse/callable/Native.h +++ b/src/gse/callable/Native.h @@ -66,7 +66,7 @@ namespace callable { #define N_GETPROP_VAL( _obj, _key, _type ) \ obj_it = _obj.find( _key ); \ if ( obj_it == _obj.end() ) { \ - throw gse::Exception( gse::EC.INVALID_CALL, (std::string)"Property " + _key + " is expected but not found", ctx, call_si ); \ + throw gse::Exception( gse::EC.INVALID_CALL, (std::string)"Property '" + _key + "' is expected but not found", ctx, call_si ); \ } \ getprop_val = obj_it->second; #define N_GETPROP_ARG( _obj, _key, _type ) \ diff --git a/src/task/game/Game.cpp b/src/task/game/Game.cpp index 0d0d119e..8b0034f9 100644 --- a/src/task/game/Game.cpp +++ b/src/task/game/Game.cpp @@ -881,7 +881,7 @@ void Game::DefineSlot( const size_t slot_index, const types::Color& color ) { }, { // greyed out types::Color::RGB( 125, 0, 0 ), - types::Color( c.red / 2, c.green / 2, c.blue / 2 ).GetRGBA() + types::Color( ( 0.5f + c.red / 2 ) / 2, ( 0.5f + c.green / 2 ) / 2, ( 0.5f + c.blue / 2 ) / 2 ).GetRGBA() } } ); diff --git a/src/task/mainmenu/menu/lobby/PlayersSectionRow.cpp b/src/task/mainmenu/menu/lobby/PlayersSectionRow.cpp index 9777b374..c149d6e0 100644 --- a/src/task/mainmenu/menu/lobby/PlayersSectionRow.cpp +++ b/src/task/mainmenu/menu/lobby/PlayersSectionRow.cpp @@ -43,13 +43,17 @@ void PlayersSectionRow::Create() { const auto& faction = player->GetFaction(); + const auto faction_color = faction.m_id == ::game::Player::RANDOM_FACTION.m_id + ? types::Color( 1.0f, 1.0f, 1.0f ) + : faction.m_colors.text; + if ( is_it_ready ) { NEW( m_elements.ready, Surface ); m_elements.ready->SetWidth( 24 ); m_elements.ready->SetHeight( 16 ); m_elements.ready->SetLeft( 8 ); m_elements.ready->SetTop( 4 ); - m_elements.ready->SetTexture( g_engine->GetTextureLoader()->GetColorTexture( faction.m_color ) ); + m_elements.ready->SetTexture( g_engine->GetTextureLoader()->GetColorTexture( faction_color ) ); m_elements.ready->SetStretchTexture( true ); AddChild( m_elements.ready ); } @@ -62,7 +66,7 @@ void PlayersSectionRow::Create() { else { m_elements.faction->SetValue( faction.m_name ); } - m_elements.faction->SetTextColor( faction.m_color ); + m_elements.faction->SetTextColor( faction_color ); m_elements.faction->SetLeft( 218 ); m_elements.faction->SetWidth( 140 ); m_elements.faction->On( @@ -86,7 +90,7 @@ void PlayersSectionRow::Create() { m_elements.difficulty_level->SetChoices( m_parent->GetDifficultyLevelChoices() ); } m_elements.difficulty_level->SetValue( player->GetDifficultyLevel().m_name ); - m_elements.difficulty_level->SetTextColor( faction.m_color ); + m_elements.difficulty_level->SetTextColor( faction_color ); m_elements.difficulty_level->SetLeft( 360 ); m_elements.difficulty_level->SetWidth( 118 ); m_elements.difficulty_level->On( @@ -135,7 +139,7 @@ void PlayersSectionRow::Create() { ); } m_elements.actions->SetValue( player->GetPlayerName() ); - m_elements.actions->SetTextColor( faction.m_color ); + m_elements.actions->SetTextColor( faction_color ); } else { diff --git a/src/types/Color.cpp b/src/types/Color.cpp index 1f1e5d02..073dcfaa 100644 --- a/src/types/Color.cpp +++ b/src/types/Color.cpp @@ -79,10 +79,10 @@ const Color::rgba_t Color::GetRGBA() const { Color Color::FromRGBA( const rgba_t rgba ) { return { - (channel_t)( ( rgba ) & 0xff ), - (channel_t)( ( rgba >> 8 ) & 0xff ), - (channel_t)( ( rgba >> 16 ) & 0xff ), - (channel_t)( ( rgba >> 24 ) & 0xff ) + (channel_t)( ( rgba ) & 0xff ) / 256, + (channel_t)( ( rgba >> 8 ) & 0xff ) / 256, + (channel_t)( ( rgba >> 16 ) & 0xff ) / 256, + (channel_t)( ( rgba >> 24 ) & 0xff ) / 256 }; } From 51e890b7a758f02e5c7bf72c7b119d7c3c1074b6 Mon Sep 17 00:00:00 2001 From: afwbkbc Date: Sun, 18 Feb 2024 17:07:30 +0200 Subject: [PATCH 15/21] fixed factions order in lobby --- src/game/State.cpp | 2 ++ src/game/bindings/Factions.cpp | 4 +++- src/game/rules/Rules.cpp | 11 +++++++---- src/game/rules/Rules.h | 3 ++- src/task/mainmenu/menu/lobby/PlayersSection.cpp | 8 +++++--- src/ui/object/ChoiceList.cpp | 2 +- 6 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/game/State.cpp b/src/game/State.cpp index 1b3a20dd..9507533f 100644 --- a/src/game/State.cpp +++ b/src/game/State.cpp @@ -107,12 +107,14 @@ void State::Configure() { // reset m_settings.global.game_rules.m_factions.clear(); + m_settings.global.game_rules.m_factions_order.clear(); // configure m_bindings->Call( ::game::bindings::Bindings::CS_ON_CONFIGURE ); // check ASSERT( !m_settings.global.game_rules.m_factions.empty(), "no factions were defined" ); + ASSERT( m_settings.global.game_rules.m_factions_order.size() == m_settings.global.game_rules.m_factions.size(), "factions order size mismatch" ); } void State::Reset() { diff --git a/src/game/bindings/Factions.cpp b/src/game/bindings/Factions.cpp index af9066b1..52f1c1e6 100644 --- a/src/game/bindings/Factions.cpp +++ b/src/game/bindings/Factions.cpp @@ -12,10 +12,11 @@ namespace bindings { BINDING_IMPL( factions ) { auto& factions = m_bindings->GetState()->m_settings.global.game_rules.m_factions; + auto& factions_order = m_bindings->GetState()->m_settings.global.game_rules.m_factions_order; const gse::type::Object::properties_t properties = { { "define", - NATIVE_CALL( this, &factions ) { + NATIVE_CALL( this, &factions, &factions_order ) { N_EXPECT_ARGS( 2 ); N_GETVALUE( id, 0, String ); if ( factions.find( id ) != factions.end() ) { @@ -28,6 +29,7 @@ BINDING_IMPL( factions ) { rules::Faction faction = { id, name }; faction.ImportPCX( pcx_file ); factions.insert({ id, faction }); + factions_order.push_back( id ); return VALUE( gse::type::Undefined ); }) }, diff --git a/src/game/rules/Rules.cpp b/src/game/rules/Rules.cpp index cdcdabc3..58ac9de3 100644 --- a/src/game/rules/Rules.cpp +++ b/src/game/rules/Rules.cpp @@ -13,10 +13,11 @@ void Rules::Initialize() { const types::Buffer Rules::Serialize() const { types::Buffer buf; - buf.WriteInt( m_factions.size() ); - for ( auto& faction : m_factions ) { - buf.WriteString( faction.first ); - buf.WriteString( faction.second.Serialize().ToString() ); + buf.WriteInt( m_factions_order.size() ); + for ( auto& id : m_factions_order ) { + const auto& faction = m_factions.at( id ); + buf.WriteString( id ); + buf.WriteString( faction.Serialize().ToString() ); } buf.WriteInt( m_difficulty_levels.size() ); @@ -31,10 +32,12 @@ const types::Buffer Rules::Serialize() const { void Rules::Unserialize( types::Buffer buf ) { m_factions.clear(); + m_factions_order.clear(); const size_t factions_count = buf.ReadInt(); for ( size_t i = 0 ; i < factions_count ; i++ ) { const auto faction_id = buf.ReadString(); m_factions[ faction_id ].Unserialize( buf.ReadString() ); + m_factions_order.push_back( faction_id ); } m_difficulty_levels.clear(); diff --git a/src/game/rules/Rules.h b/src/game/rules/Rules.h index 3a8fa252..43f5ae30 100644 --- a/src/game/rules/Rules.h +++ b/src/game/rules/Rules.h @@ -14,7 +14,8 @@ CLASS( Rules, types::Serializable ) typedef std::unordered_map< std::string, Faction > factions_t; - factions_t m_factions; + factions_t m_factions = {}; + std::vector< std::string > m_factions_order = {}; std::map< size_t, DifficultyLevel > m_difficulty_levels; virtual const DifficultyLevel& GetDefaultDifficultyLevel() const = 0; diff --git a/src/task/mainmenu/menu/lobby/PlayersSection.cpp b/src/task/mainmenu/menu/lobby/PlayersSection.cpp index dfc266e8..28b1c543 100644 --- a/src/task/mainmenu/menu/lobby/PlayersSection.cpp +++ b/src/task/mainmenu/menu/lobby/PlayersSection.cpp @@ -62,11 +62,13 @@ void PlayersSection::UpdateSlots( std::vector< ::game::Slot >& slots ) { random_faction.m_name } ); - for ( auto& faction : game_rules.m_factions ) { + for ( auto& id : game_rules.m_factions_order ) { + ASSERT( game_rules.m_factions.find( id ) != game_rules.m_factions.end(), "faction not found: " + id ); + const auto& faction = game_rules.m_factions.at( id ); m_choices.factions.push_back( { - faction.second.m_id, - faction.second.m_name + id, + faction.m_name } ); } diff --git a/src/ui/object/ChoiceList.cpp b/src/ui/object/ChoiceList.cpp index 211da985..f0efe03e 100644 --- a/src/ui/object/ChoiceList.cpp +++ b/src/ui/object/ChoiceList.cpp @@ -35,7 +35,7 @@ void ChoiceList< KEY_TYPE >::SetChoices( const choices_t& choices ) { m_labels.clear(); for ( auto& choice : choices ) { m_values.push_back( choice.first ); - m_labels[ choice.first ] = choice.second; + m_labels.insert( choice ); } if ( m_created ) { From 0d220519b7cb4738446eb36f51391960e204f65c Mon Sep 17 00:00:00 2001 From: afwbkbc Date: Sun, 18 Feb 2024 19:48:07 +0200 Subject: [PATCH 16/21] added unit morale, is_active and all types of badges --- gse/default/factions.gls.js | 6 +- gse/default/main.gls.js | 16 +-- src/game/Event.cpp | 2 + src/game/Event.h | 3 + src/game/Game.cpp | 8 +- src/game/bindings/Factions.cpp | 9 +- src/game/bindings/Units.cpp | 18 +++- src/game/event/SpawnUnit.cpp | 12 ++- src/game/event/SpawnUnit.h | 5 +- src/game/rules/Faction.cpp | 3 + src/game/rules/Faction.h | 5 + src/game/unit/Unit.cpp | 45 ++++++++- src/game/unit/Unit.h | 9 +- src/gse/callable/Native.h | 12 +++ src/task/game/Game.cpp | 172 ++++++++++++++++++++++----------- src/task/game/Game.h | 57 ++++++++--- 16 files changed, 289 insertions(+), 93 deletions(-) diff --git a/gse/default/factions.gls.js b/gse/default/factions.gls.js index d3019c54..7d429a89 100644 --- a/gse/default/factions.gls.js +++ b/gse/default/factions.gls.js @@ -79,12 +79,14 @@ return [ name: 'Caretakers', files: { pcx: 'caretake.pcx' - } + }, + is_progenitor: true }], ['USURPERS', { name: 'Usurpers', files: { pcx: 'usurper.pcx' - } + }, + is_progenitor: true }], ]; diff --git a/gse/default/main.gls.js b/gse/default/main.gls.js index d2c51247..62a24b48 100644 --- a/gse/default/main.gls.js +++ b/gse/default/main.gls.js @@ -16,10 +16,14 @@ // will be populated on start let players = []; let players_sz = 0; -let get_random_player = () => { +let random_player = () => { return players[(#game.random.get_int(0, players_sz - 1))]; }; +let random_morale = () => { + return #game.random.get_int(1, 7); // TODO: get some constants from api +}; + #game.on.start(() => { // init players @@ -42,18 +46,18 @@ let get_random_player = () => { while (x < w) { if (x % 2 == y % 2) { if (#game.random.get_int(0, 1) == 1) { - let owner = get_random_player(); + let owner = random_player(); let tile = #game.map.get_tile(x, y); let unit = null; if (tile.is_land) { if (#game.random.get_int(0, 2) != 1) { - unit = #game.units.spawn('MindWorms', owner, tile); + unit = #game.units.spawn('MindWorms', owner, tile, random_morale()); } else { - unit = #game.units.spawn('SporeLauncher', owner, tile); + unit = #game.units.spawn('SporeLauncher', owner, tile, random_morale()); } } else { if (#game.random.get_int(0, 1) == 1) { - unit = #game.units.spawn('SeaLurk', owner, tile); + unit = #game.units.spawn('SeaLurk', owner, tile, random_morale()); } } } @@ -91,6 +95,6 @@ let get_random_player = () => { #game.on.despawn_unit((unit) => { if (unit.get_def() == 'SporeLauncher') { - #game.units.spawn('MindWorms', get_random_player(), unit.get_tile()); + #game.units.spawn('MindWorms', random_player(), unit.get_tile(), random_morale()); } }); diff --git a/src/game/Event.cpp b/src/game/Event.cpp index c44f5f50..c3cb8f24 100644 --- a/src/game/Event.cpp +++ b/src/game/Event.cpp @@ -44,6 +44,8 @@ Event::Event( const Event& other ) NEW( data.unit_spawn.unitdef_name, std::string, *other.data.unit_spawn.unitdef_name ); data.unit_spawn.slot_index = other.data.unit_spawn.slot_index; data.unit_spawn.coords = other.data.unit_spawn.coords; + data.unit_spawn.is_active = other.data.unit_spawn.is_active; + data.unit_spawn.morale = other.data.unit_spawn.morale; break; } case ET_UNIT_DESPAWN: { diff --git a/src/game/Event.h b/src/game/Event.h index 058e194e..ddf9cb82 100644 --- a/src/game/Event.h +++ b/src/game/Event.h @@ -36,6 +36,7 @@ class Event { float b; float a; } color; + bool is_progenitor; }; typedef std::vector< slot_define_t > slot_defines_t; @@ -68,6 +69,8 @@ class Event { float y; float z; } coords; + bool is_active; + unit::Unit::morale_t morale; } unit_spawn; struct { size_t unit_id; diff --git a/src/game/Game.cpp b/src/game/Game.cpp index ccebe7ad..0f05d2da 100644 --- a/src/game/Game.cpp +++ b/src/game/Game.cpp @@ -288,7 +288,8 @@ void Game::Iterate() { c.green, c.blue, c.alpha - } + }, + ( faction.m_flags & rules::Faction::FF_PROGENITOR ) != 0 } ); } @@ -970,6 +971,11 @@ void Game::SpawnUnit( unit::Unit* unit ) { -c.y, c.z }; + + // temporary logic for testing - all own units are active, all foreign aren't + e.data.unit_spawn.is_active = unit->m_owner == GetPlayer()->GetSlot(); + + e.data.unit_spawn.morale = unit->m_morale; AddEvent( e ); if ( m_state->IsMaster() ) { diff --git a/src/game/bindings/Factions.cpp b/src/game/bindings/Factions.cpp index 52f1c1e6..9f5f90e6 100644 --- a/src/game/bindings/Factions.cpp +++ b/src/game/bindings/Factions.cpp @@ -24,10 +24,17 @@ BINDING_IMPL( factions ) { } N_GETVALUE( faction_def, 1, Object ); N_GETPROP( name, faction_def, "name", String ); + + rules::Faction faction = { id, name }; + N_GETPROP( files, faction_def, "files", Object ); N_GETPROP( pcx_file, files, "pcx", String ); - rules::Faction faction = { id, name }; faction.ImportPCX( pcx_file ); + + N_GETPROP_OPTBOOL( is_progenitor, faction_def, "is_progenitor") + if ( is_progenitor ) { + faction.m_flags |= rules::Faction::FF_PROGENITOR; + } factions.insert({ id, faction }); factions_order.push_back( id ); return VALUE( gse::type::Undefined ); diff --git a/src/game/bindings/Units.cpp b/src/game/bindings/Units.cpp index 59f7faef..aeedb9a0 100644 --- a/src/game/bindings/Units.cpp +++ b/src/game/bindings/Units.cpp @@ -14,6 +14,13 @@ namespace game { namespace bindings { +const unit::Unit::morale_t GetMorale( const int64_t& morale, gse::Context* ctx, const gse::si_t& call_si ) { + if ( morale < unit::Unit::MORALE_MIN || morale > unit::Unit::MORALE_MAX ) { + ERROR( gse::EC.INVALID_CALL, "Invalid morale value: " + std::to_string( morale ) + " (should be between " + std::to_string( unit::Unit::MORALE_MIN ) + " and " + std::to_string( unit::Unit::MORALE_MAX ) + ", inclusive)" ); + } + return (unit::Unit::morale_t)morale; +} + BINDING_IMPL( units ) { const gse::type::Object::properties_t properties = { { @@ -53,11 +60,18 @@ BINDING_IMPL( units ) { { "spawn", NATIVE_CALL( this ) { - N_EXPECT_ARGS( 3 ); + N_EXPECT_ARGS( 4 ); N_GETVALUE( def_name, 0, String ); N_UNWRAP( owner, 1, Slot ); N_UNWRAP( tile, 2, map::Tile ); - return GAME->AddGameEvent( new event::SpawnUnit( def_name, owner->GetIndex(), tile->coord.x, tile->coord.y ), ctx, call_si ); + N_GETVALUE( morale, 3, Int ); + return GAME->AddGameEvent( new event::SpawnUnit( + def_name, + owner->GetIndex(), + tile->coord.x, + tile->coord.y, + GetMorale( morale, ctx, call_si ) + ), ctx, call_si ); }) }, { diff --git a/src/game/event/SpawnUnit.cpp b/src/game/event/SpawnUnit.cpp index c350f36a..2e817962 100644 --- a/src/game/event/SpawnUnit.cpp +++ b/src/game/event/SpawnUnit.cpp @@ -6,12 +6,13 @@ namespace game { namespace event { -SpawnUnit::SpawnUnit( const std::string& unit_def, const size_t owner_index, const size_t pos_x, const size_t pos_y ) +SpawnUnit::SpawnUnit( const std::string& unit_def, const size_t owner_index, const size_t pos_x, const size_t pos_y, const unit::Unit::morale_t morale ) : Event( ET_UNIT_SPAWN ) , m_unit_def( unit_def ) , m_owner_index( owner_index ) , m_pos_x( pos_x ) - , m_pos_y( pos_y ) { + , m_pos_y( pos_y ) + , m_morale( morale ) { // } @@ -24,7 +25,8 @@ const gse::Value SpawnUnit::Apply( game::Game* game ) const { unit::Unit::GetNextId(), def, &owner, - tile + tile, + m_morale ); game->SpawnUnit( unit @@ -37,6 +39,7 @@ void SpawnUnit::Serialize( types::Buffer& buf, const SpawnUnit* event ) { buf.WriteInt( event->m_owner_index ); buf.WriteInt( event->m_pos_x ); buf.WriteInt( event->m_pos_y ); + buf.WriteInt( event->m_morale ); } SpawnUnit* SpawnUnit::Unserialize( types::Buffer& buf ) { @@ -44,7 +47,8 @@ SpawnUnit* SpawnUnit::Unserialize( types::Buffer& buf ) { const auto owner_index = buf.ReadInt(); const auto pos_x = buf.ReadInt(); const auto pos_y = buf.ReadInt(); - return new SpawnUnit( unit_def, owner_index, pos_x, pos_y ); + const auto morale = (unit::Unit::morale_t)buf.ReadInt(); + return new SpawnUnit( unit_def, owner_index, pos_x, pos_y, morale ); } } diff --git a/src/game/event/SpawnUnit.h b/src/game/event/SpawnUnit.h index b93af1ec..effcc54f 100644 --- a/src/game/event/SpawnUnit.h +++ b/src/game/event/SpawnUnit.h @@ -2,12 +2,14 @@ #include "Event.h" +#include "game/unit/Unit.h" + namespace game { namespace event { class SpawnUnit : public Event { public: - SpawnUnit( const std::string& unit_def, const size_t owner_index, const size_t pos_x, const size_t pos_y ); + SpawnUnit( const std::string& unit_def, const size_t owner_index, const size_t pos_x, const size_t pos_y, const unit::Unit::morale_t morale ); const gse::Value Apply( game::Game* game ) const override; @@ -22,6 +24,7 @@ class SpawnUnit : public Event { const size_t m_owner_index; const size_t m_pos_x; const size_t m_pos_y; + const unit::Unit::morale_t m_morale; }; } diff --git a/src/game/rules/Faction.cpp b/src/game/rules/Faction.cpp index 0e101fbf..0e8745f5 100644 --- a/src/game/rules/Faction.cpp +++ b/src/game/rules/Faction.cpp @@ -7,6 +7,9 @@ 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 130f2a17..166be2ab 100644 --- a/src/game/rules/Faction.h +++ b/src/game/rules/Faction.h @@ -16,8 +16,13 @@ CLASS( Faction, types::Serializable ) Faction(); 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; + std::string m_id = ""; std::string m_name = ""; + faction_flag_t m_flags = FF_NONE; struct { types::Color text = {}; diff --git a/src/game/unit/Unit.cpp b/src/game/unit/Unit.cpp index 8d5a50bb..df772c7b 100644 --- a/src/game/unit/Unit.cpp +++ b/src/game/unit/Unit.cpp @@ -10,6 +10,10 @@ namespace game { namespace unit { +// must be hardcoded because unit badge sprites are +const Unit::morale_t Unit::MORALE_MIN = 1; +const Unit::morale_t Unit::MORALE_MAX = 7; + static size_t next_id = 1; const size_t Unit::GetNextId() { return next_id; @@ -18,16 +22,43 @@ const void Unit::SetNextId( const size_t id ) { next_id = id; } -Unit::Unit( const size_t id, const Def* def, Slot* owner, map::Tile* tile ) +Unit::Unit( const size_t id, const Def* def, Slot* owner, map::Tile* tile, const morale_t morale ) : m_id( id ) , m_def( def ) , m_owner( owner ) - , m_tile( tile ) { + , m_tile( tile ) + , m_morale( morale ) { if ( next_id <= id ) { next_id = id + 1; } } +typedef const std::unordered_map< Unit::morale_t, std::string > morale_strings_t; +const morale_strings_t s_morale_strings_native = { +/* { Unit::M_VERYGREEN, "Hatchling" }, + { Unit::M_GREEN, "Larval Mass" }, + { Unit::M_DISCIPLINED, "Pre-Boil" }, + { Unit::M_HARDENED, "Boil" }, + { Unit::M_VETERAN, "Mature Boil" }, + { Unit::M_COMMANDO, "Great Boil" }, + { Unit::M_ELITE, "Demon Boil" },*/ +}; +const morale_strings_t s_morale_strings_nonnative = { +/* { Unit::M_VERYGREEN, "Very green" }, + { Unit::M_GREEN, "Green" }, + { Unit::M_DISCIPLINED, "Disciplined" }, + { Unit::M_HARDENED, "Hardened" }, + { Unit::M_VETERAN, "Veteran" }, + { Unit::M_COMMANDO, "Commando" }, + { Unit::M_ELITE, "Elite" },*/ +}; +const std::string& Unit::GetMoraleString() const { + const bool is_native = true; // TODO: non-native units + const auto& morale_strings = is_native ? s_morale_strings_native : s_morale_strings_nonnative; + ASSERT_NOLOG( morale_strings.find( m_morale ) != morale_strings.end(), "unknown morale type: " + std::to_string( m_morale ) ); + return morale_strings.at( m_morale ); +} + const types::Buffer Unit::Serialize( const Unit* unit ) { types::Buffer buf; buf.WriteInt( unit->m_id ); @@ -35,6 +66,7 @@ const types::Buffer Unit::Serialize( const Unit* unit ) { buf.WriteInt( unit->m_owner->GetIndex() ); buf.WriteInt( unit->m_tile->coord.x ); buf.WriteInt( unit->m_tile->coord.y ); + buf.WriteInt( unit->m_morale ); return buf; } @@ -46,7 +78,8 @@ Unit* Unit::Unserialize( types::Buffer& buf, const 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 Unit( id, def, slot, tile ); + const auto morale = (morale_t)buf.ReadInt(); + return new Unit( id, def, slot, tile, morale ); } WRAPIMPL_BEGIN( Unit, CLASS_UNIT ) @@ -55,6 +88,10 @@ WRAPIMPL_BEGIN( Unit, CLASS_UNIT ) "id", VALUE( gse::type::Int, m_id ) }, + { + "morale", + VALUE( gse::type::Int, m_morale ) + }, { "get_def", NATIVE_CALL( this ) { @@ -72,7 +109,7 @@ WRAPIMPL_BEGIN( Unit, CLASS_UNIT ) NATIVE_CALL( this ) { return m_tile->Wrap(); }) - } + }, }; WRAPIMPL_END_PTR( Unit ) diff --git a/src/game/unit/Unit.h b/src/game/unit/Unit.h index a1a37ec1..5b783a83 100644 --- a/src/game/unit/Unit.h +++ b/src/game/unit/Unit.h @@ -23,7 +23,11 @@ class Unit { static const size_t GetNextId(); static const void SetNextId( const size_t id ); - Unit( const size_t id, const Def* def, Slot* owner, map::Tile* tile ); + typedef uint8_t morale_t; + static const morale_t MORALE_MIN; + static const morale_t MORALE_MAX; + + Unit( const size_t id, const Def* def, Slot* owner, map::Tile* tile, const morale_t morale ); virtual ~Unit() = default; const size_t m_id; @@ -31,6 +35,9 @@ class Unit { Slot* m_owner; map::Tile* m_tile = nullptr; + morale_t m_morale; + const std::string& GetMoraleString() const; + static const types::Buffer Serialize( const Unit* unit ); static Unit* Unserialize( types::Buffer& buf, const Game* game ); diff --git a/src/gse/callable/Native.h b/src/gse/callable/Native.h index d1a9ba29..6aa24608 100644 --- a/src/gse/callable/Native.h +++ b/src/gse/callable/Native.h @@ -5,6 +5,7 @@ #include "gse/type/Callable.h" #include "gse/type/Object.h" #include "gse/type/Undefined.h" +#include "gse/type/Bool.h" namespace gse { namespace callable { @@ -86,6 +87,17 @@ namespace callable { N_GETPROP_ARG( _obj, _key, Object ); \ N_CHECK_OBJECT_CLASS( arg, gse::type::Object::_class ); \ const auto& _var = ((gse::type::Object*)arg)->value; +#define N_GETPROP_OPTBOOL( _var, _obj, _key ) \ + bool _var = false; \ + obj_it = _obj.find( _key ); \ + if ( obj_it != _obj.end() ) { \ + getprop_val = obj_it->second; \ + arg = getprop_val.Get(); \ + if ( arg->type != gse::type::Type::T_BOOL ) { \ + throw gse::Exception( gse::EC.INVALID_CALL, (std::string)"Property '" + _key + "' is expected to be bool, found: " + arg->GetTypeString( arg->type ), ctx, call_si ); \ + } \ + _var = ((gse::type::Bool*)arg)->value; \ + } class Native : public type::Callable { public: diff --git a/src/task/game/Game.cpp b/src/task/game/Game.cpp index 8b0034f9..38b826dc 100644 --- a/src/task/game/Game.cpp +++ b/src/task/game/Game.cpp @@ -18,11 +18,6 @@ namespace game { const Game::consts_t Game::s_consts = {}; -const float Game::unitbadge_def_t::SCALE_X = 0.25f; -const float Game::unitbadge_def_t::SCALE_Y = 0.5f; -const float Game::unitbadge_def_t::OFFSET_X = -0.25f; -const float Game::unitbadge_def_t::OFFSET_Y = -0.5f; - Game::Game( ::game::State* state, ui_handler_t on_start, ui_handler_t on_cancel ) : m_state( state ) , m_on_start( on_start ) @@ -855,7 +850,11 @@ const ::game::map_editor::MapEditor::brush_type_t Game::GetEditorBrush() const { return m_editor_brush; } -void Game::DefineSlot( const size_t slot_index, const types::Color& color ) { +void Game::DefineSlot( + const size_t slot_index, + const types::Color& color, + const bool is_progenitor +) { auto slot_it = m_slot_states.find( slot_index ); if ( slot_it == m_slot_states.end() ) { Log( "Initializing slot state: " + std::to_string( slot_index ) ); @@ -867,25 +866,45 @@ void Game::DefineSlot( const size_t slot_index, const types::Color& color ) { ).first; } auto& slot = slot_it->second; - if ( slot.color != color || !slot.badge.instanced_sprite ) { - const auto badges_key = "Badges-" + std::to_string( slot_index ); - if ( slot.badge.instanced_sprite ) { - RemoveInstancedSpriteByKey( badges_key ); - } + if ( slot.color != color || slot.badges.empty() ) { + const auto badges_key = "Badge_" + std::to_string( slot_index ); const auto& c = color.value; - slot.badge.instanced_sprite = GetRepaintedInstancedSprite( - badges_key, *m_unitbadge_default.instanced_sprite, { - { // active - types::Color::RGB( 252, 0, 0 ), - types::Color( c.red, c.green, c.blue ).GetRGBA() - }, - { // greyed out - types::Color::RGB( 125, 0, 0 ), - types::Color( ( 0.5f + c.red / 2 ) / 2, ( 0.5f + c.green / 2 ) / 2, ( 0.5f + c.blue / 2 ) / 2 ).GetRGBA() - } + const types::Texture::repaint_rules_t& rules = { + { // active + types::Color::RGB( 252, 0, 0 ), + types::Color( c.red, c.green, c.blue ).GetRGBA() + }, + { // greyed out + types::Color::RGB( 125, 0, 0 ), + types::Color( ( 0.5f + c.red / 2 ) / 2, ( 0.5f + c.green / 2 ) / 2, ( 0.5f + c.blue / 2 ) / 2 ).GetRGBA() } - ); - slot.badge.next_instance_id = 1; + }; + if ( !slot.badges.empty() ) { + for ( const auto& it : slot.badges ) { + const auto& badgedef = it.second; + RemoveInstancedSpriteByKey( badgedef.normal.instanced_sprite->key ); + RemoveInstancedSpriteByKey( badgedef.greyedout.instanced_sprite->key ); + } + slot.badges.clear(); + } + const badge_type_t badge_type = is_progenitor + ? BT_PROGENITOR + : BT_DEFAULT; + for ( auto morale = ::game::unit::Unit::MORALE_MIN ; morale <= ::game::unit::Unit::MORALE_MAX ; morale++ ) { + auto& badgedef = slot.badges[ morale ]; + badgedef.normal.instanced_sprite = GetRepaintedInstancedSprite( + badges_key + "_" + std::to_string( morale ) + "_NORMAL", + *m_unitbadge_sprites.at( badge_type | BT_NORMAL ).at( morale ), + rules + ); + badgedef.normal.next_instance_id = 1; + badgedef.greyedout.instanced_sprite = GetRepaintedInstancedSprite( + badges_key + "_" + std::to_string( morale ) + "_GREYEDOUT", + *m_unitbadge_sprites.at( badge_type | BT_GREYEDOUT ).at( morale ), + rules + ); + badgedef.greyedout.next_instance_id = 1; + } } slot.color = color; } @@ -946,7 +965,16 @@ void Game::DefineUnit( const ::game::unit::Def* unitdef ) { ); } -void Game::SpawnUnit( const size_t unit_id, const std::string& unitdef_name, const size_t slot_index, const float x, const float y, const float z ) { +void Game::SpawnUnit( + const size_t unit_id, + const std::string& unitdef_name, + const size_t slot_index, + const float x, + const float y, + const float z, + const bool is_active, + const ::game::unit::Unit::morale_t morale +) { ASSERT( m_unitdef_states.find( unitdef_name ) != m_unitdef_states.end(), "unitdef not found" ); ASSERT( m_slot_states.find( slot_index ) != m_slot_states.end(), "slot not found" ); @@ -962,10 +990,13 @@ void Game::SpawnUnit( const size_t unit_id, const std::string& unitdef_name, con ASSERT( unitdef_state.m_type == ::game::unit::Def::DT_STATIC, "only static unitdefs are supported for now" ); ASSERT( unitdef_state.static_.render.is_sprite, "only sprite unitdefs are supported for now" ); + unit_state.is_active = is_active; + unit_state.morale = morale; + // add render - unit_state.instance_id = unitdef_state.static_.render.sprite.next_instance_id++; + unit_state.render.instance_id = unitdef_state.static_.render.sprite.next_instance_id++; unitdef_state.static_.render.sprite.instanced_sprite->actor->SetInstance( - unit_state.instance_id, { + unit_state.render.instance_id, { x, y, z @@ -973,13 +1004,15 @@ void Game::SpawnUnit( const size_t unit_id, const std::string& unitdef_name, con ); // add badge render - unit_state.badge_def = &slot_state.badge; // TODO: variations - - unit_state.badge_instance_id = unit_state.badge_def->next_instance_id++; - unit_state.badge_def->instanced_sprite->actor->SetInstance( - unit_state.badge_instance_id, { - x + ::game::map::s_consts.tile.scale.x * unitbadge_def_t::OFFSET_X, - y - ::game::map::s_consts.tile.scale.y * ::game::map::s_consts.sprite.y_scale * unitbadge_def_t::OFFSET_Y, + unit_state.render.badge_def = unit_state.is_active + ? &slot_state.badges.at( unit_state.morale ).normal + : &slot_state.badges.at( unit_state.morale ).greyedout; + + unit_state.render.badge_instance_id = unit_state.render.badge_def->next_instance_id++; + unit_state.render.badge_def->instanced_sprite->actor->SetInstance( + unit_state.render.badge_instance_id, { + x + ::game::map::s_consts.tile.scale.x * s_consts.badges.offset_x, + y - ::game::map::s_consts.tile.scale.y * ::game::map::s_consts.sprite.y_scale * s_consts.badges.offset_y, z } ); @@ -1004,8 +1037,9 @@ void Game::DespawnUnit( const size_t unit_id ) { ASSERT( unitdef_state->m_type == ::game::unit::Def::DT_STATIC, "only static unitdefs are supported for now" ); ASSERT( unitdef_state->static_.render.is_sprite, "only sprite unitdefs are supported for now" ); - unitdef_state->static_.render.sprite.instanced_sprite->actor->RemoveInstance( unit_state.instance_id ); - unit_state.badge_def->instanced_sprite->actor->RemoveInstance( unit_state.badge_instance_id ); + unitdef_state->static_.render.sprite.instanced_sprite->actor->RemoveInstance( unit_state.render.instance_id ); + unit_state.render.badge_def->instanced_sprite->actor->RemoveInstance( unit_state.render.badge_instance_id ); + m_unit_states.erase( it ); } @@ -1055,7 +1089,7 @@ void Game::ProcessEvent( const ::game::Event& event ) { case ::game::Event::ET_SLOT_DEFINE: { for ( const auto& d : *event.data.slot_define.slotdefs ) { const auto& c = d.color; - DefineSlot( d.slot_index, types::Color( c.r, c.g, c.b, c.a ) ); + DefineSlot( d.slot_index, types::Color( c.r, c.g, c.b, c.a ), d.is_progenitor ); } break; } @@ -1069,7 +1103,7 @@ void Game::ProcessEvent( const ::game::Event& event ) { case ::game::Event::ET_UNIT_SPAWN: { const auto& d = event.data.unit_spawn; const auto& c = d.coords; - SpawnUnit( d.unit_id, *d.unitdef_name, d.slot_index, c.x, c.y, c.z ); + SpawnUnit( d.unit_id, *d.unitdef_name, d.slot_index, c.x, c.y, c.z, d.is_active, d.morale ); break; } case ::game::Event::ET_UNIT_DESPAWN: { @@ -1199,27 +1233,49 @@ void Game::Initialize( actor->SetInstance( instance.first, instance.second.second ); } - // Predefined sprites - // TODO: variations - m_unitbadge_default.instanced_sprite = &GetInstancedSprite( - "Badges", "flags.pcx", { - 49, - 63, - }, - { - 23, - 30, - }, - { - 61, - 78, - }, - { - ::game::map::s_consts.tile.scale.x * unitbadge_def_t::SCALE_X, - ::game::map::s_consts.tile.scale.y * ::game::map::s_consts.sprite.y_scale * unitbadge_def_t::SCALE_Y - }, - 0.52f - ); + { + // load badge sprites + ASSERT( m_unitbadge_sprites.empty(), "badge sprites already initialized" ); + const uint8_t w = 23; + const uint8_t h = 30; + const uint8_t margin = 1; + const std::string& pcx_file = "flags.pcx"; + uint32_t x, y; + for ( auto badge_type = BT_PROGENITOR ; badge_type <= BT_DEFAULT ; badge_type++ ) { + for ( auto badge_mode = BT_NORMAL ; badge_mode <= BT_GREYEDOUT ; badge_mode++ ) { + const uint8_t row = badge_type | badge_mode; + auto& spritemap = m_unitbadge_sprites[ row ]; + for ( auto morale = ::game::unit::Unit::MORALE_MIN ; morale <= ::game::unit::Unit::MORALE_MAX ; morale++ ) { + x = morale * ( w + margin ) + margin; + y = row * ( h + margin ) + margin; + spritemap.insert( + { + morale, + &GetInstancedSprite( + "Badge_" + std::to_string( badge_type ) + "_" + std::to_string( badge_mode ), pcx_file, { + x, + y, + }, + { + w, + h, + }, + { + x + w / 2, + y + h / 2, + }, + { + ::game::map::s_consts.tile.scale.x * s_consts.badges.scale_x, + ::game::map::s_consts.tile.scale.y * ::game::map::s_consts.sprite.y_scale * s_consts.badges.scale_y + }, + 0.52f + ) + } + ); + } + } + } + } // UI ui->AddTheme( &m_ui.theme ); diff --git a/src/task/game/Game.h b/src/task/game/Game.h index bc2d4ad0..5bdd39ce 100644 --- a/src/task/game/Game.h +++ b/src/task/game/Game.h @@ -81,6 +81,12 @@ CLASS( Game, base::Task ) const struct { const size_t draw_frequency_ms = 60; // TODO: this value doesn't seem realistic, why? } map_editing; + const struct { + const float scale_x = 0.25f; + const float scale_y = 0.5f; + const float offset_x = -0.25f; + const float offset_y = -0.5; + } badges; }; static const consts_t s_consts; @@ -158,9 +164,22 @@ CLASS( Game, base::Task ) void UpdateMapData( const types::Vec2< size_t >& map_size ); - void DefineSlot( const size_t slot_index, const types::Color& color ); + void DefineSlot( + const size_t slot_index, + const types::Color& color, + const bool is_progenitor + ); void DefineUnit( const ::game::unit::Def* unitdef ); - void SpawnUnit( const size_t unit_id, const std::string& unitdef_name, const size_t slot_index, const float x, const float y, const float z ); + void SpawnUnit( + const size_t unit_id, + const std::string& unitdef_name, + const size_t slot_index, + const float x, + const float y, + const float z, + const bool is_active, + const ::game::unit::Unit::morale_t morale + ); void DespawnUnit( const size_t unit_id ); void ProcessEvent( const ::game::Event& event ); @@ -377,28 +396,40 @@ CLASS( Game, base::Task ) }; std::unordered_map< std::string, unitdef_state_t > m_unitdef_states = {}; - struct unitbadge_def_t { - static const float SCALE_X; - static const float SCALE_Y; - static const float OFFSET_X; - static const float OFFSET_Y; + typedef std::unordered_map< ::game::unit::Unit::morale_t, Game::instanced_sprite_t* > unitbadge_spritemap_t; + typedef uint8_t badge_type_t; + const badge_type_t BT_NORMAL = 0 << 0; + const badge_type_t BT_GREYEDOUT = 1 << 0; + const badge_type_t BT_DEFAULT = 1 << 1; + const badge_type_t BT_PROGENITOR = 0 << 1; + typedef std::unordered_map< badge_type_t, unitbadge_spritemap_t > unitbadge_spritemaps_t; + struct unitbadge_subdef_t { Game::instanced_sprite_t* instanced_sprite = nullptr; size_t next_instance_id = 1; }; - unitbadge_def_t m_unitbadge_default = {}; // TODO: variations + struct unitbadge_def_t { + unitbadge_subdef_t normal; + unitbadge_subdef_t greyedout; + }; + typedef std::unordered_map< ::game::unit::Unit::morale_t, unitbadge_def_t > unitbadge_defs_t; + unitbadge_spritemaps_t m_unitbadge_sprites = {}; struct slot_state_t { - types::Color color; - unitbadge_def_t badge; + types::Color color = {}; + unitbadge_defs_t badges = {}; }; std::unordered_map< size_t, slot_state_t > m_slot_states = {}; struct unit_state_t { unitdef_state_t* def = nullptr; slot_state_t* slot = nullptr; - size_t instance_id = 0; - unitbadge_def_t* badge_def = nullptr; - size_t badge_instance_id = 0; + struct { + size_t instance_id = 0; + unitbadge_subdef_t* badge_def = nullptr; + size_t badge_instance_id = 0; + } render; + bool is_active = false; + ::game::unit::Unit::morale_t morale = 0; }; std::unordered_map< size_t, unit_state_t > m_unit_states = {}; From 70a7e33c01264a8f8ee49d90910b295da5fd5a6b Mon Sep 17 00:00:00 2001 From: afwbkbc Date: Sun, 18 Feb 2024 21:16:06 +0200 Subject: [PATCH 17/21] added health property to units --- gse/default/main.gls.js | 12 ++++++++---- src/game/Event.cpp | 1 + src/game/Event.h | 1 + src/game/Game.cpp | 2 ++ src/game/State.cpp | 12 +++++++++++- src/game/bindings/Random.cpp | 15 ++++++++++++++- src/game/bindings/Units.cpp | 17 +++++++++++++++-- src/game/event/SpawnUnit.cpp | 19 +++++++++++++++---- src/game/event/SpawnUnit.h | 10 +++++++++- src/game/unit/Unit.cpp | 17 ++++++++++++++--- src/game/unit/Unit.h | 14 +++++++++++++- src/task/game/Game.cpp | 6 ++++-- src/task/game/Game.h | 4 +++- 13 files changed, 110 insertions(+), 20 deletions(-) diff --git a/gse/default/main.gls.js b/gse/default/main.gls.js index 62a24b48..e350bcd7 100644 --- a/gse/default/main.gls.js +++ b/gse/default/main.gls.js @@ -24,6 +24,10 @@ let random_morale = () => { return #game.random.get_int(1, 7); // TODO: get some constants from api }; +let random_health = () => { + return #game.random.get_float(#to_float(0), #to_float(1)); +}; + #game.on.start(() => { // init players @@ -51,13 +55,13 @@ let random_morale = () => { let unit = null; if (tile.is_land) { if (#game.random.get_int(0, 2) != 1) { - unit = #game.units.spawn('MindWorms', owner, tile, random_morale()); + unit = #game.units.spawn('MindWorms', owner, tile, random_morale(), random_health()); } else { - unit = #game.units.spawn('SporeLauncher', owner, tile, random_morale()); + unit = #game.units.spawn('SporeLauncher', owner, tile, random_morale(), random_health()); } } else { if (#game.random.get_int(0, 1) == 1) { - unit = #game.units.spawn('SeaLurk', owner, tile, random_morale()); + unit = #game.units.spawn('SeaLurk', owner, tile, random_morale(), random_health()); } } } @@ -95,6 +99,6 @@ let random_morale = () => { #game.on.despawn_unit((unit) => { if (unit.get_def() == 'SporeLauncher') { - #game.units.spawn('MindWorms', random_player(), unit.get_tile(), random_morale()); + #game.units.spawn('MindWorms', random_player(), unit.get_tile(), random_morale(), random_health()); } }); diff --git a/src/game/Event.cpp b/src/game/Event.cpp index c3cb8f24..bb5b55c3 100644 --- a/src/game/Event.cpp +++ b/src/game/Event.cpp @@ -46,6 +46,7 @@ Event::Event( const Event& other ) data.unit_spawn.coords = other.data.unit_spawn.coords; data.unit_spawn.is_active = other.data.unit_spawn.is_active; data.unit_spawn.morale = other.data.unit_spawn.morale; + data.unit_spawn.health = other.data.unit_spawn.health; break; } case ET_UNIT_DESPAWN: { diff --git a/src/game/Event.h b/src/game/Event.h index ddf9cb82..c3c787b4 100644 --- a/src/game/Event.h +++ b/src/game/Event.h @@ -71,6 +71,7 @@ class Event { } coords; bool is_active; unit::Unit::morale_t morale; + unit::Unit::health_t health; } unit_spawn; struct { size_t unit_id; diff --git a/src/game/Game.cpp b/src/game/Game.cpp index 0f05d2da..eb59c094 100644 --- a/src/game/Game.cpp +++ b/src/game/Game.cpp @@ -976,6 +976,8 @@ void Game::SpawnUnit( unit::Unit* unit ) { e.data.unit_spawn.is_active = unit->m_owner == GetPlayer()->GetSlot(); e.data.unit_spawn.morale = unit->m_morale; + e.data.unit_spawn.health = unit->m_health; + AddEvent( e ); if ( m_state->IsMaster() ) { diff --git a/src/game/State.cpp b/src/game/State.cpp index 9507533f..fc6e3334 100644 --- a/src/game/State.cpp +++ b/src/game/State.cpp @@ -96,7 +96,17 @@ void State::InitBindings() { if ( !m_bindings ) { Log( "Initializing bindings" ); m_bindings = new bindings::Bindings( this ); - m_bindings->RunMain(); + try { + m_bindings->RunMain(); + } + catch ( gse::Exception& err ) { + if ( m_game ) { + m_game->OnGSEError( err ); + } + else { + throw std::runtime_error( err.ToStringAndCleanup() ); + } + } } } diff --git a/src/game/bindings/Random.cpp b/src/game/bindings/Random.cpp index 16d85ce3..078450e0 100644 --- a/src/game/bindings/Random.cpp +++ b/src/game/bindings/Random.cpp @@ -2,6 +2,7 @@ #include "gse/type/Object.h" #include "gse/type/Int.h" +#include "gse/type/Float.h" namespace game { namespace bindings { @@ -19,7 +20,19 @@ BINDING_IMPL( random ) { } return VALUE( gse::type::Int, GAME->GetRandom()->GetInt64( min, max ) ); }) - } + }, + { + "get_float", + NATIVE_CALL( this ) { + N_EXPECT_ARGS( 2 ); + N_GETVALUE( min, 0, Float ); + N_GETVALUE( max, 1, Float ); + if ( max < min ) { + ERROR( gse::EC.INVALID_CALL, "Maximum value is smaller than minimum ( " + std::to_string( max ) + " < " + std::to_string( min ) + " )" ); + } + return VALUE( gse::type::Float, GAME->GetRandom()->GetFloat( min, max ) ); + }) + }, }; return VALUE( gse::type::Object, properties ); } diff --git a/src/game/bindings/Units.cpp b/src/game/bindings/Units.cpp index aeedb9a0..e5aee8b0 100644 --- a/src/game/bindings/Units.cpp +++ b/src/game/bindings/Units.cpp @@ -3,6 +3,7 @@ #include "gse/type/Object.h" #include "gse/type/String.h" #include "gse/type/Int.h" +#include "gse/type/Float.h" #include "game/unit/StaticDef.h" #include "game/unit/SpriteRender.h" @@ -21,6 +22,16 @@ const unit::Unit::morale_t GetMorale( const int64_t& morale, gse::Context* ctx, return (unit::Unit::morale_t)morale; } +const unit::Unit::health_t GetHealth( const float health, gse::Context* ctx, const gse::si_t& call_si ) { + if ( health < unit::Unit::HEALTH_MIN || health > unit::Unit::HEALTH_MAX ) { + ERROR( gse::EC.INVALID_CALL, "Invalid health value: " + std::to_string( health ) + " (should be between " + std::to_string( unit::Unit::HEALTH_MIN ) + " and " + std::to_string( unit::Unit::HEALTH_MAX ) + ", inclusive)" ); + } + if ( health == 0 ) { + ERROR( gse::EC.INVALID_CALL, "Invalid health value: " + std::to_string( health ) + " (you can't spawn a dead unit)" ); + } + return (unit::Unit::health_t)health; +} + BINDING_IMPL( units ) { const gse::type::Object::properties_t properties = { { @@ -60,17 +71,19 @@ BINDING_IMPL( units ) { { "spawn", NATIVE_CALL( this ) { - N_EXPECT_ARGS( 4 ); + N_EXPECT_ARGS( 5 ); N_GETVALUE( def_name, 0, String ); N_UNWRAP( owner, 1, Slot ); N_UNWRAP( tile, 2, map::Tile ); N_GETVALUE( morale, 3, Int ); + N_GETVALUE( health, 4, Float ); return GAME->AddGameEvent( new event::SpawnUnit( def_name, owner->GetIndex(), tile->coord.x, tile->coord.y, - GetMorale( morale, ctx, call_si ) + GetMorale( morale, ctx, call_si ), + GetHealth( health, ctx, call_si ) ), ctx, call_si ); }) }, diff --git a/src/game/event/SpawnUnit.cpp b/src/game/event/SpawnUnit.cpp index 2e817962..9db2babd 100644 --- a/src/game/event/SpawnUnit.cpp +++ b/src/game/event/SpawnUnit.cpp @@ -6,13 +6,21 @@ namespace game { namespace event { -SpawnUnit::SpawnUnit( const std::string& unit_def, const size_t owner_index, const size_t pos_x, const size_t pos_y, const unit::Unit::morale_t morale ) +SpawnUnit::SpawnUnit( + const std::string& unit_def, + const size_t owner_index, + const size_t pos_x, + const size_t pos_y, + const unit::Unit::morale_t morale, + const unit::Unit::health_t health +) : Event( ET_UNIT_SPAWN ) , m_unit_def( unit_def ) , m_owner_index( owner_index ) , m_pos_x( pos_x ) , m_pos_y( pos_y ) - , m_morale( morale ) { + , m_morale( morale ) + , m_health( health ) { // } @@ -26,7 +34,8 @@ const gse::Value SpawnUnit::Apply( game::Game* game ) const { def, &owner, tile, - m_morale + m_morale, + m_health ); game->SpawnUnit( unit @@ -40,6 +49,7 @@ void SpawnUnit::Serialize( types::Buffer& buf, const SpawnUnit* event ) { buf.WriteInt( event->m_pos_x ); buf.WriteInt( event->m_pos_y ); buf.WriteInt( event->m_morale ); + buf.WriteFloat( event->m_health ); } SpawnUnit* SpawnUnit::Unserialize( types::Buffer& buf ) { @@ -48,7 +58,8 @@ SpawnUnit* SpawnUnit::Unserialize( types::Buffer& buf ) { const auto pos_x = buf.ReadInt(); const auto pos_y = buf.ReadInt(); const auto morale = (unit::Unit::morale_t)buf.ReadInt(); - return new SpawnUnit( unit_def, owner_index, pos_x, pos_y, morale ); + const auto health = (unit::Unit::health_t)buf.ReadFloat(); + return new SpawnUnit( unit_def, owner_index, pos_x, pos_y, morale, health ); } } diff --git a/src/game/event/SpawnUnit.h b/src/game/event/SpawnUnit.h index effcc54f..9b7012cc 100644 --- a/src/game/event/SpawnUnit.h +++ b/src/game/event/SpawnUnit.h @@ -9,7 +9,14 @@ namespace event { class SpawnUnit : public Event { public: - SpawnUnit( const std::string& unit_def, const size_t owner_index, const size_t pos_x, const size_t pos_y, const unit::Unit::morale_t morale ); + SpawnUnit( + const std::string& unit_def, + const size_t owner_index, + const size_t pos_x, + const size_t pos_y, + const unit::Unit::morale_t morale, + const unit::Unit::health_t health + ); const gse::Value Apply( game::Game* game ) const override; @@ -25,6 +32,7 @@ class SpawnUnit : public Event { const size_t m_pos_x; const size_t m_pos_y; const unit::Unit::morale_t m_morale; + const unit::Unit::health_t m_health; }; } diff --git a/src/game/unit/Unit.cpp b/src/game/unit/Unit.cpp index df772c7b..912beb88 100644 --- a/src/game/unit/Unit.cpp +++ b/src/game/unit/Unit.cpp @@ -2,6 +2,7 @@ #include "gse/type/Object.h" #include "gse/type/Int.h" +#include "gse/type/Float.h" #include "gse/callable/Native.h" #include "game/Game.h" @@ -14,6 +15,9 @@ namespace unit { const Unit::morale_t Unit::MORALE_MIN = 1; const Unit::morale_t Unit::MORALE_MAX = 7; +const Unit::health_t Unit::HEALTH_MIN = 0.0f; +const Unit::health_t Unit::HEALTH_MAX = 1.0f; + static size_t next_id = 1; const size_t Unit::GetNextId() { return next_id; @@ -22,12 +26,13 @@ const void Unit::SetNextId( const size_t id ) { next_id = id; } -Unit::Unit( const size_t id, const Def* def, Slot* owner, map::Tile* tile, const morale_t morale ) +Unit::Unit( const size_t id, const Def* def, Slot* owner, map::Tile* tile, const morale_t morale, const health_t health ) : m_id( id ) , m_def( def ) , m_owner( owner ) , m_tile( tile ) - , m_morale( morale ) { + , m_morale( morale ) + , m_health( health ) { if ( next_id <= id ) { next_id = id + 1; } @@ -67,6 +72,7 @@ const types::Buffer Unit::Serialize( const Unit* unit ) { buf.WriteInt( unit->m_tile->coord.x ); buf.WriteInt( unit->m_tile->coord.y ); buf.WriteInt( unit->m_morale ); + buf.WriteFloat( unit->m_health ); return buf; } @@ -79,7 +85,8 @@ Unit* Unit::Unserialize( types::Buffer& buf, const Game* game ) { const auto pos_y = buf.ReadInt(); auto* tile = game ? game->GetMap()->GetTile( pos_x, pos_y ) : nullptr; const auto morale = (morale_t)buf.ReadInt(); - return new Unit( id, def, slot, tile, morale ); + const auto health = (health_t)buf.ReadFloat(); + return new Unit( id, def, slot, tile, morale, health ); } WRAPIMPL_BEGIN( Unit, CLASS_UNIT ) @@ -92,6 +99,10 @@ WRAPIMPL_BEGIN( Unit, CLASS_UNIT ) "morale", VALUE( gse::type::Int, m_morale ) }, + { + "health", + VALUE( gse::type::Float, m_health ) + }, { "get_def", NATIVE_CALL( this ) { diff --git a/src/game/unit/Unit.h b/src/game/unit/Unit.h index 5b783a83..e41d5425 100644 --- a/src/game/unit/Unit.h +++ b/src/game/unit/Unit.h @@ -27,7 +27,18 @@ class Unit { static const morale_t MORALE_MIN; static const morale_t MORALE_MAX; - Unit( const size_t id, const Def* def, Slot* owner, map::Tile* tile, const morale_t morale ); + typedef float health_t; + static const health_t HEALTH_MIN; + static const health_t HEALTH_MAX; + + Unit( + const size_t id, + const Def* def, + Slot* owner, + map::Tile* tile, + const morale_t morale, + const health_t health + ); virtual ~Unit() = default; const size_t m_id; @@ -36,6 +47,7 @@ class Unit { map::Tile* m_tile = nullptr; morale_t m_morale; + health_t m_health; const std::string& GetMoraleString() const; static const types::Buffer Serialize( const Unit* unit ); diff --git a/src/task/game/Game.cpp b/src/task/game/Game.cpp index 38b826dc..6329d987 100644 --- a/src/task/game/Game.cpp +++ b/src/task/game/Game.cpp @@ -973,7 +973,8 @@ void Game::SpawnUnit( const float y, const float z, const bool is_active, - const ::game::unit::Unit::morale_t morale + const ::game::unit::Unit::morale_t morale, + const ::game::unit::Unit::health_t health ) { ASSERT( m_unitdef_states.find( unitdef_name ) != m_unitdef_states.end(), "unitdef not found" ); @@ -992,6 +993,7 @@ void Game::SpawnUnit( unit_state.is_active = is_active; unit_state.morale = morale; + unit_state.health = health; // add render unit_state.render.instance_id = unitdef_state.static_.render.sprite.next_instance_id++; @@ -1103,7 +1105,7 @@ void Game::ProcessEvent( const ::game::Event& event ) { case ::game::Event::ET_UNIT_SPAWN: { const auto& d = event.data.unit_spawn; const auto& c = d.coords; - SpawnUnit( d.unit_id, *d.unitdef_name, d.slot_index, c.x, c.y, c.z, d.is_active, d.morale ); + SpawnUnit( d.unit_id, *d.unitdef_name, d.slot_index, c.x, c.y, c.z, d.is_active, d.morale, d.health ); break; } case ::game::Event::ET_UNIT_DESPAWN: { diff --git a/src/task/game/Game.h b/src/task/game/Game.h index 5bdd39ce..fd2660c8 100644 --- a/src/task/game/Game.h +++ b/src/task/game/Game.h @@ -178,7 +178,8 @@ CLASS( Game, base::Task ) const float y, const float z, const bool is_active, - const ::game::unit::Unit::morale_t morale + const ::game::unit::Unit::morale_t morale, + const ::game::unit::Unit::health_t health ); void DespawnUnit( const size_t unit_id ); @@ -430,6 +431,7 @@ CLASS( Game, base::Task ) } render; bool is_active = false; ::game::unit::Unit::morale_t morale = 0; + ::game::unit::Unit::health_t health = 0; }; std::unordered_map< size_t, unit_state_t > m_unit_states = {}; From 4ccbf9752272c8dae0083b2a785c3aa2f2cd670c Mon Sep 17 00:00:00 2001 From: afwbkbc Date: Sun, 18 Feb 2024 22:44:29 +0200 Subject: [PATCH 18/21] added healthbars to unit badges --- src/game/map/Map.cpp | 2 +- src/task/game/Game.cpp | 96 ++++++++++++++++++++++++++++++++++++------ src/task/game/Game.h | 18 +++++++- src/types/Texture.cpp | 18 +++++++- src/types/Texture.h | 2 + 5 files changed, 119 insertions(+), 17 deletions(-) diff --git a/src/game/map/Map.cpp b/src/game/map/Map.cpp index addd3886..7ffc3aa4 100644 --- a/src/game/map/Map.cpp +++ b/src/game/map/Map.cpp @@ -539,7 +539,7 @@ Tiles* Map::GetTilesPtr() const { } const std::string Map::GetTerrainSpriteActor( const std::string& name, const Consts::pcx_texture_coordinates_t& tex_coords, const float z_index ) { - const auto key = "Terrain_" + name + " ter1.pcx " + tex_coords.ToString() + " " + ::game::map::s_consts.tc.ter1_pcx.dimensions.ToString(); + const auto key = "Terrain_" + name + " " + tex_coords.ToString() + " " + ::game::map::s_consts.tc.ter1_pcx.dimensions.ToString(); auto it = m_sprite_actors.find( key ); if ( it == m_sprite_actors.end() ) { diff --git a/src/task/game/Game.cpp b/src/task/game/Game.cpp index 6329d987..5519e49a 100644 --- a/src/task/game/Game.cpp +++ b/src/task/game/Game.cpp @@ -427,7 +427,7 @@ types::Texture* Game::GetRepaintedSourceTexture( const std::string& name, const Game::instanced_sprite_t& Game::GetInstancedSprite( const std::string& name, - const std::string& tex_file, + types::Texture* texture, const ::game::map::Consts::pcx_texture_coordinates_t& src_xy, const ::game::map::Consts::pcx_texture_coordinates_t& src_wh, const ::game::map::Consts::pcx_texture_coordinates_t& src_cxy, @@ -435,15 +435,13 @@ Game::instanced_sprite_t& Game::GetInstancedSprite( const float z_index ) { - const auto key = name + " " + tex_file + " " + src_xy.ToString() + " " + src_wh.ToString(); + const auto key = name + " " + src_xy.ToString() + " " + src_wh.ToString(); auto it = m_instanced_sprites.find( key ); if ( it == m_instanced_sprites.end() ) { Log( "Creating instanced sprite: " + key ); - auto* texture = GetSourceTexture( tex_file ); - NEWV( sprite, scene::actor::Sprite, @@ -493,7 +491,7 @@ Game::instanced_sprite_t& Game::GetInstancedSpriteByKey( const std::string& key Game::instanced_sprite_t& Game::GetTerrainInstancedSprite( const ::game::map::Map::sprite_actor_t& actor ) { return GetInstancedSprite( "Terrain_" + actor.name, - "ter1.pcx", + GetSourceTexture( "ter1.pcx" ), actor.tex_coords, ::game::map::s_consts.tc.ter1_pcx.dimensions, { @@ -928,7 +926,7 @@ void Game::DefineUnit( const ::game::unit::Def* unitdef ) { unitdef_state.static_.render.is_sprite = true; unitdef_state.static_.render.sprite.instanced_sprite = &GetInstancedSprite( - "Unit_" + def->m_name, render->m_file, { + "Unit_" + def->m_name, GetSourceTexture( render->m_file ), { render->m_x, render->m_y, }, @@ -1009,12 +1007,22 @@ void Game::SpawnUnit( unit_state.render.badge_def = unit_state.is_active ? &slot_state.badges.at( unit_state.morale ).normal : &slot_state.badges.at( unit_state.morale ).greyedout; - unit_state.render.badge_instance_id = unit_state.render.badge_def->next_instance_id++; unit_state.render.badge_def->instanced_sprite->actor->SetInstance( unit_state.render.badge_instance_id, { x + ::game::map::s_consts.tile.scale.x * s_consts.badges.offset_x, - y - ::game::map::s_consts.tile.scale.y * ::game::map::s_consts.sprite.y_scale * s_consts.badges.offset_y, + y - ::game::map::s_consts.tile.scale.y * s_consts.badges.offset_y * ::game::map::s_consts.sprite.y_scale, + z + } + ); + + // add healthbar render + unit_state.render.badge_healthbar_def = &m_healthbar_sprites.at( floor( unit_state.health * s_consts.badges.healthbars.resolution ) ); + unit_state.render.badge_healthbar_instance_id = unit_state.render.badge_healthbar_def->next_instance_id++; + unit_state.render.badge_healthbar_def->instanced_sprite->actor->SetInstance( + unit_state.render.badge_healthbar_instance_id, { + x + ::game::map::s_consts.tile.scale.x * s_consts.badges.healthbars.offset_x, + y - ::game::map::s_consts.tile.scale.y * s_consts.badges.healthbars.offset_y * ::game::map::s_consts.sprite.y_scale, z } ); @@ -1041,6 +1049,7 @@ void Game::DespawnUnit( const size_t unit_id ) { unitdef_state->static_.render.sprite.instanced_sprite->actor->RemoveInstance( unit_state.render.instance_id ); unit_state.render.badge_def->instanced_sprite->actor->RemoveInstance( unit_state.render.badge_instance_id ); + unit_state.render.badge_healthbar_def->instanced_sprite->actor->RemoveInstance( unit_state.render.badge_healthbar_instance_id ); m_unit_states.erase( it ); } @@ -1241,20 +1250,27 @@ void Game::Initialize( const uint8_t w = 23; const uint8_t h = 30; const uint8_t margin = 1; - const std::string& pcx_file = "flags.pcx"; + auto* texture = GetSourceTexture( "flags.pcx" ); + const types::Color transparent( 0.0f, 0.0f, 0.0f, 0.0f ); uint32_t x, y; for ( auto badge_type = BT_PROGENITOR ; badge_type <= BT_DEFAULT ; badge_type++ ) { for ( auto badge_mode = BT_NORMAL ; badge_mode <= BT_GREYEDOUT ; badge_mode++ ) { const uint8_t row = badge_type | badge_mode; auto& spritemap = m_unitbadge_sprites[ row ]; for ( auto morale = ::game::unit::Unit::MORALE_MIN ; morale <= ::game::unit::Unit::MORALE_MAX ; morale++ ) { - x = morale * ( w + margin ) + margin; + x = ( morale - ::game::unit::Unit::MORALE_MIN ) * ( w + margin ) + margin; y = row * ( h + margin ) + margin; + + // cut holes for healthbars + texture->Fill( x + 6, y + 6, x + 7, y + 26, transparent ); + spritemap.insert( { morale, &GetInstancedSprite( - "Badge_" + std::to_string( badge_type ) + "_" + std::to_string( badge_mode ), pcx_file, { + "Badge_" + std::to_string( badge_type ) + "_" + std::to_string( badge_mode ), + texture, + { x, y, }, @@ -1268,9 +1284,9 @@ void Game::Initialize( }, { ::game::map::s_consts.tile.scale.x * s_consts.badges.scale_x, - ::game::map::s_consts.tile.scale.y * ::game::map::s_consts.sprite.y_scale * s_consts.badges.scale_y + ::game::map::s_consts.tile.scale.y * s_consts.badges.scale_y * ::game::map::s_consts.sprite.y_scale }, - 0.52f + 0.6f ) } ); @@ -1278,6 +1294,55 @@ void Game::Initialize( } } } + { + // load healthbar sprites + ASSERT( m_healthbar_textures.empty(), "healthbar textures already initialized" ); + ASSERT( m_healthbar_sprites.empty(), "healthbar sprites already initialized" ); + + const auto res = s_consts.badges.healthbars.resolution; + + const float stepval = 1.0f / ( res ); + types::Color black( 0.0f, 0.0f, 0.0f ); + types::Color color( 1.0f - stepval / 2, stepval / 2, 0.0f ); + + m_healthbar_textures.reserve( res ); + m_healthbar_sprites.resize( res ); + for ( auto step = 0 ; step < res ; step++ ) { + + // generate healthbar texture + auto* texture = new types::Texture( "HealthBar_" + std::to_string( step ), 1, res ); + + texture->Fill( 0, 0, 0, step, color ); + if ( step < res - 1 ) { + texture->Fill( 0, step + 1, 0, res - 1, black ); + } + + m_healthbar_textures.push_back( texture ); + m_healthbar_sprites.at( step ).instanced_sprite = &GetInstancedSprite( + "Badge_Healthbar_" + std::to_string( step ), + texture, + { + 0, + 0, + }, + { + 1, + res, + }, + { + 0, + 0, + }, + { + ::game::map::s_consts.tile.scale.x * s_consts.badges.healthbars.scale_x, + ::game::map::s_consts.tile.scale.y * s_consts.badges.healthbars.scale_y * ::game::map::s_consts.sprite.y_scale + }, + 0.525f + ); + color.value.red -= stepval; + color.value.green += stepval; + } + } // UI ui->AddTheme( &m_ui.theme ); @@ -1644,6 +1709,11 @@ void Game::Deinitialize() { m_textures.terrain = nullptr; } + for ( const auto& texture : m_healthbar_textures ) { + delete texture; + } + m_healthbar_textures.clear(); + m_slot_states.clear(); for ( const auto& it : m_textures.repainted_source ) { diff --git a/src/task/game/Game.h b/src/task/game/Game.h index fd2660c8..b2c078eb 100644 --- a/src/task/game/Game.h +++ b/src/task/game/Game.h @@ -86,6 +86,13 @@ CLASS( Game, base::Task ) const float scale_y = 0.5f; const float offset_x = -0.25f; const float offset_y = -0.5; + const struct { + const uint8_t resolution = 10; + const float scale_x = 0.04f; + const float scale_y = 0.36f; + const float offset_x = -0.292f; + const float offset_y = -0.476f; + } healthbars; } badges; }; static const consts_t s_consts; @@ -109,7 +116,7 @@ CLASS( Game, base::Task ) }; instanced_sprite_t& GetInstancedSprite( const std::string& name, - const std::string& tex_file, + types::Texture* texture, const ::game::map::Consts::pcx_texture_coordinates_t& src_xy, const ::game::map::Consts::pcx_texture_coordinates_t& src_wh, const ::game::map::Consts::pcx_texture_coordinates_t& src_cxy, @@ -415,6 +422,13 @@ CLASS( Game, base::Task ) typedef std::unordered_map< ::game::unit::Unit::morale_t, unitbadge_def_t > unitbadge_defs_t; unitbadge_spritemaps_t m_unitbadge_sprites = {}; + struct unitbadge_healthbar_def_t { + Game::instanced_sprite_t* instanced_sprite = nullptr; + size_t next_instance_id = 1; + }; + std::vector< types::Texture* > m_healthbar_textures = {}; + std::vector< unitbadge_healthbar_def_t > m_healthbar_sprites = {}; + struct slot_state_t { types::Color color = {}; unitbadge_defs_t badges = {}; @@ -428,6 +442,8 @@ CLASS( Game, base::Task ) size_t instance_id = 0; unitbadge_subdef_t* badge_def = nullptr; size_t badge_instance_id = 0; + unitbadge_healthbar_def_t* badge_healthbar_def = nullptr; + size_t badge_healthbar_instance_id = 0; } render; bool is_active = false; ::game::unit::Unit::morale_t morale = 0; diff --git a/src/types/Texture.cpp b/src/types/Texture.cpp index 78b0ce53..0e201357 100644 --- a/src/types/Texture.cpp +++ b/src/types/Texture.cpp @@ -102,11 +102,25 @@ void Texture::Erase( const size_t x1, const size_t y1, const size_t x2, const si ); } +void Texture::Fill( const size_t x1, const size_t y1, const size_t x2, const size_t y2, const types::Color& color ) { + ASSERT( x2 >= x1, "invalid source x size ( " + std::to_string( x2 ) + " < " + std::to_string( x1 ) + " )" ); + ASSERT( y2 >= y1, "invalid source y size ( " + std::to_string( y2 ) + " < " + std::to_string( y1 ) + " )" ); + ASSERT( x2 < m_width, "x overflow ( " + std::to_string( x2 ) + " >= " + std::to_string( m_width ) + " )" ); + ASSERT( y2 < m_height, "y overflow ( " + std::to_string( y2 ) + " >= " + std::to_string( m_height ) + " )" ); + + const uint32_t rgba = color.GetRGBA(); + for ( auto y = y1 ; y <= y2 ; y++ ) { + for ( auto x = x1 ; x <= x2 ; x++ ) { + memcpy( ptr( m_bitmap, ( y * m_width + x ) * m_bpp, sizeof( rgba ) ), &rgba, sizeof( rgba ) ); + } + } +} + void Texture::AddFrom( const types::Texture* source, add_flag_t flags, const size_t x1, const size_t y1, const size_t x2, const size_t y2, const size_t dest_x, const size_t dest_y, const rotate_t rotate, const float alpha, util::Random* rng, util::Perlin* perlin ) { - ASSERT( dest_x + ( x2 - x1 ) < m_width, "destination x overflow ( " + std::to_string( dest_x + ( x2 - x1 ) ) + " >= " + std::to_string( m_width ) + " )" ); - ASSERT( dest_y + ( y2 - y1 ) < m_height, "destination y overflow (" + std::to_string( dest_y + ( y2 - y1 ) ) + " >= " + std::to_string( m_height ) + " )" ); ASSERT( x2 >= x1, "invalid source x size ( " + std::to_string( x2 ) + " < " + std::to_string( x1 ) + " )" ); ASSERT( y2 >= y1, "invalid source y size ( " + std::to_string( y2 ) + " < " + std::to_string( y1 ) + " )" ); + ASSERT( dest_x + ( x2 - x1 ) < m_width, "destination x overflow ( " + std::to_string( dest_x + ( x2 - x1 ) ) + " >= " + std::to_string( m_width ) + " )" ); + ASSERT( dest_y + ( y2 - y1 ) < m_height, "destination y overflow (" + std::to_string( dest_y + ( y2 - y1 ) ) + " >= " + std::to_string( m_height ) + " )" ); ASSERT( alpha >= 0, "invalid alpha value ( " + std::to_string( alpha ) + " < 0 )" ); ASSERT( alpha <= 1, "invalid alpha value ( " + std::to_string( alpha ) + " > 1 )" ); diff --git a/src/types/Texture.h b/src/types/Texture.h index 61f09b85..a617813b 100644 --- a/src/types/Texture.h +++ b/src/types/Texture.h @@ -110,6 +110,8 @@ CLASS( Texture, Serializable ) */ void AddFrom( const types::Texture* source, add_flag_t flags, const size_t x1, const size_t y1, const size_t x2, const size_t y2, const size_t dest_x = 0, const size_t dest_y = 0, const rotate_t rotate = 0, const float alpha = 1.0f, util::Random* rng = nullptr, util::Perlin* perlin = nullptr ); + void Fill( const size_t x1, const size_t y1, const size_t x2, const size_t y2, const types::Color& color ); + typedef std::unordered_map< types::Color::rgba_t, types::Color::rgba_t > repaint_rules_t; void RepaintFrom( const types::Texture* original, const repaint_rules_t& rules ); From 7b603c0b5d41522b90fa3332eec7d83099a87c61 Mon Sep 17 00:00:00 2001 From: afwbkbc Date: Sun, 18 Feb 2024 23:45:39 +0200 Subject: [PATCH 19/21] morale based sprites for native lifeforms --- gse/default/main.gls.js | 6 ++- gse/default/units.gls.js | 40 +++++---------- src/game/bindings/Factions.cpp | 2 +- src/game/bindings/Units.cpp | 3 +- src/game/unit/SpriteRender.cpp | 18 +++++-- src/game/unit/SpriteRender.h | 12 ++++- src/gse/callable/Native.h | 12 +++-- src/task/game/Game.cpp | 94 ++++++++++++++++++++++++++-------- src/task/game/Game.h | 32 ++++++------ 9 files changed, 140 insertions(+), 79 deletions(-) diff --git a/gse/default/main.gls.js b/gse/default/main.gls.js index e350bcd7..91723cfa 100644 --- a/gse/default/main.gls.js +++ b/gse/default/main.gls.js @@ -57,7 +57,11 @@ let random_health = () => { if (#game.random.get_int(0, 2) != 1) { unit = #game.units.spawn('MindWorms', owner, tile, random_morale(), random_health()); } else { - unit = #game.units.spawn('SporeLauncher', owner, tile, random_morale(), random_health()); + if (#game.random.get_int(0, 1) == 0) { + unit = #game.units.spawn('FungalTower', owner, tile, random_morale(), random_health()); + } else { + unit = #game.units.spawn('SporeLauncher', owner, tile, random_morale(), random_health()); + } } } else { if (#game.random.get_int(0, 1) == 1) { diff --git a/gse/default/units.gls.js b/gse/default/units.gls.js index 456cdd0b..d89c4648 100644 --- a/gse/default/units.gls.js +++ b/gse/default/units.gls.js @@ -1,36 +1,20 @@ -return [ - - ['MindWorms', { - type: 'static', - render: { - type: 'sprite', - file: 'Units.pcx', - x: 206, y: 233, - w: 100, h: 75, - cx: 257, cy: 284, - }, - }], - - ['SeaLurk', { +let native_lifeform = (name, base_y) => { + return [name, { type: 'static', render: { type: 'sprite', file: 'Units.pcx', - x: 104, y: 310, + x: 2, y: base_y, w: 100, h: 75, - cx: 155, cy: 353, + cx: 53, cy: base_y + 51, + morale_based_xshift: 102 }, - }], - - ['SporeLauncher', { - type: 'static', - render: { - type: 'sprite', - file: 'Units.pcx', - x: 308, y: 387, - w: 100, h: 75, - cx: 359, cy: 438, - } - }] + }]; +}; +return [ + native_lifeform('FungalTower', 79), + native_lifeform('MindWorms', 233), + native_lifeform('SeaLurk', 310), + native_lifeform('SporeLauncher', 387), ]; diff --git a/src/game/bindings/Factions.cpp b/src/game/bindings/Factions.cpp index 9f5f90e6..d5f0832a 100644 --- a/src/game/bindings/Factions.cpp +++ b/src/game/bindings/Factions.cpp @@ -31,7 +31,7 @@ BINDING_IMPL( factions ) { N_GETPROP( pcx_file, files, "pcx", String ); faction.ImportPCX( pcx_file ); - N_GETPROP_OPTBOOL( is_progenitor, faction_def, "is_progenitor") + 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/bindings/Units.cpp b/src/game/bindings/Units.cpp index e5aee8b0..b48a6552 100644 --- a/src/game/bindings/Units.cpp +++ b/src/game/bindings/Units.cpp @@ -52,9 +52,10 @@ BINDING_IMPL( units ) { N_GETPROP( sprite_h, render_def, "h", Int ); N_GETPROP( sprite_cx, render_def, "cx", Int ); N_GETPROP( sprite_cy, render_def, "cy", Int ); + N_GETPROP_OPT_INT( sprite_morale_based_xshift, render_def, "morale_based_xshift" ); const auto* def = new unit::StaticDef( name, - new unit::SpriteRender( sprite_file, sprite_x, sprite_y, sprite_w, sprite_h, sprite_cx, sprite_cy ) + new unit::SpriteRender( sprite_file, sprite_x, sprite_y, sprite_w, sprite_h, sprite_cx, sprite_cy, sprite_morale_based_xshift ) ); return GAME->AddGameEvent( new event::DefineUnit( def ), ctx, call_si ); } diff --git a/src/game/unit/SpriteRender.cpp b/src/game/unit/SpriteRender.cpp index e92cee7e..83c0c3e9 100644 --- a/src/game/unit/SpriteRender.cpp +++ b/src/game/unit/SpriteRender.cpp @@ -3,7 +3,16 @@ namespace game { namespace unit { -SpriteRender::SpriteRender( const std::string& file, const uint32_t x, const uint32_t y, const uint32_t w, const uint32_t h, const uint32_t cx, const uint32_t cy ) +SpriteRender::SpriteRender( + const std::string& file, + const uint32_t x, + const uint32_t y, + const uint32_t w, + const uint32_t h, + const uint32_t cx, + const uint32_t cy, + const uint32_t morale_based_xshift +) : Render( RT_SPRITE ) , m_file( file ) , m_x( x ) @@ -13,7 +22,8 @@ SpriteRender::SpriteRender( const std::string& file, const uint32_t x, const uin , m_cx( cx ) , m_cy( cy ) , m_cshift_x( (float)( cx - x ) / w * 2 - 1.0f ) - , m_cshift_y( (float)( cy - y ) / h * 2 - 1.0f ) { + , m_cshift_y( (float)( cy - y ) / h * 2 - 1.0f ) + , m_morale_based_xshift( morale_based_xshift ) { // } @@ -47,6 +57,7 @@ void SpriteRender::Serialize( types::Buffer& buf, const SpriteRender* render ) { buf.WriteInt( render->m_h ); buf.WriteInt( render->m_cx ); buf.WriteInt( render->m_cy ); + buf.WriteInt( render->m_morale_based_xshift ); } SpriteRender* SpriteRender::Unserialize( types::Buffer& buf ) { @@ -57,7 +68,8 @@ SpriteRender* SpriteRender::Unserialize( types::Buffer& buf ) { const auto h = buf.ReadInt(); const auto cx = buf.ReadInt(); const auto cy = buf.ReadInt(); - return new SpriteRender( file, x, y, w, h, cx, cy ); + const auto morale_based_xshift = buf.ReadInt(); + return new SpriteRender( file, x, y, w, h, cx, cy, morale_based_xshift ); } } diff --git a/src/game/unit/SpriteRender.h b/src/game/unit/SpriteRender.h index 15a042de..8bba4343 100644 --- a/src/game/unit/SpriteRender.h +++ b/src/game/unit/SpriteRender.h @@ -9,7 +9,16 @@ namespace unit { class SpriteRender : public Render { public: - SpriteRender( const std::string& file, const uint32_t x, const uint32_t y, const uint32_t w, const uint32_t h, const uint32_t cx, const uint32_t cy ); + SpriteRender( + const std::string& file, + const uint32_t x, + const uint32_t y, + const uint32_t w, + const uint32_t h, + const uint32_t cx, + const uint32_t cy, + const uint32_t morale_based_xshift + ); const std::string m_file; const uint32_t m_x; @@ -18,6 +27,7 @@ class SpriteRender : public Render { const uint32_t m_h; const uint32_t m_cx; const uint32_t m_cy; + const uint32_t m_morale_based_xshift; const float m_cshift_x; const float m_cshift_y; diff --git a/src/gse/callable/Native.h b/src/gse/callable/Native.h index 6aa24608..401f2615 100644 --- a/src/gse/callable/Native.h +++ b/src/gse/callable/Native.h @@ -87,17 +87,19 @@ namespace callable { N_GETPROP_ARG( _obj, _key, Object ); \ N_CHECK_OBJECT_CLASS( arg, gse::type::Object::_class ); \ const auto& _var = ((gse::type::Object*)arg)->value; -#define N_GETPROP_OPTBOOL( _var, _obj, _key ) \ - bool _var = false; \ +#define N_GETPROP_OPT( _vartype, _var, _obj, _key, _type, _default ) \ + _vartype _var = _default; \ obj_it = _obj.find( _key ); \ if ( obj_it != _obj.end() ) { \ getprop_val = obj_it->second; \ arg = getprop_val.Get(); \ - if ( arg->type != gse::type::Type::T_BOOL ) { \ - throw gse::Exception( gse::EC.INVALID_CALL, (std::string)"Property '" + _key + "' is expected to be bool, found: " + arg->GetTypeString( arg->type ), ctx, call_si ); \ + if ( arg->type != gse::type::_type::GetType() ) { \ + throw gse::Exception( gse::EC.INVALID_CALL, (std::string)"Property '" + _key + "' is expected to be " + #_type + ", found: " + arg->GetTypeString( arg->type ), ctx, call_si ); \ } \ - _var = ((gse::type::Bool*)arg)->value; \ + _var = ((gse::type::_type*)arg)->value; \ } +#define N_GETPROP_OPT_BOOL( _var, _obj, _key ) N_GETPROP_OPT( bool, _var, _obj, _key, Bool, false ) +#define N_GETPROP_OPT_INT( _var, _obj, _key ) N_GETPROP_OPT( int64_t, _var, _obj, _key, Int, 0 ) class Native : public type::Callable { public: diff --git a/src/task/game/Game.cpp b/src/task/game/Game.cpp index 5519e49a..80cf6f20 100644 --- a/src/task/game/Game.cpp +++ b/src/task/game/Game.cpp @@ -925,26 +925,61 @@ void Game::DefineUnit( const ::game::unit::Def* unitdef ) { const auto* render = (::game::unit::SpriteRender*)def->m_render; unitdef_state.static_.render.is_sprite = true; - unitdef_state.static_.render.sprite.instanced_sprite = &GetInstancedSprite( - "Unit_" + def->m_name, GetSourceTexture( render->m_file ), { - render->m_x, - render->m_y, - }, - { - render->m_w, - render->m_h, - }, - { - render->m_cx, - render->m_cy, - }, - { - ::game::map::s_consts.tile.scale.x, - ::game::map::s_consts.tile.scale.y * ::game::map::s_consts.sprite.y_scale - }, - 0.5f - ); + const auto name = "Unit_" + def->m_name; + auto* texture = GetSourceTexture( render->m_file ); + const ::game::map::Consts::pcx_texture_coordinates_t& src_wh = { + render->m_w, + render->m_h, + }; + const Vec2< float >& dst_wh = { + ::game::map::s_consts.tile.scale.x, + ::game::map::s_consts.tile.scale.y * ::game::map::s_consts.sprite.y_scale + }; + const auto zindex = 0.5f; + + unitdef_state.static_.render.morale_based_xshift = render->m_morale_based_xshift; + if ( unitdef_state.static_.render.morale_based_xshift ) { + NEW( unitdef_state.static_.render.morale_based_sprites, morale_based_sprite_states_t ); + for ( ::game::unit::Unit::morale_t morale = ::game::unit::Unit::MORALE_MIN ; morale <= ::game::unit::Unit::MORALE_MAX ; morale++ ) { + const uint32_t xshift = unitdef_state.static_.render.morale_based_xshift * ( morale - ::game::unit::Unit::MORALE_MIN ); + unitdef_state.static_.render.morale_based_sprites->insert( + { + morale, + { + &GetInstancedSprite( + name + "_" + std::to_string( morale ), texture, { + render->m_x + xshift, + render->m_y, + }, + src_wh, + { + render->m_cx + xshift, + render->m_cy, + }, + dst_wh, + zindex + ), + } + } + ); + } + } + else { + unitdef_state.static_.render.sprite.instanced_sprite = &GetInstancedSprite( + name, texture, { + render->m_x, + render->m_y, + }, + src_wh, + { + render->m_cx, + render->m_cy, + }, + dst_wh, + zindex + ); + } break; } default: @@ -994,8 +1029,11 @@ void Game::SpawnUnit( unit_state.health = health; // add render - unit_state.render.instance_id = unitdef_state.static_.render.sprite.next_instance_id++; - unitdef_state.static_.render.sprite.instanced_sprite->actor->SetInstance( + auto& sprite = unitdef_state.static_.render.morale_based_xshift + ? unitdef_state.static_.render.morale_based_sprites->at( unit_state.morale ) + : unitdef_state.static_.render.sprite; + unit_state.render.instance_id = sprite.next_instance_id++; + sprite.instanced_sprite->actor->SetInstance( unit_state.render.instance_id, { x, y, @@ -1047,7 +1085,10 @@ void Game::DespawnUnit( const size_t unit_id ) { ASSERT( unitdef_state->m_type == ::game::unit::Def::DT_STATIC, "only static unitdefs are supported for now" ); ASSERT( unitdef_state->static_.render.is_sprite, "only sprite unitdefs are supported for now" ); - unitdef_state->static_.render.sprite.instanced_sprite->actor->RemoveInstance( unit_state.render.instance_id ); + auto& sprite = unitdef_state->static_.render.morale_based_xshift + ? unitdef_state->static_.render.morale_based_sprites->at( unit_state.morale ) + : unitdef_state->static_.render.sprite; + sprite.instanced_sprite->actor->RemoveInstance( unit_state.render.instance_id ); unit_state.render.badge_def->instanced_sprite->actor->RemoveInstance( unit_state.render.badge_instance_id ); unit_state.render.badge_healthbar_def->instanced_sprite->actor->RemoveInstance( unit_state.render.badge_healthbar_instance_id ); @@ -1721,6 +1762,15 @@ void Game::Deinitialize() { } m_textures.repainted_source.clear(); + for ( auto& it : m_unitdef_states ) { + if ( it.second.m_type == ::game::unit::Def::DT_STATIC ) { + if ( it.second.static_.render.morale_based_xshift ) { + DELETE( it.second.static_.render.morale_based_sprites ); + } + } + } + m_unitdef_states.clear(); + for ( auto& it : m_actors_map ) { m_world_scene->RemoveActor( it.second ); DELETE( it.second ); diff --git a/src/task/game/Game.h b/src/task/game/Game.h index b2c078eb..9017583f 100644 --- a/src/task/game/Game.h +++ b/src/task/game/Game.h @@ -386,17 +386,23 @@ CLASS( Game, base::Task ) #endif } m_mt_ids = {}; + struct sprite_state_t { + Game::instanced_sprite_t* instanced_sprite = nullptr; + size_t next_instance_id = 1; + }; + + typedef std::unordered_map< ::game::unit::Unit::morale_t, sprite_state_t > morale_based_sprite_states_t; + struct unitdef_state_t { ::game::unit::Def::def_type_t m_type; union { struct { struct { bool is_sprite; + uint32_t morale_based_xshift; union { - struct { - Game::instanced_sprite_t* instanced_sprite = nullptr; - size_t next_instance_id = 1; - } sprite; + sprite_state_t sprite; + morale_based_sprite_states_t* morale_based_sprites; }; } render; } static_; @@ -411,23 +417,15 @@ CLASS( Game, base::Task ) const badge_type_t BT_DEFAULT = 1 << 1; const badge_type_t BT_PROGENITOR = 0 << 1; typedef std::unordered_map< badge_type_t, unitbadge_spritemap_t > unitbadge_spritemaps_t; - struct unitbadge_subdef_t { - Game::instanced_sprite_t* instanced_sprite = nullptr; - size_t next_instance_id = 1; - }; struct unitbadge_def_t { - unitbadge_subdef_t normal; - unitbadge_subdef_t greyedout; + sprite_state_t normal; + sprite_state_t greyedout; }; typedef std::unordered_map< ::game::unit::Unit::morale_t, unitbadge_def_t > unitbadge_defs_t; unitbadge_spritemaps_t m_unitbadge_sprites = {}; - struct unitbadge_healthbar_def_t { - Game::instanced_sprite_t* instanced_sprite = nullptr; - size_t next_instance_id = 1; - }; std::vector< types::Texture* > m_healthbar_textures = {}; - std::vector< unitbadge_healthbar_def_t > m_healthbar_sprites = {}; + std::vector< sprite_state_t > m_healthbar_sprites = {}; struct slot_state_t { types::Color color = {}; @@ -440,9 +438,9 @@ CLASS( Game, base::Task ) slot_state_t* slot = nullptr; struct { size_t instance_id = 0; - unitbadge_subdef_t* badge_def = nullptr; + sprite_state_t* badge_def = nullptr; size_t badge_instance_id = 0; - unitbadge_healthbar_def_t* badge_healthbar_def = nullptr; + sprite_state_t* badge_healthbar_def = nullptr; size_t badge_healthbar_instance_id = 0; } render; bool is_active = false; From 1688c9a342607fc6d90e493ab3188c62564efb69 Mon Sep 17 00:00:00 2001 From: afwbkbc Date: Sun, 18 Feb 2024 23:57:14 +0200 Subject: [PATCH 20/21] fixed fungal towers spawning --- gse/default/main.gls.js | 15 +++++++++++++-- src/game/map/Tile.cpp | 17 +++++++++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/gse/default/main.gls.js b/gse/default/main.gls.js index 91723cfa..b4d8a378 100644 --- a/gse/default/main.gls.js +++ b/gse/default/main.gls.js @@ -57,8 +57,19 @@ let random_health = () => { if (#game.random.get_int(0, 2) != 1) { unit = #game.units.spawn('MindWorms', owner, tile, random_morale(), random_health()); } else { - if (#game.random.get_int(0, 1) == 0) { - unit = #game.units.spawn('FungalTower', owner, tile, random_morale(), random_health()); + if (tile.has_fungus && #game.random.get_int(0, 1) == 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 < 7 && i < sz) { + if (neighbours[i].has_fungus) { + morale++; + } + i++; + } + unit = #game.units.spawn('FungalTower', owner, tile, morale, random_health()); } else { unit = #game.units.spawn('SporeLauncher', owner, tile, random_morale(), random_health()); } diff --git a/src/game/map/Tile.cpp b/src/game/map/Tile.cpp index ffef5e60..160d791d 100644 --- a/src/game/map/Tile.cpp +++ b/src/game/map/Tile.cpp @@ -1,6 +1,7 @@ #include "Tile.h" #include "gse/type/Object.h" +#include "gse/type/Array.h" #include "gse/type/Int.h" #include "gse/type/Bool.h" #include "gse/callable/Native.h" @@ -104,6 +105,10 @@ WRAPIMPL_BEGIN( Tile, CLASS_TILE ) "is_land", VALUE( gse::type::Bool, !is_water_tile ) }, + { + "has_fungus", + VALUE( gse::type::Bool, ( features & F_XENOFUNGUS ) == F_XENOFUNGUS ) + }, GETN( W ), GETN( NW ), GETN( N ), @@ -112,9 +117,21 @@ WRAPIMPL_BEGIN( Tile, CLASS_TILE ) GETN( SE ), GETN( S ), GETN( SW ), + { + "get_surrounding_tiles", + NATIVE_CALL( this ) { + N_ARGS( 0 ); + gse::type::Array::elements_t result = {}; + for ( const auto& n : neighbours ) { + result.push_back( n->Wrap() ); + } + return VALUE( gse::type::Array, result ); + }) + }, { "get_units", NATIVE_CALL( this ) { + N_ARGS( 0 ); gse::type::Object::properties_t result = {}; for ( auto& it : units ) { result.insert_or_assign( std::to_string( it.second->m_id ), it.second->Wrap() ); From 3a0c45693eed117c222e1a97bf8bf00edea2726b Mon Sep 17 00:00:00 2001 From: afwbkbc Date: Mon, 19 Feb 2024 00:17:05 +0200 Subject: [PATCH 21/21] fixed release build --- src/game/rules/Rules.h | 1 + src/types/Texture.h | 1 + 2 files changed, 2 insertions(+) diff --git a/src/game/rules/Rules.h b/src/game/rules/Rules.h index 43f5ae30..f09163fb 100644 --- a/src/game/rules/Rules.h +++ b/src/game/rules/Rules.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include "types/Serializable.h" diff --git a/src/types/Texture.h b/src/types/Texture.h index a617813b..f724b670 100644 --- a/src/types/Texture.h +++ b/src/types/Texture.h @@ -2,6 +2,7 @@ #include #include +#include #include "types/Serializable.h"