From e0ee8a0256e8143e109176adf366a9c2fdd473e6 Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Sun, 6 Oct 2024 17:11:44 +0200 Subject: [PATCH] add skin (#67) * build in release mode * fix raze city * cleanup local client * fix raze city * add skin * add skin * add skin * add skin for advances * add skin for advances * add skin for advances * add skin for advances * add skin for advances --- client/assets/HTOWERT.TTF | Bin 0 -> 92736 bytes client/assets/button_background.png | Bin 0 -> 5618 bytes client/assets/button_clicked_background.png | Bin 0 -> 10769 bytes client/assets/button_hovered_background.png | Bin 0 -> 6043 bytes client/assets/window_background.png | Bin 0 -> 13322 bytes client/src/advance_ui.rs | 199 +++++++++++------- client/src/assets.rs | 111 +++++++++- client/src/client.rs | 46 ++-- client/src/client_state.rs | 32 ++- client/src/collect_ui.rs | 7 +- client/src/construct_ui.rs | 1 + client/src/dialog_ui.rs | 23 +- client/src/happiness_ui.rs | 23 +- client/src/layout_ui.rs | 51 +++++ client/src/lib.rs | 2 +- client/src/local_client.rs | 166 --------------- client/src/local_client/bin/main.rs | 175 ++++++++++++++- client/src/move_ui.rs | 3 +- client/src/payment_ui.rs | 2 + client/src/player_ui.rs | 193 +++++++++-------- client/src/recruit_unit_ui.rs | 1 + client/src/select_ui.rs | 15 +- client/src/status_phase_ui.rs | 13 ++ client/src/unit_ui.rs | 9 +- scripts/publish-client.sh | 2 +- server/src/content/advances.rs | 16 +- server/src/player.rs | 6 +- .../tests/test_games/change_government.json | 6 +- .../test_games/change_government.outcome.json | 8 +- 29 files changed, 689 insertions(+), 421 deletions(-) create mode 100644 client/assets/HTOWERT.TTF create mode 100644 client/assets/button_background.png create mode 100644 client/assets/button_clicked_background.png create mode 100644 client/assets/button_hovered_background.png create mode 100644 client/assets/window_background.png create mode 100644 client/src/layout_ui.rs delete mode 100644 client/src/local_client.rs diff --git a/client/assets/HTOWERT.TTF b/client/assets/HTOWERT.TTF new file mode 100644 index 0000000000000000000000000000000000000000..48ad007c12a5420765c9ecd576463fbe55d8ebcb GIT binary patch literal 92736 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%0Dizzut0pfU~swIlFKF%1{l_b;V@CiF)1#= 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 M07*qoM6N<$f(YBLlmGw# literal 0 HcmV?d00001 diff --git a/client/assets/button_clicked_background.png b/client/assets/button_clicked_background.png new file mode 100644 index 0000000000000000000000000000000000000000..d6c6654832f42d13b472a853c2134ed0d6a62eae GIT binary patch literal 10769 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 Ah5!Hn literal 0 HcmV?d00001 diff --git a/client/assets/button_hovered_background.png b/client/assets/button_hovered_background.png new file mode 100644 index 0000000000000000000000000000000000000000..d004de323fb0783335cdc5fc3596de907ffc7c56 GIT binary patch literal 6043 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 VFW?*P(H#H)002ovPDHLkV1l(ijiLYm literal 0 HcmV?d00001 diff --git a/client/assets/window_background.png b/client/assets/window_background.png new file mode 100644 index 0000000000000000000000000000000000000000..2bd90b3608a4f9ecce605d94683ba49af335914c GIT binary patch literal 13322 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 StateUpdate { - show_generic_advance_menu("Advances", game, player, |name| { + show_generic_advance_menu("Advances", game, player, |a| { StateUpdate::SetDialog(ActiveDialog::AdvancePayment(AdvancePayment::new( game, player.index, - name, + a.name.as_str(), ))) }) } pub fn show_free_advance_menu(game: &Game, player: &ShownPlayer) -> StateUpdate { - show_generic_advance_menu("Select a free advance", game, player, |name| { - StateUpdate::status_phase(StatusPhaseAction::FreeAdvance(name.to_string())) + show_generic_advance_menu("Select a free advance", game, player, |a| { + if can_advance(game, player, a) { + return StateUpdate::execute_with_confirm( + description(game.get_player(player.index), a), + Action::StatusPhase(StatusPhaseAction::FreeAdvance(a.name.clone())), + ); + } + advance_info(game, player, a) }) } +fn advance_info(game: &Game, player: &ShownPlayer, a: &Advance) -> StateUpdate { + StateUpdate::execute_with_cancel(description(game.get_player(player.index), a)) +} + pub fn show_generic_advance_menu( title: &str, game: &Game, player: &ShownPlayer, - new_update: impl Fn(&str) -> StateUpdate, + new_update: impl Fn(&Advance) -> StateUpdate, ) -> StateUpdate { dialog(player, title, |ui| { let p = player.get(game); - let mut update = StateUpdate::None; - let mut current_group = None; - for (_a, list) in &advances::get_all().iter().chunk_by(|a| { - if a.required.is_none() { - current_group = Some(&a.name); - &a.name - } else { - current_group.unwrap() - } - }) { - let advances = list.collect::>(); - ui.group(hash!(&advances[0].name), vec2(1500., 90.), |ui| { - for a in advances { - let name = &a.name; - let can_advance = if player.can_play_action { - p.can_advance(name) - } else if player.can_control - && matches!( - game.state, - GameState::StatusPhase(StatusPhaseState::FreeAdvance) - ) - { - p.can_advance_free(name) - } else { - false - }; - - let desc = description(p, a); - if p.has_advance(name) { - ui.label(None, &desc); - } else if can_advance { - if ui.button(None, desc) { - update = new_update(name); - } - } else { - ui.label(None, &desc); - }; + + for advances in groups() { + let pos = group_pos(&advances[0]); + for (i, a) in advances.into_iter().enumerate() { + let pos = pos * vec2(140., 210.) + vec2(0., i as f32 * 35.); + let name = &a.name; + let can_advance = can_advance(game, player, &a); + + if can_advance || p.has_advance(name) { + let mut data = p.has_advance(name); + Checkbox::new(hash!(name)) + // .label(name) + .pos(pos + vec2(60., 50.)) + .size(vec2(0., 0.)) + .ui(ui, &mut data); + if data != p.has_advance(name) { + return new_update(&a); + } } - }); + // Button::new(name.clone()).position(pos + vec2(0., 0.)).ui(ui); + ui.label(pos + vec2(0., 0.), name); + } } - update + StateUpdate::None }) } -fn description(p: &Player, a: &Advance) -> String { +fn can_advance(game: &Game, player: &ShownPlayer, a: &Advance) -> bool { + let name = &a.name; + let p = player.get(game); + if player.can_play_action { + p.can_advance(name) + } else if player.can_control + && matches!( + game.state, + GameState::StatusPhase(StatusPhaseState::FreeAdvance) + ) + { + p.can_advance_free(name) + } else { + false + } +} + +fn groups() -> Vec> { + let mut current_group = None; + advances::get_all() + .into_iter() + .chunk_by(|a| { + if a.required.is_none() { + current_group = Some(a.name.clone()); + a.name.clone() + } else { + current_group.as_ref().unwrap().clone() + } + }) + .into_iter() + .map(|(_k, a)| a.collect::>()) + .collect::>() +} + +fn group_pos(advance: &Advance) -> Vec2 { + match advance.name.as_str() { + "Farming" => vec2(0., 0.), + "Mining" => vec2(1., 0.), + "Fishing" => vec2(2., 0.), + "Philosophy" => vec2(3., 0.), + "Tactics" => vec2(4., 0.), + "Math" => vec2(2., 1.), + "Voting" => vec2(3., 1.), + "Dogma" => vec2(5., 1.), + _ => panic!("Unknown advance: {}", advance.name), + } +} + +fn description(p: &Player, a: &Advance) -> Vec { let name = &a.name; let desc = &a.description; - let mut parts = vec![]; - parts.push(if p.has_advance(name) { - format!("+ {name}") - } else { - format!(" {name}") - }); + let mut parts: Vec = vec![]; + parts.push(name.clone()); parts.push(desc.clone()); parts.push(format!("Cost: {}", p.advance_cost(name))); if let Some(r) = &a.required { @@ -181,25 +217,32 @@ fn description(p: &Player, a: &Advance) -> String { parts.push(format!("Unlocks: {u}")); } - parts.join(", ") + parts } -pub fn pay_advance_dialog(ap: &AdvancePayment, player: &ShownPlayer) -> StateUpdate { - payment_dialog( - player, - &format!("Pay for advance {}", ap.name), - ap, - AdvancePayment::valid, - |ap| { - StateUpdate::Execute(Action::Playing(PlayingAction::Advance { - advance: ap.name.to_string(), - payment: ap.payment.to_resource_pile(), - })) - }, - |ap, r| ap.payment.get(r).selectable.max > 0, - |ap, r| add(ap, r, 1), - |ap, r| add(ap, r, -1), - ) +pub fn pay_advance_dialog(ap: &AdvancePayment, player: &ShownPlayer, game: &Game) -> StateUpdate { + let a = advances::get_advance_by_name(ap.name.as_str()).unwrap(); + + if can_advance(game, player, &a) { + payment_dialog( + player, + &format!("Pay for advance {}", ap.name), + description(game.get_player(player.index), &a), + ap, + AdvancePayment::valid, + |ap| { + StateUpdate::Execute(Action::Playing(PlayingAction::Advance { + advance: ap.name.to_string(), + payment: ap.payment.to_resource_pile(), + })) + }, + |ap, r| ap.payment.get(r).selectable.max > 0, + |ap, r| add(ap, r, 1), + |ap, r| add(ap, r, -1), + ) + } else { + advance_info(game, player, &a) + } } fn add(ap: &AdvancePayment, r: ResourceType, i: i32) -> StateUpdate { diff --git a/client/src/assets.rs b/client/src/assets.rs index 745b15e8..fbe47e9d 100644 --- a/client/src/assets.rs +++ b/client/src/assets.rs @@ -1,5 +1,6 @@ use crate::client::Features; -use macroquad::prelude::{load_texture, Texture2D}; +use macroquad::prelude::{load_texture, load_ttf_font, Color, Image, RectOffset, Texture2D}; +use macroquad::ui::{root_ui, Skin}; use server::map::Terrain; use server::unit::UnitType; use std::collections::HashMap; @@ -7,6 +8,7 @@ use std::collections::HashMap; pub struct Assets { pub terrain: HashMap, pub units: HashMap, + pub skin: Skin, // pub cities: HashMap, // pub resources: HashMap, } @@ -16,6 +18,7 @@ impl Assets { Self { terrain: Self::terrain(features).await, units: HashMap::new(), + skin: Self::skin(features).await, // cities: HashMap::new(), // resources: HashMap::new(), } @@ -31,9 +34,111 @@ impl Assets { (Terrain::Forest, "forest.png"), (Terrain::Water, "water.png"), ] { - let url = &features.assets_url; - map.insert(t, load_texture(&format!("{url}{f}")).await.unwrap()); + map.insert(t, load_texture(&features.get_asset(f)).await.unwrap()); } map } + + async fn skin(features: &Features) -> Skin { + let font = load_ttf_font(&features.get_asset("HTOWERT.TTF")) + .await + .unwrap(); + let image = + Image::from_file_with_format(include_bytes!("../assets/button_background.png"), None) + .unwrap(); + let label_style = root_ui() + .style_builder() + .background(image.clone()) + .background_margin(RectOffset::new(37.0, 37.0, 5.0, 5.0)) + .margin(RectOffset::new(10.0, 10.0, 0.0, 0.0)) + .with_font(&font) + .unwrap() + .text_color(Color::from_rgba(180, 180, 120, 255)) + .font_size(20) + .build(); + + let window_style = root_ui() + .style_builder() + .background( + Image::from_file_with_format( + include_bytes!("../assets/window_background.png"), + None, + ) + .unwrap(), + ) + .background_margin(RectOffset::new(20.0, 20.0, 10.0, 10.0)) + .margin(RectOffset::new(-20.0, -30.0, 0.0, 0.0)) + .build(); + + let button_style = root_ui() + .style_builder() + .background(image) + .background_margin(RectOffset::new(37.0, 37.0, 5.0, 5.0)) + .margin(RectOffset::new(10.0, 10.0, 0.0, 0.0)) + .background_hovered( + Image::from_file_with_format( + include_bytes!("../assets/button_hovered_background.png"), + None, + ) + .unwrap(), + ) + .background_clicked( + Image::from_file_with_format( + include_bytes!("../assets/button_clicked_background.png"), + None, + ) + .unwrap(), + ) + .with_font(&font) + .unwrap() + .text_color(Color::from_rgba(180, 180, 100, 255)) + .font_size(20) + .build(); + + let editbox_style = root_ui() + .style_builder() + .background_margin(RectOffset::new(0., 0., 0., 0.)) + .with_font(&font) + .unwrap() + .text_color(Color::from_rgba(120, 120, 120, 255)) + .color_selected(Color::from_rgba(190, 190, 190, 255)) + .font_size(50) + .build(); + + // let checkbox_style = root_ui() + // .style_builder() + // .background( + // Image::from_file_with_format( + // include_bytes!("../examples/ui_assets/checkbox_background.png"), + // None, + // ) + // .unwrap(), + // ) + // .background_hovered( + // Image::from_file_with_format( + // include_bytes!("../examples/ui_assets/checkbox_hovered_background.png"), + // None, + // ) + // .unwrap(), + // ) + // .background_clicked( + // Image::from_file_with_format( + // include_bytes!("../examples/ui_assets/checkbox_clicked_background.png"), + // None, + // ) + // .unwrap(), + // ) + // .build(); + + Skin { + editbox_style, + window_style, + button_style, + window_titlebar_style: label_style.clone(), + label_style, + // checkbox_style, + title_height: 30., + ..root_ui().default_skin() + } + } } diff --git a/client/src/client.rs b/client/src/client.rs index b6d4fc7c..0c76e8c2 100644 --- a/client/src/client.rs +++ b/client/src/client.rs @@ -1,21 +1,23 @@ use macroquad::input::{is_mouse_button_pressed, mouse_position, MouseButton}; use macroquad::prelude::*; +use macroquad::prelude::{clear_background, vec2}; +use macroquad::ui::root_ui; use server::action::Action; use server::game::Game; use server::position::Position; -use server::status_phase::StatusPhaseAction; use crate::advance_ui::{pay_advance_dialog, show_advance_menu, show_free_advance_menu}; use crate::client_state::{ActiveDialog, ShownPlayer, State, StateUpdate, StateUpdates}; use crate::collect_ui::{click_collect_option, collect_resources_dialog}; use crate::construct_ui::pay_construction_dialog; use crate::dialog_ui::active_dialog_window; -use crate::happiness_ui::{add_increase_happiness, increase_happiness_menu}; +use crate::happiness_ui::{increase_happiness_dialog, increase_happiness_menu}; use crate::hex_ui::pixel_to_coordinate; use crate::log_ui::show_log; use crate::map_ui::{draw_map, show_tile_menu}; use crate::player_ui::{show_global_controls, show_globals}; +use crate::status_phase_ui::raze_city_confirm_dialog; use crate::{combat_ui, dialog_ui, influence_ui, move_ui, recruit_unit_ui, status_phase_ui}; pub async fn init(features: &Features) -> State { @@ -43,8 +45,10 @@ pub fn render_and_update( } fn render(game: &Game, state: &mut State, features: &Features) -> StateUpdate { + root_ui().push_skin(&state.assets.skin); + clear_background(BLACK); + let player = &state.shown_player(game); - clear_background(WHITE); state.camera = Camera2D { zoom: vec2(state.zoom, state.zoom * screen_width() / screen_height()), @@ -57,8 +61,8 @@ fn render(game: &Game, state: &mut State, features: &Features) -> StateUpdate { draw_map(game, state); } let mut updates = StateUpdates::new(); - let update = show_globals(game, player); - updates.add(update); + updates.add(show_globals(game, player)); + updates.add(show_global_controls(game, state, features)); if player.can_control { if let Some(u) = &state.pending_update { @@ -67,8 +71,6 @@ fn render(game: &Game, state: &mut State, features: &Features) -> StateUpdate { } } - updates.add(show_global_controls(game, state, features)); - updates.add(match &state.active_dialog { ActiveDialog::None => StateUpdate::None, ActiveDialog::Log => show_log(game, player), @@ -80,7 +82,7 @@ fn render(game: &Game, state: &mut State, features: &Features) -> StateUpdate { // playing actions ActiveDialog::IncreaseHappiness(h) => increase_happiness_menu(h, player), ActiveDialog::AdvanceMenu => show_advance_menu(game, player), - ActiveDialog::AdvancePayment(p) => pay_advance_dialog(p, player), + ActiveDialog::AdvancePayment(p) => pay_advance_dialog(p, player, game), ActiveDialog::ConstructionPayment(p) => pay_construction_dialog(game, p, player), ActiveDialog::CollectResources(c) => collect_resources_dialog(game, c, player), ActiveDialog::RecruitUnitSelection(s) => recruit_unit_ui::select_dialog(game, s, player), @@ -134,13 +136,7 @@ pub fn try_click(game: &Game, state: &State, player: &ShownPlayer) -> StateUpdat ActiveDialog::ReplaceUnits(r) => recruit_unit_ui::click_replace(pos, r), ActiveDialog::RemoveCasualties(_s) => StateUpdate::None, ActiveDialog::CollectResources(col) => click_collect_option(col, pos), - ActiveDialog::RazeSize1City => { - if player.get(game).can_raze_city(pos) { - StateUpdate::status_phase(StatusPhaseAction::RaseSize1City(Some(pos))) - } else { - StateUpdate::None - } - } + ActiveDialog::RazeSize1City => raze_city_confirm_dialog(game, player, pos), ActiveDialog::PlaceSettler => { if player.get(game).get_city(pos).is_some() { StateUpdate::Execute(Action::PlaceSettler(pos)) @@ -148,18 +144,7 @@ pub fn try_click(game: &Game, state: &State, player: &ShownPlayer) -> StateUpdat StateUpdate::None } } - ActiveDialog::IncreaseHappiness(h) => { - if let Some(city) = player.get(game).get_city(pos) { - StateUpdate::SetDialog(ActiveDialog::IncreaseHappiness(add_increase_happiness( - player.get(game), - city, - pos, - h, - ))) - } else { - StateUpdate::None - } - } + ActiveDialog::IncreaseHappiness(h) => increase_happiness_dialog(game, player, pos, h), _ => StateUpdate::OpenDialog(ActiveDialog::TileMenu(pos)), } } else { @@ -172,6 +157,13 @@ pub struct Features { pub assets_url: String, } +impl Features { + #[must_use] + pub fn get_asset(&self, asset: &str) -> String { + format!("{}{}", self.assets_url, asset) + } +} + pub enum GameSyncRequest { None, ExecuteAction(Action), diff --git a/client/src/client_state.rs b/client/src/client_state.rs index 45d929d5..5dfff8f7 100644 --- a/client/src/client_state.rs +++ b/client/src/client_state.rs @@ -98,6 +98,8 @@ impl ActiveDialog { pub struct PendingUpdate { pub action: Action, pub warning: Vec, + pub info: Vec, + pub can_confirm: bool, } #[must_use] @@ -124,10 +126,33 @@ impl StateUpdate { if warning.is_empty() { StateUpdate::Execute(action) } else { - StateUpdate::ExecuteWithWarning(PendingUpdate { action, warning }) + StateUpdate::ExecuteWithWarning(PendingUpdate { + action, + warning, + info: vec![], + can_confirm: true, + }) } } + pub fn execute_with_confirm(info: Vec, action: Action) -> StateUpdate { + StateUpdate::ExecuteWithWarning(PendingUpdate { + action, + warning: vec![], + info, + can_confirm: true, + }) + } + + pub fn execute_with_cancel(info: Vec) -> StateUpdate { + StateUpdate::ExecuteWithWarning(PendingUpdate { + action: Action::Undo, // never used + warning: vec![], + info, + can_confirm: false, + }) + } + pub fn execute_activation(action: Action, warning: Vec, city: &City) -> StateUpdate { if city.is_activated() { match city.mood_state { @@ -195,6 +220,7 @@ pub struct ShownPlayer { pub can_control: bool, pub can_play_action: bool, pub active_dialog: ActiveDialog, + pub pending_update: bool, } impl ShownPlayer { @@ -216,7 +242,7 @@ pub struct State { } pub const ZOOM: f32 = 0.001; -pub const OFFSET: Vec2 = vec2(-0.8, 0.45); +pub const OFFSET: Vec2 = vec2(0., 0.45); impl State { pub async fn new(features: &Features) -> State { @@ -243,6 +269,7 @@ impl State { can_control: control, can_play_action: control && game.state == GameState::Playing && game.actions_left > 0, active_dialog: self.active_dialog.clone(), + pending_update: self.pending_update.is_some(), } } @@ -278,6 +305,7 @@ impl State { GameSyncRequest::ExecuteAction(action) } else { self.pending_update = None; + self.close_dialog(); GameSyncRequest::None } } diff --git a/client/src/collect_ui.rs b/client/src/collect_ui.rs index 0360a139..8484e461 100644 --- a/client/src/collect_ui.rs +++ b/client/src/collect_ui.rs @@ -3,7 +3,7 @@ use std::iter; use macroquad::color::BLACK; use macroquad::math::i32; -use macroquad::prelude::{draw_circle_lines, draw_text, Vec2, WHITE}; +use macroquad::prelude::{draw_circle_lines, draw_text, WHITE}; use server::action::Action; use server::consts::PORT_CHOICES; @@ -15,6 +15,7 @@ use server::resource_pile::ResourcePile; use crate::client_state::{ActiveDialog, ShownPlayer, State, StateUpdate}; use crate::dialog_ui::active_dialog_window; use crate::hex_ui; +use crate::layout_ui::{cancel_pos, ok_pos}; use crate::resource_ui::resource_pile_string; #[derive(Clone)] @@ -70,7 +71,7 @@ pub fn collect_resources_dialog( ) .is_some(); let label = if valid { "OK" } else { "(OK)" }; - if ui.button(Vec2::new(0., 40.), label) && valid { + if ui.button(ok_pos(player), label) && valid { return StateUpdate::execute_activation( Action::Playing(PlayingAction::Collect { city_position: collect.city_position, @@ -84,7 +85,7 @@ pub fn collect_resources_dialog( city, ); }; - if ui.button(Vec2::new(80., 40.), "Cancel") { + if ui.button(cancel_pos(player), "Cancel") { return StateUpdate::Cancel; }; StateUpdate::None diff --git a/client/src/construct_ui.rs b/client/src/construct_ui.rs index 37d0a7c7..3933dbd7 100644 --- a/client/src/construct_ui.rs +++ b/client/src/construct_ui.rs @@ -101,6 +101,7 @@ pub fn pay_construction_dialog( payment_dialog( player, &format!("Pay for {}", payment.name), + vec![], payment, |cp| cp.payment.get(ResourceType::Discount).selectable.current == 0, |cp| match &cp.project { diff --git a/client/src/dialog_ui.rs b/client/src/dialog_ui.rs index 4c6b8f3e..988a1d59 100644 --- a/client/src/dialog_ui.rs +++ b/client/src/dialog_ui.rs @@ -1,4 +1,5 @@ use crate::client_state::{PendingUpdate, ShownPlayer, StateUpdate}; +use crate::layout_ui::{cancel_pos, ok_only_pos, ok_pos}; use macroquad::hash; use macroquad::math::{vec2, Vec2}; use macroquad::prelude::screen_height; @@ -25,11 +26,11 @@ where { let width = screen_width() - 20.; let size = if player.active_dialog.is_map_dialog() { - vec2(width / 2.0, 100.) + vec2(width / 2.0, 270.) } else { - vec2(width, screen_height() - 100.) + vec2(width, screen_height() - 40.) }; - custom_dialog(title, vec2(10., 70.), size, f) + custom_dialog(title, vec2(10., 10.), size, f) } pub fn custom_dialog(title: &str, position: Vec2, size: Vec2, f: F) -> StateUpdate @@ -67,11 +68,21 @@ where pub fn show_pending_update(update: &PendingUpdate, player: &ShownPlayer) -> StateUpdate { active_dialog_window(player, "Are you sure?", |ui| { - ui.label(None, &format!("Warning: {}", update.warning.join(", "))); - if ui.button(None, "OK") { + for i in &update.info { + ui.label(None, i); + } + if !update.warning.is_empty() { + ui.label(None, &format!("Warning: {}", update.warning.join(", "))); + } + if update.can_confirm && ui.button(ok_pos(player), "OK") { return StateUpdate::ResolvePendingUpdate(true); } - if ui.button(None, "Cancel") { + let p = if update.can_confirm { + cancel_pos(player) + } else { + ok_only_pos(player) + }; + if ui.button(p, "Cancel") { return StateUpdate::ResolvePendingUpdate(false); } StateUpdate::None diff --git a/client/src/happiness_ui.rs b/client/src/happiness_ui.rs index 456e7030..529ea143 100644 --- a/client/src/happiness_ui.rs +++ b/client/src/happiness_ui.rs @@ -8,6 +8,7 @@ use server::resource_pile::ResourcePile; use crate::client_state::{ActiveDialog, ShownPlayer, StateUpdate}; use crate::dialog_ui::active_dialog_window; +use crate::layout_ui::{cancel_pos, ok_pos}; #[derive(Clone)] pub struct IncreaseHappiness { @@ -21,6 +22,24 @@ impl IncreaseHappiness { } } +pub fn increase_happiness_dialog( + game: &Game, + player: &ShownPlayer, + pos: Position, + h: &IncreaseHappiness, +) -> StateUpdate { + if let Some(city) = player.get(game).get_city(pos) { + StateUpdate::SetDialog(ActiveDialog::IncreaseHappiness(add_increase_happiness( + player.get(game), + city, + pos, + h, + ))) + } else { + StateUpdate::None + } +} + pub fn add_increase_happiness( player: &Player, city: &City, @@ -88,10 +107,10 @@ fn increase_happiness_new_steps( pub fn increase_happiness_menu(h: &IncreaseHappiness, player: &ShownPlayer) -> StateUpdate { active_dialog_window(player, "Increase Happiness", |ui| { ui.label(None, &format!("Cost: {:?}", h.cost)); - if ui.button(None, "Cancel") { + if ui.button(cancel_pos(player), "Cancel") { return StateUpdate::Cancel; } - if ui.button(None, "Confirm") { + if ui.button(ok_pos(player), "Confirm") { return StateUpdate::Execute(Action::Playing(PlayingAction::IncreaseHappiness { happiness_increases: h.steps.clone(), })); diff --git a/client/src/layout_ui.rs b/client/src/layout_ui.rs new file mode 100644 index 00000000..57510539 --- /dev/null +++ b/client/src/layout_ui.rs @@ -0,0 +1,51 @@ +use crate::client_state::ShownPlayer; +use macroquad::math::{vec2, Vec2}; +use macroquad::prelude::screen_height; +use macroquad::ui::root_ui; +use macroquad::window::screen_width; + +pub fn top_left_label(p: Vec2, label: &str) { + root_ui().label(p, label); +} + +pub fn top_center_label(p: Vec2, label: &str) { + root_ui().label(vec2(screen_width() / 2.0, 0.) + p, label); +} + +pub fn right_center_label(p: Vec2, label: &str) { + root_ui().label(vec2(screen_width(), screen_height() / 2.0) + p, label); +} + +pub fn right_center_button(p: Vec2, label: &str) -> bool { + root_ui().button(vec2(screen_width(), screen_height() / 2.0) + p, label) +} + +pub fn bottom_left_button(p: Vec2, label: &str) -> bool { + root_ui().button(vec2(0., screen_height()) + p, label) +} + +pub fn bottom_right_button(p: Vec2, label: &str) -> bool { + root_ui().button(vec2(screen_width(), screen_height()) + p, label) +} + +pub fn cancel_pos(player: &ShownPlayer) -> Vec2 { + small_dialog(player) + .then(|| Vec2::new(screen_width() / 4.0, 190.)) + .unwrap_or_else(|| Vec2::new(screen_width() / 2., screen_height() - 130.)) +} + +pub fn ok_pos(player: &ShownPlayer) -> Vec2 { + small_dialog(player) + .then(|| Vec2::new(screen_width() / 4.0 - 150., 190.)) + .unwrap_or_else(|| Vec2::new(screen_width() / 2. - 150., screen_height() - 130.)) +} + +pub fn ok_only_pos(player: &ShownPlayer) -> Vec2 { + small_dialog(player) + .then(|| Vec2::new(screen_width() / 4.0 - 75., 190.)) + .unwrap_or_else(|| Vec2::new(screen_width() / 2. - 75., screen_height() - 130.)) +} + +fn small_dialog(player: &ShownPlayer) -> bool { + player.active_dialog.is_map_dialog() || player.pending_update +} diff --git a/client/src/lib.rs b/client/src/lib.rs index 1ff4e312..0bc96ff9 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -20,7 +20,7 @@ mod dialog_ui; mod happiness_ui; mod hex_ui; mod influence_ui; -pub mod local_client; +mod layout_ui; mod log_ui; mod map_ui; mod move_ui; diff --git a/client/src/local_client.rs b/client/src/local_client.rs deleted file mode 100644 index e9eb1519..00000000 --- a/client/src/local_client.rs +++ /dev/null @@ -1,166 +0,0 @@ -#![allow(clippy::missing_panics_doc)] - -use crate::client; -use macroquad::prelude::next_frame; -use std::fs::File; -use std::io::BufReader; - -use crate::client::{Features, GameSyncRequest, GameSyncResult}; -use server::city::City; -use server::game::{Game, GameData}; -use server::leader::Leader; -use server::map::Terrain; -use server::position::Position; -use server::resource_pile::ResourcePile; -use server::unit::UnitType; - -pub async fn run(mut game: Game, features: &Features) { - let mut state = client::init(features).await; - - let mut sync_result = GameSyncResult::None; - state.show_player = game.active_player(); - loop { - state.control_player = Some(game.active_player()); - - let message = client::render_and_update(&game, &mut state, &sync_result, features); - sync_result = GameSyncResult::None; - match message { - GameSyncRequest::None => {} - GameSyncRequest::ExecuteAction(a) => { - game.execute_action(a, game.active_player()); - state.show_player = game.active_player(); - sync_result = GameSyncResult::Update; - } - GameSyncRequest::Import => { - game = import(); - state.show_player = game.active_player(); - sync_result = GameSyncResult::Update; - } - GameSyncRequest::Export => { - export(&game); - } - }; - next_frame().await; - } -} - -#[must_use] -pub fn setup_local_game() -> Game { - let mut game = Game::new(2, "0".to_string(), false); - game.dice_roll_outcomes = vec![1, 1, 10, 10, 10, 10, 10, 10, 10, 10]; - let add_unit = |game: &mut Game, pos: &str, player_index: usize, unit_type: UnitType| { - game.recruit( - player_index, - vec![unit_type], - Position::from_offset(pos), - None, - vec![], - ); - }; - - let player_index1 = 0; - let player_index2 = 1; - game.players[player_index1].gain_resources(ResourcePile::new(50, 50, 50, 50, 50, 50, 50)); - game.players[player_index2].gain_resources(ResourcePile::new(50, 50, 50, 50, 50, 50, 50)); - add_city(&mut game, player_index1, "A1"); - add_city(&mut game, player_index1, "C2"); - add_city(&mut game, player_index2, "C1"); - add_city(&mut game, player_index2, "B2"); - - add_terrain(&mut game, "A1", Terrain::Fertile); - add_terrain(&mut game, "A2", Terrain::Water); - add_terrain( - &mut game, - "A3", - Terrain::Exhausted(Box::new(Terrain::Forest)), - ); - add_terrain(&mut game, "B1", Terrain::Mountain); - add_terrain(&mut game, "B2", Terrain::Forest); - add_terrain(&mut game, "B3", Terrain::Fertile); - add_terrain(&mut game, "C1", Terrain::Barren); - add_terrain(&mut game, "C2", Terrain::Forest); - add_terrain(&mut game, "C3", Terrain::Water); - add_terrain(&mut game, "D2", Terrain::Water); - - add_unit(&mut game, "C2", player_index1, UnitType::Infantry); - add_unit(&mut game, "C2", player_index1, UnitType::Cavalry); - add_unit(&mut game, "C2", player_index1, UnitType::Leader); - add_unit(&mut game, "C2", player_index1, UnitType::Elephant); - add_unit(&mut game, "C2", player_index1, UnitType::Settler); - add_unit(&mut game, "C2", player_index1, UnitType::Settler); - add_unit(&mut game, "C2", player_index1, UnitType::Settler); - add_unit(&mut game, "C2", player_index1, UnitType::Settler); - game.players[player_index1].active_leader = - Some(Leader::builder("Alexander", "", "", "", "").build()); - - add_unit(&mut game, "C1", player_index2, UnitType::Infantry); - add_unit(&mut game, "C1", player_index2, UnitType::Infantry); - - game.players[player_index1] - .get_city_mut(Position::from_offset("A1")) - .unwrap() - .increase_mood_state(); - game.players[player_index1] - .get_city_mut(Position::from_offset("C2")) - .unwrap() - .pieces - .academy = Some(1); - game.players[player_index1] - .get_city_mut(Position::from_offset("C2")) - .unwrap() - .pieces - .port = Some(1); - game.players[player_index1] - .get_city_mut(Position::from_offset("C2")) - .unwrap() - .port_position = Some(Position::from_offset("C3")); - game.players[player_index1] - .get_city_mut(Position::from_offset("C2")) - .unwrap() - .pieces - .wonders = vec![game.wonders_left.pop().unwrap()]; - game.players[player_index1] - .get_city_mut(Position::from_offset("C2")) - .unwrap() - .increase_mood_state(); - - game.players[player_index2] - .get_city_mut(Position::from_offset("B2")) - .unwrap() - .pieces - .port = Some(1); - game.players[player_index2] - .get_city_mut(Position::from_offset("B2")) - .unwrap() - .port_position = Some(Position::from_offset("C3")); - add_unit(&mut game, "B2", player_index2, UnitType::Ship); - - game -} - -fn add_city(game: &mut Game, player_index: usize, s: &str) { - game.players[player_index] - .cities - .push(City::new(player_index, Position::from_offset(s))); -} - -fn add_terrain(game: &mut Game, pos: &str, terrain: Terrain) { - game.map.tiles.insert(Position::from_offset(pos), terrain); -} - -const EXPORT_FILE: &str = "game.json"; - -fn import() -> Game { - let file = File::open(EXPORT_FILE).expect("Failed to open export file"); - let reader = BufReader::new(file); - let data: GameData = serde_json::from_reader(reader).expect("Failed to read export file"); - Game::from_data(data) -} - -fn export(game: &Game) { - serde_json::to_writer_pretty( - File::create(EXPORT_FILE).expect("Failed to create export file"), - &game.cloned_data(), - ) - .expect("Failed to write export file"); -} diff --git a/client/src/local_client/bin/main.rs b/client/src/local_client/bin/main.rs index 1e69b492..717f6c09 100644 --- a/client/src/local_client/bin/main.rs +++ b/client/src/local_client/bin/main.rs @@ -1,10 +1,21 @@ -use client::client::Features; -use client::local_client; -use server::game::Game; +#![allow(clippy::missing_panics_doc)] + +use client::client::{init, render_and_update, Features, GameSyncRequest, GameSyncResult}; +use macroquad::miniquad::window::set_window_size; +use macroquad::prelude::next_frame; +use server::city::City; +use server::game::{Game, GameData}; +use server::leader::Leader; +use server::map::Terrain; +use server::position::Position; +use server::resource_pile::ResourcePile; +use server::unit::UnitType; +use std::fs::File; +use std::io::BufReader; #[macroquad::main("Clash")] async fn main() { - // set_fullscreen(true); + set_window_size(900, 400); let features = Features { import_export: true, @@ -15,8 +26,160 @@ async fn main() { let game = if false { Game::new(2, "a".repeat(32), true) } else { - local_client::setup_local_game() + setup_local_game() + }; + + run(game, &features).await; +} + +pub async fn run(mut game: Game, features: &Features) { + let mut state = init(features).await; + + let mut sync_result = GameSyncResult::None; + state.show_player = game.active_player(); + loop { + state.control_player = Some(game.active_player()); + + let message = render_and_update(&game, &mut state, &sync_result, features); + sync_result = GameSyncResult::None; + match message { + GameSyncRequest::None => {} + GameSyncRequest::ExecuteAction(a) => { + game.execute_action(a, game.active_player()); + state.show_player = game.active_player(); + sync_result = GameSyncResult::Update; + } + GameSyncRequest::Import => { + game = import(); + state.show_player = game.active_player(); + sync_result = GameSyncResult::Update; + } + GameSyncRequest::Export => { + export(&game); + } + }; + next_frame().await; + } +} + +#[must_use] +pub fn setup_local_game() -> Game { + let mut game = Game::new(2, "0".to_string(), false); + game.round = 6; + game.dice_roll_outcomes = vec![1, 1, 10, 10, 10, 10, 10, 10, 10, 10]; + let add_unit = |game: &mut Game, pos: &str, player_index: usize, unit_type: UnitType| { + game.recruit( + player_index, + vec![unit_type], + Position::from_offset(pos), + None, + vec![], + ); }; - local_client::run(game, &features).await; + let player_index1 = 0; + let player_index2 = 1; + game.players[player_index1].gain_resources(ResourcePile::new(50, 50, 50, 50, 50, 50, 50)); + game.players[player_index2].gain_resources(ResourcePile::new(50, 50, 50, 50, 50, 50, 50)); + add_city(&mut game, player_index1, "A1"); + add_city(&mut game, player_index1, "C2"); + add_city(&mut game, player_index2, "C1"); + add_city(&mut game, player_index2, "B2"); + + add_terrain(&mut game, "A1", Terrain::Fertile); + add_terrain(&mut game, "A2", Terrain::Water); + add_terrain( + &mut game, + "A3", + Terrain::Exhausted(Box::new(Terrain::Forest)), + ); + add_terrain(&mut game, "B1", Terrain::Mountain); + add_terrain(&mut game, "B2", Terrain::Forest); + add_terrain(&mut game, "B3", Terrain::Fertile); + add_terrain(&mut game, "C1", Terrain::Barren); + add_terrain(&mut game, "C2", Terrain::Forest); + add_terrain(&mut game, "C3", Terrain::Water); + add_terrain(&mut game, "D2", Terrain::Water); + + add_unit(&mut game, "C2", player_index1, UnitType::Infantry); + add_unit(&mut game, "C2", player_index1, UnitType::Cavalry); + add_unit(&mut game, "C2", player_index1, UnitType::Leader); + add_unit(&mut game, "C2", player_index1, UnitType::Elephant); + add_unit(&mut game, "C2", player_index1, UnitType::Settler); + add_unit(&mut game, "C2", player_index1, UnitType::Settler); + add_unit(&mut game, "C2", player_index1, UnitType::Settler); + add_unit(&mut game, "C2", player_index1, UnitType::Settler); + game.players[player_index1].active_leader = + Some(Leader::builder("Alexander", "", "", "", "").build()); + + add_unit(&mut game, "C1", player_index2, UnitType::Infantry); + add_unit(&mut game, "C1", player_index2, UnitType::Infantry); + + game.players[player_index1] + .get_city_mut(Position::from_offset("A1")) + .unwrap() + .increase_mood_state(); + game.players[player_index1] + .get_city_mut(Position::from_offset("C2")) + .unwrap() + .pieces + .academy = Some(1); + game.players[player_index1] + .get_city_mut(Position::from_offset("C2")) + .unwrap() + .pieces + .port = Some(1); + game.players[player_index1] + .get_city_mut(Position::from_offset("C2")) + .unwrap() + .port_position = Some(Position::from_offset("C3")); + game.players[player_index1] + .get_city_mut(Position::from_offset("C2")) + .unwrap() + .pieces + .wonders = vec![game.wonders_left.pop().unwrap()]; + game.players[player_index1] + .get_city_mut(Position::from_offset("C2")) + .unwrap() + .increase_mood_state(); + + game.players[player_index2] + .get_city_mut(Position::from_offset("B2")) + .unwrap() + .pieces + .port = Some(1); + game.players[player_index2] + .get_city_mut(Position::from_offset("B2")) + .unwrap() + .port_position = Some(Position::from_offset("C3")); + add_unit(&mut game, "B2", player_index2, UnitType::Ship); + + game +} + +fn add_city(game: &mut Game, player_index: usize, s: &str) { + game.players[player_index] + .cities + .push(City::new(player_index, Position::from_offset(s))); +} + +fn add_terrain(game: &mut Game, pos: &str, terrain: Terrain) { + game.map.tiles.insert(Position::from_offset(pos), terrain); +} + +const EXPORT_FILE: &str = "game.json"; + +fn import() -> Game { + let file = File::open(EXPORT_FILE).expect("Failed to open export file"); + let reader = BufReader::new(file); + let data: GameData = serde_json::from_reader(reader).expect("Failed to read export file"); + Game::from_data(data) +} + +fn export(game: &Game) { + serde_json::to_writer_pretty( + File::create(EXPORT_FILE).expect("Failed to create export file"), + &game.cloned_data(), + ) + .expect("Failed to write export file"); } diff --git a/client/src/move_ui.rs b/client/src/move_ui.rs index 9ee8916e..906edde5 100644 --- a/client/src/move_ui.rs +++ b/client/src/move_ui.rs @@ -8,6 +8,7 @@ use server::position::Position; use server::unit::{MovementAction, Unit}; use crate::client_state::{ActiveDialog, ShownPlayer, StateUpdate}; +use crate::layout_ui::ok_pos; use crate::unit_ui; use crate::unit_ui::UnitSelection; @@ -28,7 +29,7 @@ pub fn move_units_dialog(game: &Game, sel: &MoveSelection, player: &ShownPlayer) panic!("game is not in movement") }; - if ui.button(None, "End Move Units") { + if ui.button(ok_pos(player), "End Move Units") { StateUpdate::execute_with_warning( Action::Movement(MovementAction::Stop), if movement_actions_left > 0 { diff --git a/client/src/payment_ui.rs b/client/src/payment_ui.rs index d51abcf6..171b6a5b 100644 --- a/client/src/payment_ui.rs +++ b/client/src/payment_ui.rs @@ -80,6 +80,7 @@ pub trait HasPayment { pub fn payment_dialog( player: &ShownPlayer, title: &str, + info: Vec, has_payment: &T, is_valid: impl FnOnce(&T) -> bool, execute_action: impl FnOnce(&T) -> StateUpdate, @@ -90,6 +91,7 @@ pub fn payment_dialog( select_ui::count_dialog( player, title, + info, has_payment, |p| p.payment().resources.clone(), |p| resource_name(p.resource), diff --git a/client/src/player_ui.rs b/client/src/player_ui.rs index 2c6dfda1..fc176df1 100644 --- a/client/src/player_ui.rs +++ b/client/src/player_ui.rs @@ -1,6 +1,10 @@ use crate::client::Features; use crate::client_state::{ActiveDialog, ShownPlayer, State, StateUpdate, OFFSET, ZOOM}; use crate::happiness_ui::start_increase_happiness; +use crate::layout_ui::{ + bottom_left_button, bottom_right_button, right_center_button, right_center_label, + top_center_label, top_left_label, +}; use macroquad::math::vec2; use macroquad::prelude::*; use macroquad::ui::{root_ui, Ui}; @@ -11,16 +15,10 @@ use server::playing_actions::PlayingAction; use server::resource_pile::ResourcePile; pub fn show_globals(game: &Game, player: &ShownPlayer) -> StateUpdate { - let y = 5.; - - let ui = &mut root_ui(); - ui.label(vec2(10., y), &format!("Age {}", game.age)); - ui.label(vec2(45., y), &format!("Round {}", game.round)); + show_top_left(game); + show_top_center(game, player); - show_player_status(game, player, ui); - show_wonders(game, player, ui); - - let y = 50.; + let mut y = -100.; let i = game .players @@ -30,66 +28,92 @@ pub fn show_globals(game: &Game, player: &ShownPlayer) -> StateUpdate { let mut players: Vec<_> = game.players.iter().map(|p| p.index).collect(); players.rotate_left(i); - for (i, &p) in players.iter().enumerate() { + for p in players { let p = game.get_player(p); let shown = player.index == p.index; let prefix = if shown { "* " } else { "" }; - let suffix = &player_suffix(game, p); let name = p.get_name(); - let x = i as f32 * 500.; - let label = format!("{prefix}{name}{suffix}"); + let x = -200.; + let label = format!("{prefix}{name}"); if shown { - ui.label(vec2(x, y), &label); - } else if ui.button(vec2(x, y), label) { + right_center_label(vec2(x, y), &label); + } else if right_center_button(vec2(x, y), &label) { return StateUpdate::SetShownPlayer(p.index); } + y += 40.; } + StateUpdate::None } -fn player_suffix(game: &Game, player: &Player) -> String { - let actions_left = if game.current_player_index == player.index { - match &game.state { - GameState::StatusPhase(_) | GameState::Finished => "", - _ => &format!("{} actions Left", game.actions_left), - } - } else { - "" - }; +fn show_top_center(game: &Game, player: &ShownPlayer) { + let p = game.get_player(player.index); + + top_center_label(vec2(-400., 0.), &resource_ui(p, "Fd", |r| r.food)); + top_center_label(vec2(-320., 0.), &resource_ui(p, "Wd", |r| r.wood)); + top_center_label(vec2(-240., 0.), &resource_ui(p, "Ore", |r| r.ore)); + top_center_label(vec2(-160., 0.), &resource_ui(p, "Id", |r| r.ideas)); + top_center_label(vec2(-80., 0.), &resource_ui(p, "Gld", |r| r.gold as u32)); + top_center_label(vec2(0., 0.), &resource_ui(p, "Md", |r| r.mood_tokens)); + top_center_label(vec2(80., 0.), &resource_ui(p, "Cul", |r| r.culture_tokens)); + + top_center_label(vec2(170., 0.), &format!("Civ {}", p.civilization.name)); + top_center_label(vec2(250., 0.), &format!("VP {}", p.victory_points())); + top_center_label( + vec2(300., 0.), + &format!( + "Ldr {}", + if let Some(l) = &p.active_leader { + &l.name + } else { + "-" + } + ), + ); + show_wonders(game, player, &mut root_ui()); +} - let moves_left = if game.current_player_index == player.index { - match moves_left(&game.state) { - None => "", - Some(m) => &format!("{m} moves left"), - } - } else { - "" +fn show_top_left(game: &Game) { + let mut y = 0.; + let mut label = |label: String| { + top_left_label(vec2(0., y), &label); + y += 30.; }; - let active_player = if player.index == game.active_player() { - match &game.state { - GameState::Playing => "Play Actions", - GameState::StatusPhase(ref p) => &format!("Status Phase: {p:?}"), - GameState::Movement { .. } => "Movement", - GameState::CulturalInfluenceResolution(_) => "Cultural Influence Resolution", - GameState::Combat(c) => &format!("Combat Round {} Phase {:?}", c.round, c.phase), - GameState::PlaceSettler { .. } => "Place Settler", - GameState::Finished => "Finished", + match &game.state { + GameState::Finished => label("Finished".to_string()), + _ => label(format!("Age {}", game.age)), + } + match &game.state { + GameState::StatusPhase(ref p) => label(format!("Status Phase: {p:?}")), + _ => label(format!("Round {}", game.round)), + } + + let current = game.current_player_index; + label(format!("Playing {}", &game.get_player(current).get_name())); + + match &game.state { + GameState::StatusPhase(_) | GameState::Finished => {} + _ => label(format!("Actions {}", game.actions_left)), + } + + match &game.state { + GameState::Movement { .. } => label("Movement".to_string()), + GameState::CulturalInfluenceResolution(_) => { + label("Cultural Influence Resolution".to_string()); } - } else { - "" - }; + GameState::Combat(c) => label(format!("Combat Round {} Phase {:?}", c.round, c.phase)), + GameState::PlaceSettler { .. } => label("Place Settler".to_string()), + _ => {} + } - let status = vec![active_player, actions_left, moves_left] - .into_iter() - .filter(|s| !s.is_empty()) - .collect::>() - .join(", "); + if let Some(m) = moves_left(&game.state) { + label(format!("Moves left {m}")); + } - if status.is_empty() { - String::new() - } else { - format!(" ({status})") + let active = game.active_player(); + if active != current { + label(format!("Active {}", game.get_player(active).get_name())); } } @@ -110,6 +134,7 @@ fn moves_left(state: &GameState) -> Option { } pub fn show_wonders(game: &Game, player: &ShownPlayer, ui: &mut Ui) { + //todo move to cards ui let player = game.get_player(player.index); let y = 5.; for (i, name) in player.wonders.iter().enumerate() { @@ -130,34 +155,6 @@ pub fn show_wonders(game: &Game, player: &ShownPlayer, ui: &mut Ui) { } } -pub fn show_player_status(game: &Game, player: &ShownPlayer, ui: &mut Ui) { - let player = game.get_player(player.index); - let mut i: f32 = 0.; - let mut res = |label: String| { - ui.label(vec2(110. + i, 5.), &label); - i += 70.; - }; - - res(resource_ui(player, "Food", |r| r.food)); - res(resource_ui(player, "Wood", |r| r.wood)); - res(resource_ui(player, "Ore", |r| r.ore)); - res(resource_ui(player, "Ideas", |r| r.ideas)); - res(resource_ui(player, "Gold", |r| r.gold as u32)); - res(resource_ui(player, "Mood", |r| r.mood_tokens)); - res(resource_ui(player, "Culture", |r| r.culture_tokens)); - - res(format!("Civ {}", player.civilization.name)); - res(format!("VP {}", player.victory_points())); - res(format!( - "Leader {}", - if let Some(l) = &player.active_leader { - &l.name - } else { - "-" - } - )); -} - fn resource_ui(player: &Player, name: &str, f: impl Fn(&ResourcePile) -> u32) -> String { let r: &ResourcePile = &player.resources; let l: &ResourcePile = &player.resource_limit; @@ -166,48 +163,46 @@ fn resource_ui(player: &Player, name: &str, f: impl Fn(&ResourcePile) -> u32) -> pub fn show_global_controls(game: &Game, state: &mut State, features: &Features) -> StateUpdate { let player = state.shown_player(game); - let y = 30.; - let ui = &mut root_ui(); - if ui.button(vec2(10., y), "+") { + if bottom_left_button(vec2(0., -50.), "+") { state.zoom *= 1.1; return StateUpdate::None; } - if ui.button(vec2(25., y), "-") { + if bottom_left_button(vec2(70., -50.), "-") { state.zoom /= 1.1; return StateUpdate::None; } - if ui.button(vec2(40., y), "Reset") { + if bottom_left_button(vec2(140., -50.), "0") { state.zoom = ZOOM; state.offset = OFFSET; return StateUpdate::None; } - if ui.button(vec2(100., y), "L") { + if bottom_left_button(vec2(210., -80.), "") { state.offset += vec2(-0.1, 0.); return StateUpdate::None; } - if ui.button(vec2(120., y), "R") { + if bottom_left_button(vec2(310., -80.), "") { state.offset += vec2(0.1, 0.); return StateUpdate::None; } - if ui.button(vec2(140., y), "U") { + if bottom_left_button(vec2(260., -110.), "") { state.offset += vec2(0., 0.1); return StateUpdate::None; } - if ui.button(vec2(160., y), "D") { + if bottom_left_button(vec2(260., -50.), "") { state.offset += vec2(0., -0.1); return StateUpdate::None; } - if game.can_undo() && ui.button(vec2(180., y), "Undo") { + if game.can_undo() && bottom_right_button(vec2(-400., -50.), "Undo") { return StateUpdate::Execute(Action::Undo); } - if game.can_redo() && ui.button(vec2(220., y), "Redo") { + if game.can_redo() && bottom_right_button(vec2(-300., -50.), "Redo") { return StateUpdate::Execute(Action::Redo); } if player.can_control && matches!(game.state, GameState::Playing) - && ui.button(vec2(270., y), "End Turn") + && bottom_right_button(vec2(-180., -50.), "End Turn") { let left = game.actions_left; return StateUpdate::execute_with_warning( @@ -220,31 +215,31 @@ pub fn show_global_controls(game: &Game, state: &mut State, features: &Features) ); } - if player.can_play_action && ui.button(vec2(340., y), "Move") { + if player.can_play_action && bottom_left_button(vec2(0., -170.), "Move") { return StateUpdate::execute(Action::Playing(PlayingAction::MoveUnits)); } - if player.can_play_action && ui.button(vec2(390., y), "Happiness") { + if player.can_play_action && bottom_left_button(vec2(0., -140.), "Inc. Hap.") { return start_increase_happiness(game, &player); } - if ui.button(vec2(490., y), "Advances") { + if bottom_left_button(vec2(0., -110.), "Advances") { return StateUpdate::OpenDialog(ActiveDialog::AdvanceMenu); }; - if ui.button(vec2(560., y), "Log") { + if bottom_left_button(vec2(0., -80.), "Log") { return StateUpdate::OpenDialog(ActiveDialog::Log); }; let d = state.game_state_dialog(game, &ActiveDialog::None); if !matches!(d, ActiveDialog::None) && d.title() != state.active_dialog.title() - && ui.button(vec2(600., y), format!("Back to {}", d.title())) + && bottom_left_button(vec2(0., -200.), &format!("Back to {}", d.title())) { return StateUpdate::OpenDialog(d); } if features.import_export { - if ui.button(vec2(1000., y), "Import") { + if bottom_right_button(vec2(-300., -100.), "Import") { return StateUpdate::Import; }; - if ui.button(vec2(1100., y), "Export") { + if bottom_right_button(vec2(-150., -100.), "Export") { return StateUpdate::Export; }; } diff --git a/client/src/recruit_unit_ui.rs b/client/src/recruit_unit_ui.rs index 28780976..599ef398 100644 --- a/client/src/recruit_unit_ui.rs +++ b/client/src/recruit_unit_ui.rs @@ -214,6 +214,7 @@ pub fn select_dialog(game: &Game, a: &RecruitAmount, player: &ShownPlayer) -> St select_ui::count_dialog( player, "Recruit units", + vec![], a, |s| s.selectable.clone(), |s| s.name.as_ref(), diff --git a/client/src/select_ui.rs b/client/src/select_ui.rs index 7209c0c3..6c34099f 100644 --- a/client/src/select_ui.rs +++ b/client/src/select_ui.rs @@ -1,5 +1,6 @@ use crate::client_state::{ShownPlayer, StateUpdate, StateUpdates}; use crate::dialog_ui::active_dialog_window; +use crate::layout_ui::{cancel_pos, ok_pos}; use macroquad::hash; use macroquad::math::{bool, Vec2}; use macroquad::ui::widgets::Group; @@ -22,6 +23,7 @@ pub trait HasCountSelectableObject { pub fn count_dialog( player: &ShownPlayer, title: &str, + info: Vec, container: &C, get_objects: impl Fn(&C) -> Vec, label: impl Fn(&O) -> &str, @@ -32,16 +34,19 @@ pub fn count_dialog( minus: impl Fn(&C, &O) -> StateUpdate, ) -> StateUpdate { active_dialog_window(player, title, |ui| { + for i in info { + ui.label(None, &i); + } let mut updates = StateUpdates::new(); for (i, p) in get_objects(container).iter().enumerate() { if show(container, p) { - Group::new(hash!("res", i), Vec2::new(100., 40.)).ui(ui, |ui| { + Group::new(hash!("res", i), Vec2::new(120., 150.)).ui(ui, |ui| { let c = p.counter(); ui.label(Vec2::new(0., 0.), &format!("{} {}", &label(p), c.current)); - if c.current > c.min && ui.button(Vec2::new(0., 20.), "-") { + if c.current > c.min && ui.button(Vec2::new(0., 80.), "-") { updates.add(minus(container, p)); } - if c.current < c.max && ui.button(Vec2::new(20., 20.), "+") { + if c.current < c.max && ui.button(Vec2::new(0., 40.), "+") { updates.add(plus(container, p)); }; }); @@ -50,10 +55,10 @@ pub fn count_dialog( let valid = is_valid(container); let label = if valid { "OK" } else { "(OK)" }; - if ui.button(Vec2::new(20., 160.), label) && valid { + if ui.button(ok_pos(player), label) && valid { return execute_action(container); }; - if ui.button(Vec2::new(80., 160.), "Cancel") { + if ui.button(cancel_pos(player), "Cancel") { return StateUpdate::Cancel; }; diff --git a/client/src/status_phase_ui.rs b/client/src/status_phase_ui.rs index 68ae2774..28add2a5 100644 --- a/client/src/status_phase_ui.rs +++ b/client/src/status_phase_ui.rs @@ -2,8 +2,10 @@ use crate::client_state::{ActiveDialog, ShownPlayer, StateUpdate}; use crate::dialog_ui::active_dialog_window; use crate::select_ui; use crate::select_ui::{ConfirmSelection, Selection, SelectionConfirm}; +use server::action::Action; use server::content::advances; use server::game::Game; +use server::position::Position; use server::status_phase::{ChangeGovernmentType, StatusPhaseAction}; pub fn determine_first_player_dialog(game: &Game, player: &ShownPlayer) -> StateUpdate { @@ -26,6 +28,17 @@ pub fn determine_first_player_dialog(game: &Game, player: &ShownPlayer) -> State ) } +pub fn raze_city_confirm_dialog(game: &Game, player: &ShownPlayer, pos: Position) -> StateUpdate { + if player.get(game).can_raze_city(pos) { + StateUpdate::execute_with_confirm( + vec![format!("Raze {pos} to get 1 gold")], + Action::StatusPhase(StatusPhaseAction::RaseSize1City(Some(pos))), + ) + } else { + StateUpdate::None + } +} + pub fn raze_city_dialog(player: &ShownPlayer) -> StateUpdate { active_dialog_window(player, "Select a city to raze - or decline.", |ui| { if ui.button(None, "Decline") { diff --git a/client/src/unit_ui.rs b/client/src/unit_ui.rs index 9b972abe..26e69f10 100644 --- a/client/src/unit_ui.rs +++ b/client/src/unit_ui.rs @@ -1,5 +1,5 @@ use macroquad::color::BLACK; -use macroquad::math::u32; +use macroquad::math::{u32, vec2}; use macroquad::prelude::draw_text; use macroquad::shapes::draw_circle; use macroquad::ui::Ui; @@ -78,7 +78,7 @@ pub fn unit_selection_dialog( ) -> StateUpdate { active_dialog_window(player, title, |ui| { if let Some(current_tile) = sel.current_tile() { - for (p, unit_id) in units_on_tile(game, current_tile) { + for (i, (p, unit_id)) in units_on_tile(game, current_tile).enumerate() { let unit = game.get_player(p).get_unit(unit_id).unwrap(); let can_sel = sel.can_select(game, unit); let is_selected = sel.selected_units().contains(&unit_id); @@ -87,9 +87,10 @@ pub fn unit_selection_dialog( l += " (selected)"; } + let pos = vec2(((i / 4) as f32) * 200., i.rem_euclid(4) as f32 * 35.); if !can_sel { - ui.label(None, &l); - } else if ui.button(None, l) { + ui.label(pos, &l); + } else if ui.button(pos, l) { let mut new = sel.clone(); if is_selected { new.selected_units_mut().retain(|u| u != &unit_id); diff --git a/scripts/publish-client.sh b/scripts/publish-client.sh index 28f30c37..023e26a6 100755 --- a/scripts/publish-client.sh +++ b/scripts/publish-client.sh @@ -9,7 +9,7 @@ if [ -z "$VERSION" ]; then fi echo "Building client..." -./scripts/build-remote-client.sh # --release +./scripts/build-remote-client.sh --release echo "Publishing client..." pushd client/js/dist diff --git a/server/src/content/advances.rs b/server/src/content/advances.rs index 8ee40af9..8c80a996 100644 --- a/server/src/content/advances.rs +++ b/server/src/content/advances.rs @@ -33,7 +33,7 @@ pub fn get_all() -> Vec { Advance::builder( "Irrigation", - "✦ Your cities may Collect food from Barren spaces\n✦ Ignore Famine events", + "Your cities may Collect food from Barren spaces, Ignore Famine events", ) .add_collect_option(Barren, ResourcePile::food(1)) .with_advance_bonus(MoodToken) @@ -48,7 +48,7 @@ pub fn get_all() -> Vec { Advance::builder( "Engineering", - "✦ Immediately draw 1 wonder\n✦ May Construct wonder happy cities", + "Immediately draw 1 wonder, May Construct wonder happy cities", ) .add_one_time_ability_initializer(Game::draw_wonder_card) .add_custom_action(ConstructWonder) @@ -66,7 +66,7 @@ pub fn get_all() -> Vec { Advance::builder( "Philosophy", - "✦ Immediately gain 1 idea\n✦ Gain 1 idea after getting a Science advance", + "Immediately gain 1 idea, Gain 1 idea after getting a Science advance", ) .add_one_time_ability_initializer(|game, player_index| { game.players[player_index].gain_resources(ResourcePile::ideas(1)); @@ -107,7 +107,7 @@ pub fn get_all() -> Vec { Advance::builder( "Tactics", - "✦ May Move Army units\n✦ May use Tactics on Action Cards", + "May Move Army units, May use Tactics on Action Cards", ) .with_advance_bonus(CultureToken) .with_unlocked_building("Fortress") @@ -150,7 +150,7 @@ pub fn get_all() -> Vec { .build(), Advance::builder( - "Democracy Advance", + "Voting", "TestGovernment1", ) .leading_government_advance("Democracy") @@ -159,11 +159,11 @@ pub fn get_all() -> Vec { "Democracy 2", "TestGovernment1", ) - .with_required_advance("Democracy Advance") + .with_required_advance("Voting") .build(), Advance::builder( - "Theocracy Advance", + "Dogma", "TestGovernment2", ) .leading_government_advance("Theocracy") @@ -172,7 +172,7 @@ pub fn get_all() -> Vec { "Theocracy 2", "TestGovernment2", ) - .with_required_advance("Theocracy Advance") + .with_required_advance("Dogma") .build(), ] } diff --git a/server/src/player.rs b/server/src/player.rs index 6d48badc..596ce417 100644 --- a/server/src/player.rs +++ b/server/src/player.rs @@ -566,8 +566,10 @@ impl Player { #[must_use] pub fn can_raze_city(&self, city_position: Position) -> bool { - self.get_city(city_position) - .is_some_and(|city| city.size() == 1) + self.cities.len() > 1 + && self + .get_city(city_position) + .is_some_and(|city| city.size() == 1) } /// diff --git a/server/tests/test_games/change_government.json b/server/tests/test_games/change_government.json index 6ee3be59..963a37e6 100644 --- a/server/tests/test_games/change_government.json +++ b/server/tests/test_games/change_government.json @@ -124,7 +124,7 @@ "active_leader": null, "available_leaders": [], "advances": [ - "Democracy Advance", + "Voting", "Democracy 2", "Farming", "Fishing", @@ -412,7 +412,7 @@ { "Playing": { "Advance": { - "advance": "Democracy Advance", + "advance": "Voting", "payment": { "food": 0, "wood": 0, @@ -485,7 +485,7 @@ "The game has started", "Age 1 has started", "Round 1/3", - "Player1 payed 2 ideas to get the Democracy Advance advance", + "Player1 payed 2 ideas to get the Voting advance", "Player1 payed 2 ideas to get the Democracy 2 advance", "Player1 ended his turn with 1 actions left", "It's Player2's turn", diff --git a/server/tests/test_games/change_government.outcome.json b/server/tests/test_games/change_government.outcome.json index 0618109b..70c986c9 100644 --- a/server/tests/test_games/change_government.outcome.json +++ b/server/tests/test_games/change_government.outcome.json @@ -124,11 +124,11 @@ "active_leader": null, "available_leaders": [], "advances": [ + "Dogma", "Farming", "Fishing", "Mining", - "Theocracy 2", - "Theocracy Advance" + "Theocracy 2" ], "unlocked_special_advance": [], "wonders": [], @@ -412,7 +412,7 @@ { "Playing": { "Advance": { - "advance": "Democracy Advance", + "advance": "Voting", "payment": { "food": 0, "wood": 0, @@ -495,7 +495,7 @@ "The game has started", "Age 1 has started", "Round 1/3", - "Player1 payed 2 ideas to get the Democracy Advance advance", + "Player1 payed 2 ideas to get the Voting advance", "Player1 payed 2 ideas to get the Democracy 2 advance", "Player1 ended his turn with 1 actions left", "It's Player2's turn",