Skip to content

Commit 1be6ff0

Browse files
authored
action cards (#183)
1 parent 1012669 commit 1be6ff0

File tree

167 files changed

+4633
-952
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

167 files changed

+4633
-952
lines changed

client/src/advance_ui.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ pub fn show_advance_menu(
108108
// tooltip should be shown on top of everything
109109
show_tooltip_for_rect(rc, &description(p, a), rect);
110110

111-
if rc.can_control()
111+
if rc.can_control_shown_player()
112112
&& matches!(
113113
advance_state,
114114
AdvanceState::Available | AdvanceState::Removable

client/src/cards_ui.rs

+200-19
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,200 @@
1-
// pub fn show_wonders(game: &Game, player: &ShownPlayer, ui: &mut Ui) {
2-
//todo move to cards ui
3-
// let player = game.get_player(player.index);
4-
// let y = 5.;
5-
//
6-
// for (i, card) in player.wonder_cards.iter().enumerate() {
7-
// let req = match card.required_advances[..] {
8-
// [] => String::from("no advances"),
9-
// _ => card.required_advances.join(", "),
10-
// };
11-
// ui.label(
12-
// vec2(900. + i as f32 * 30.0, y),
13-
// &format!(
14-
// "Wonder Card {} cost {} requires {}",
15-
// &card.name, card.cost, req
16-
// ),
17-
// );
18-
// }
19-
// }
1+
use crate::client_state::{ActiveDialog, StateUpdate};
2+
use crate::custom_phase_ui::MultiSelection;
3+
use crate::dialog_ui::ok_button;
4+
use crate::layout_ui::{bottom_centered_text, left_mouse_button_pressed_in_rect};
5+
use crate::render_context::RenderContext;
6+
use crate::select_ui::HighlightType;
7+
use crate::tooltip::show_tooltip_for_rect;
8+
use macroquad::color::BLACK;
9+
use macroquad::math::{vec2, Rect};
10+
use macroquad::prelude::{draw_rectangle, draw_rectangle_lines, Color, GREEN, RED, YELLOW};
11+
use server::action::Action;
12+
use server::card::{hand_cards, HandCard, HandCardType};
13+
use server::content::action_cards::get_action_card;
14+
use server::content::custom_phase_actions::CurrentEventResponse;
15+
use server::content::wonders::get_wonder;
16+
use server::player::Player;
17+
use server::playing_actions::PlayingAction;
18+
use server::tactics_card::CombatRole;
19+
use server::wonder::Wonder;
20+
21+
pub struct HandCardObject {
22+
id: HandCard,
23+
name: String,
24+
description: Vec<String>,
25+
color: Color,
26+
}
27+
28+
impl HandCardObject {
29+
pub fn new(id: HandCard, color: Color, name: String, description: Vec<String>) -> Self {
30+
Self {
31+
id,
32+
name,
33+
description,
34+
color,
35+
}
36+
}
37+
}
38+
39+
#[must_use]
40+
pub fn wonder_cards(player: &Player) -> Vec<Wonder> {
41+
player.wonder_cards.iter().map(|n| get_wonder(n)).collect()
42+
}
43+
44+
const ACTION_CARD_COLOR: Color = RED;
45+
const WONDER_CARD_COLOR: Color = YELLOW;
46+
47+
pub(crate) fn show_cards(rc: &RenderContext) -> StateUpdate {
48+
let p = rc.shown_player;
49+
let cards = hand_cards(p, &HandCardType::get_all());
50+
let size = vec2(180., 30.);
51+
52+
let selection = if let ActiveDialog::HandCardsRequest(r) = &rc.state.active_dialog {
53+
Some(r)
54+
} else {
55+
None
56+
};
57+
58+
for pass in 0..2 {
59+
let mut y = (cards.len() as f32 * -size.y) / 2.;
60+
for card in &cards {
61+
let screen = rc.state.screen_size;
62+
let pos = vec2(screen.x, screen.y / 2.0) + vec2(-size.x, y);
63+
64+
let c = get_card_object(card);
65+
66+
if pass == 0 {
67+
draw_rectangle(pos.x, pos.y, size.x, size.y, c.color);
68+
let (thickness, border) = highlight(&c, selection);
69+
draw_rectangle_lines(pos.x, pos.y, size.x, size.y, thickness, border);
70+
71+
rc.state.draw_text(&c.name, pos.x + 10., pos.y + 22.);
72+
} else {
73+
let rect = Rect::new(pos.x, pos.y, size.x, size.y);
74+
75+
// tooltip should be shown on top of everything
76+
show_tooltip_for_rect(rc, &c.description, rect);
77+
78+
if left_mouse_button_pressed_in_rect(rect, rc) {
79+
if let Some(s) = selection {
80+
return StateUpdate::OpenDialog(ActiveDialog::HandCardsRequest(
81+
s.clone().toggle(c.id),
82+
));
83+
}
84+
if can_play_card(rc, card) {
85+
return play_card(card);
86+
}
87+
}
88+
}
89+
90+
y += size.y;
91+
}
92+
}
93+
StateUpdate::None
94+
}
95+
96+
fn can_play_card(rc: &RenderContext, card: &HandCard) -> bool {
97+
if rc.can_control_shown_player() && rc.is_playing() {
98+
if let HandCard::ActionCard(id) = card {
99+
return (get_action_card(*id).civil_card.can_play)(rc.game, rc.shown_player);
100+
}
101+
}
102+
false
103+
}
104+
105+
fn play_card(card: &HandCard) -> StateUpdate {
106+
match card {
107+
HandCard::ActionCard(a) => StateUpdate::execute_with_warning(
108+
Action::Playing(PlayingAction::ActionCard(*a)),
109+
vec![],
110+
),
111+
HandCard::Wonder(_) => panic!("wonders are played in the construct menu"),
112+
}
113+
}
114+
115+
fn highlight(c: &HandCardObject, selection: Option<&MultiSelection<HandCard>>) -> (f32, Color) {
116+
if let Some(s) = selection {
117+
if s.selected.contains(&c.id) {
118+
return (8.0, GREEN);
119+
}
120+
if s.request.choices.contains(&c.id) {
121+
return (8.0, HighlightType::Choices.color());
122+
}
123+
}
124+
(2.0, BLACK)
125+
}
126+
127+
fn get_card_object(card: &HandCard) -> HandCardObject {
128+
match card {
129+
HandCard::ActionCard(a) if *a == 0 => HandCardObject::new(
130+
card.clone(),
131+
ACTION_CARD_COLOR,
132+
"Action Card".to_string(),
133+
vec!["Hidden Action Card".to_string()],
134+
),
135+
HandCard::ActionCard(id) => {
136+
let a = get_action_card(*id);
137+
HandCardObject::new(
138+
card.clone(),
139+
ACTION_CARD_COLOR,
140+
a.civil_card.name.clone(),
141+
vec![
142+
a.civil_card.description.clone(),
143+
format!("Tactics: {}", a.tactics_card.name),
144+
format!("Unit Type: {:?}", a.tactics_card.fighter_requirement),
145+
format!(
146+
"Role: {:?}",
147+
match a.tactics_card.role_requirement {
148+
None => "None".to_string(),
149+
Some(r) => match r {
150+
CombatRole::Attacker => "Attacker".to_string(),
151+
CombatRole::Defender => "Defender".to_string(),
152+
},
153+
}
154+
),
155+
a.tactics_card.description.clone(),
156+
],
157+
)
158+
}
159+
HandCard::Wonder(n) if n.is_empty() => HandCardObject::new(
160+
card.clone(),
161+
WONDER_CARD_COLOR,
162+
"Wonder Card".to_string(),
163+
vec!["Hidden Wonder Card".to_string()],
164+
),
165+
HandCard::Wonder(name) => {
166+
let w = get_wonder(name);
167+
HandCardObject::new(
168+
card.clone(),
169+
WONDER_CARD_COLOR,
170+
w.name.clone(),
171+
vec![
172+
w.description.clone(),
173+
format!("Cost: {}", w.cost.to_string()),
174+
format!("Required advances: {}", w.required_advances.join(", ")),
175+
],
176+
)
177+
}
178+
}
179+
}
180+
181+
pub fn select_cards_dialog(rc: &RenderContext, s: &MultiSelection<HandCard>) -> StateUpdate {
182+
bottom_centered_text(
183+
rc,
184+
format!(
185+
"{}: {} cards selected",
186+
s.request.description,
187+
s.selected.len()
188+
)
189+
.as_str(),
190+
);
191+
192+
if ok_button(
193+
rc,
194+
crate::custom_phase_ui::multi_select_tooltip(s, s.request.is_valid(&s.selected), "cards"),
195+
) {
196+
StateUpdate::response(CurrentEventResponse::SelectHandCards(s.selected.clone()))
197+
} else {
198+
StateUpdate::None
199+
}
200+
}

client/src/city_ui.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use crate::action_buttons::{
22
base_or_custom_action, base_or_custom_available, custom_action_buttons,
33
};
4+
use crate::cards_ui::wonder_cards;
45
use crate::client_state::{ActiveDialog, StateUpdate};
56
use crate::collect_ui::CollectResources;
67
use crate::construct_ui::{new_building_positions, ConstructionPayment, ConstructionProject};
@@ -84,8 +85,7 @@ fn wonder_icons<'a>(rc: &'a RenderContext, city: &'a City) -> IconActionVec<'a>
8485
let owner = rc.shown_player;
8586
let game = rc.game;
8687

87-
owner
88-
.wonder_cards()
88+
wonder_cards(owner)
8989
.into_iter()
9090
.filter(|w| city.can_build_wonder(w, owner, game))
9191
.map(|w| {

client/src/client.rs

+8-5
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use server::game::Game;
77
use server::position::Position;
88

99
use crate::advance_ui::{pay_advance_dialog, show_paid_advance_menu};
10+
use crate::cards_ui::show_cards;
1011
use crate::client_state::{
1112
ActiveDialog, CameraMode, DialogChooser, State, StateUpdate, StateUpdates,
1213
};
@@ -23,8 +24,8 @@ use crate::player_ui::{player_select, show_global_controls, show_top_center, sho
2324
use crate::render_context::RenderContext;
2425
use crate::unit_ui::unit_selection_click;
2526
use crate::{
26-
custom_actions_ui, custom_phase_ui, dialog_ui, influence_ui, map_ui, move_ui, recruit_unit_ui,
27-
status_phase_ui, tooltip,
27+
cards_ui, custom_actions_ui, custom_phase_ui, dialog_ui, influence_ui, map_ui, move_ui,
28+
recruit_unit_ui, status_phase_ui, tooltip,
2829
};
2930

3031
fn render_with_mutable_state(game: &Game, state: &mut State, features: &Features) -> StateUpdate {
@@ -62,6 +63,7 @@ fn render(rc: &RenderContext, features: &Features) -> StateUpdate {
6263
show_top_center(rc);
6364
}
6465
if !state.active_dialog.is_modal() {
66+
updates.add(show_cards(rc));
6567
updates.add(player_select(rc));
6668
updates.add(show_global_controls(rc, features));
6769
}
@@ -79,7 +81,7 @@ fn render(rc: &RenderContext, features: &Features) -> StateUpdate {
7981
return StateUpdate::OpenDialog(ActiveDialog::AdvanceMenu);
8082
};
8183

82-
let can_control = rc.can_control();
84+
let can_control = rc.can_control_shown_player();
8385
if can_control {
8486
if let Some(u) = &state.pending_update {
8587
updates.add(dialog_ui::show_pending_update(u, rc));
@@ -166,6 +168,7 @@ fn render_active_dialog(rc: &RenderContext) -> StateUpdate {
166168
ActiveDialog::StructuresRequest(r) => custom_phase_ui::select_structures_dialog(rc, r),
167169
ActiveDialog::BoolRequest(d) => custom_phase_ui::bool_request_dialog(rc, d),
168170
ActiveDialog::PositionRequest(r) => custom_phase_ui::position_request_dialog(rc, r),
171+
ActiveDialog::HandCardsRequest(r) => cards_ui::select_cards_dialog(rc, r),
169172
}
170173
}
171174

@@ -185,7 +188,7 @@ pub fn try_click(rc: &RenderContext) -> StateUpdate {
185188
let mouse_pos = rc.mouse_pos();
186189
let pos = Position::from_coordinate(pixel_to_coordinate(mouse_pos));
187190

188-
if rc.can_control() {
191+
if rc.can_control_shown_player() {
189192
if let ActiveDialog::CulturalInfluence(b) = &rc.state.active_dialog {
190193
return influence_ui::hover(rc, mouse_pos, b);
191194
}
@@ -199,7 +202,7 @@ pub fn try_click(rc: &RenderContext) -> StateUpdate {
199202
return StateUpdate::None;
200203
}
201204

202-
if rc.can_control() {
205+
if rc.can_control_shown_player() {
203206
let update = controlling_player_click(rc, mouse_pos, pos);
204207
if !matches!(update, StateUpdate::None) {
205208
return update;

client/src/client_state.rs

+9-1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ use crate::render_context::RenderContext;
1515
use crate::status_phase_ui::ChooseAdditionalAdvances;
1616
use macroquad::prelude::*;
1717
use server::action::Action;
18+
use server::card::HandCard;
1819
use server::city::{City, MoodState};
1920
use server::content::custom_phase_actions::{
2021
AdvanceRequest, ChangeGovernmentRequest, CurrentEventRequest, CurrentEventResponse,
@@ -57,6 +58,7 @@ pub enum ActiveDialog {
5758
UnitTypeRequest(UnitTypeRequest),
5859
UnitsRequest(UnitsSelection),
5960
StructuresRequest(MultiSelection<SelectedStructure>),
61+
HandCardsRequest(MultiSelection<HandCard>),
6062
BoolRequest(String),
6163
ChangeGovernmentType(ChangeGovernmentRequest),
6264
ChooseAdditionalAdvances(ChooseAdditionalAdvances),
@@ -95,6 +97,7 @@ impl ActiveDialog {
9597
ActiveDialog::UnitsRequest(_) => "custom phase units request",
9698
ActiveDialog::StructuresRequest(_) => "custom phase structures request",
9799
ActiveDialog::BoolRequest(_) => "custom phase bool request",
100+
ActiveDialog::HandCardsRequest(_) => "custom phase hand cards request",
98101
}
99102
}
100103

@@ -163,6 +166,9 @@ impl ActiveDialog {
163166
custom_phase_event_help(rc, &r.request.description)
164167
}
165168
ActiveDialog::PositionRequest(r) => custom_phase_event_help(rc, &r.request.description),
169+
ActiveDialog::HandCardsRequest(r) => {
170+
custom_phase_event_help(rc, &r.request.description)
171+
}
166172
ActiveDialog::PlayerRequest(r) => custom_phase_event_help(rc, &r.description),
167173
}
168174
}
@@ -550,7 +556,9 @@ impl State {
550556
panic!("ExploreResolution expected");
551557
}
552558
}
553-
CurrentEventRequest::SelectHandCards(_) => todo!(),
559+
CurrentEventRequest::SelectHandCards(r) => {
560+
ActiveDialog::HandCardsRequest(MultiSelection::new(r.clone()))
561+
}
554562
};
555563
}
556564
match &game.state {

client/src/construct_ui.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use crate::cards_ui::wonder_cards;
12
use crate::client_state::{ActiveDialog, StateUpdate};
23
use crate::payment_ui::{payment_dialog, Payment};
34
use crate::recruit_unit_ui::RecruitSelection;
@@ -106,7 +107,7 @@ impl ConstructionPayment {
106107
let cost = match &project {
107108
ConstructionProject::Building(b, _) => p.construct_cost(*b, city, None),
108109
ConstructionProject::Wonder(name) => p.wonder_cost(
109-
p.wonder_cards().iter().find(|w| w.name == *name).unwrap(),
110+
wonder_cards(p).iter().find(|w| w.name == *name).unwrap(),
110111
city,
111112
None,
112113
),

client/src/custom_phase_ui.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,11 @@ pub fn select_structures_dialog(
204204
}
205205
}
206206

207-
fn multi_select_tooltip<T: Clone>(s: &MultiSelection<T>, valid: bool, name: &str) -> OkTooltip {
207+
pub(crate) fn multi_select_tooltip<T: Clone>(
208+
s: &MultiSelection<T>,
209+
valid: bool,
210+
name: &str,
211+
) -> OkTooltip {
208212
if valid {
209213
OkTooltip::Valid(format!("Select {name}"))
210214
} else {

client/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
clippy::cast_possible_truncation,
88
clippy::missing_panics_doc
99
)]
10+
extern crate core;
1011

1112
mod action_buttons;
1213
mod advance_ui;

0 commit comments

Comments
 (0)