Skip to content

Commit 8494f5d

Browse files
authored
fix Collect (#139)
1 parent 3291562 commit 8494f5d

File tree

94 files changed

+286
-4792
lines changed

Some content is hidden

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

94 files changed

+286
-4792
lines changed

client/src/action_buttons.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -147,5 +147,7 @@ pub fn base_or_custom_action(
147147
base.clone(),
148148
)
149149
});
150-
special.unwrap_or(StateUpdate::OpenDialog(base.unwrap()))
150+
special
151+
.or_else(|| base.map(StateUpdate::OpenDialog))
152+
.unwrap_or(StateUpdate::None)
151153
}

client/src/city_ui.rs

+9-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::action_buttons::{base_or_custom_action, base_or_custom_available};
22
use crate::client_state::{ActiveDialog, StateUpdate};
3-
use crate::collect_ui::{possible_resource_collections, CollectResources};
3+
use crate::collect_ui::CollectResources;
44
use crate::construct_ui::{new_building_positions, ConstructionPayment, ConstructionProject};
55
use crate::happiness_ui::{
66
add_increase_happiness, can_play_increase_happiness, open_increase_happiness_dialog,
@@ -14,11 +14,13 @@ use crate::{hex_ui, player_ui};
1414
use macroquad::prelude::*;
1515
use server::city::{City, MoodState};
1616
use server::city_pieces::Building;
17+
use server::collect::possible_resource_collections;
1718
use server::content::custom_actions::CustomActionType;
1819
use server::game::Game;
1920
use server::playing_actions::PlayingActionType;
2021
use server::resource::ResourceType;
2122
use server::unit::{UnitType, Units};
23+
use std::collections::HashMap;
2224
use std::ops::Add;
2325

2426
pub type IconAction<'a> = (&'a Texture2D, String, Box<dyn Fn() -> StateUpdate + 'a>);
@@ -182,7 +184,12 @@ fn collect_resources_button<'a>(rc: &'a RenderContext, city: &'a City) -> Option
182184
ActiveDialog::CollectResources(CollectResources::new(
183185
city.player_index,
184186
city.position,
185-
possible_resource_collections(rc.game, city.position, city.player_index),
187+
possible_resource_collections(
188+
rc.game,
189+
city.position,
190+
city.player_index,
191+
&HashMap::new(),
192+
),
186193
custom,
187194
))
188195
},

client/src/collect_ui.rs

+13-40
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
use std::collections::HashMap;
2-
use std::iter;
32

43
use crate::client_state::{ActiveDialog, StateUpdate};
54
use crate::dialog_ui::{
@@ -17,10 +16,10 @@ use macroquad::math::vec2;
1716
use macroquad::prelude::WHITE;
1817
use macroquad::shapes::draw_circle;
1918
use server::action::Action;
20-
use server::consts::PORT_CHOICES;
19+
use server::collect::{get_total_collection, possible_resource_collections};
2120
use server::content::custom_actions::CustomAction;
2221
use server::game::Game;
23-
use server::playing_actions::{get_total_collection, Collect, PlayingAction};
22+
use server::playing_actions::{Collect, PlayingAction};
2423
use server::position::Position;
2524
use server::resource::ResourceType;
2625
use server::resource_pile::ResourcePile;
@@ -121,38 +120,12 @@ pub fn collect_dialog(rc: &RenderContext, collect: &CollectResources) -> StateUp
121120
StateUpdate::None
122121
}
123122

124-
pub fn possible_resource_collections(
125-
game: &Game,
126-
city_pos: Position,
127-
player_index: usize,
128-
) -> HashMap<Position, Vec<ResourcePile>> {
129-
let collect_options = &game.get_player(player_index).collect_options;
130-
let city = game.get_city(player_index, city_pos);
131-
city_pos
132-
.neighbors()
133-
.into_iter()
134-
.chain(iter::once(city_pos))
135-
.filter_map(|pos| {
136-
if city
137-
.port_position
138-
.is_some_and(|p| p == pos && !is_blocked(game, player_index, p))
139-
{
140-
return Some((pos, PORT_CHOICES.to_vec()));
141-
}
142-
if let Some(t) = game.map.get(pos) {
143-
if let Some(option) = collect_options
144-
.get(t)
145-
.filter(|_| pos == city_pos || !is_blocked(game, player_index, pos))
146-
{
147-
return Some((pos, option.clone()));
148-
}
149-
}
150-
None
151-
})
152-
.collect()
153-
}
154-
155-
fn click_collect_option(col: &CollectResources, p: Position, pile: &ResourcePile) -> StateUpdate {
123+
fn click_collect_option(
124+
rc: &RenderContext,
125+
col: &CollectResources,
126+
p: Position,
127+
pile: &ResourcePile,
128+
) -> StateUpdate {
156129
let mut new = col.clone();
157130
let old = col.collections.iter().find(|(pos, _)| pos == &p);
158131

@@ -161,6 +134,10 @@ fn click_collect_option(col: &CollectResources, p: Position, pile: &ResourcePile
161134
new.collections.push((p, pile.clone()));
162135
}
163136

137+
let used = col.collections.clone().into_iter().collect();
138+
new.possible_collections =
139+
possible_resource_collections(rc.game, col.city_position, col.player_index, &used);
140+
164141
StateUpdate::OpenDialog(ActiveDialog::CollectResources(new))
165142
}
166143

@@ -185,7 +162,7 @@ pub fn draw_resource_collect_tile(rc: &RenderContext, pos: Position) -> StateUpd
185162
draw_circle(center.x, center.y, 20., color);
186163
if let Some(p) = left_mouse_button_pressed(rc) {
187164
if is_in_circle(p, center, 20.) {
188-
return click_collect_option(collect, pos, pile);
165+
return click_collect_option(rc, collect, pos, pile);
189166
}
190167
}
191168

@@ -227,7 +204,3 @@ fn draw_collect_item(rc: &RenderContext, center: Point, resources: &[(ResourceTy
227204
});
228205
}
229206
}
230-
231-
fn is_blocked(game: &Game, player_index: usize, pos: Position) -> bool {
232-
game.get_any_city(pos).is_some() || game.enemy_player(player_index, pos).is_some()
233-
}

server/src/ability_initializer.rs

+2-26
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::{
2-
content::custom_actions::CustomActionType, events::EventMut, game::Game, map::Terrain,
3-
player_events::PlayerEvents, resource_pile::ResourcePile, utils,
2+
content::custom_actions::CustomActionType, events::EventMut, game::Game,
3+
player_events::PlayerEvents,
44
};
55

66
pub type AbilityInitializer = Box<dyn Fn(&mut Game, usize)>;
@@ -62,30 +62,6 @@ pub trait AbilityInitializerSetup: Sized {
6262
player.custom_actions.remove(&deinitializer_action);
6363
})
6464
}
65-
66-
fn add_collect_option(self, terrain: Terrain, option: ResourcePile) -> Self {
67-
let deinitializer_terrain = terrain.clone();
68-
let deinitializer_option = option.clone();
69-
self.add_one_time_ability_initializer(move |game, player_index| {
70-
let player = &mut game.players[player_index];
71-
player
72-
.collect_options
73-
.entry(terrain.clone())
74-
.or_default()
75-
.push(option.clone());
76-
})
77-
.add_ability_undo_deinitializer(move |game, player_index| {
78-
let player = &mut game.players[player_index];
79-
utils::remove_element(
80-
player
81-
.collect_options
82-
.get_mut(&deinitializer_terrain)
83-
.expect("player should have options for terrain type"),
84-
&deinitializer_option,
85-
);
86-
//*Note that this will break if multiple effects add the same collect option
87-
})
88-
}
8965
}
9066

9167
pub fn join_ability_initializers(setup: Vec<AbilityInitializer>) -> AbilityInitializer {

server/src/city_pieces.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use serde::{Deserialize, Serialize};
22

3+
use crate::content::advances::TACTICS;
34
use crate::{content::wonders, wonder::Wonder};
45
use Building::*;
56

@@ -225,7 +226,7 @@ impl Building {
225226
Self::Market => "Bartering",
226227
Self::Obelisk => "Arts",
227228
Self::Observatory => "Math",
228-
Self::Fortress => "Tactics",
229+
Self::Fortress => TACTICS,
229230
Self::Port => "Fishing",
230231
Self::Temple => "Myths",
231232
})

server/src/collect.rs

+116
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
use crate::game::Game;
2+
use crate::map::Terrain::{Fertile, Forest, Mountain};
3+
use crate::playing_actions::Collect;
4+
use crate::position::Position;
5+
use crate::resource_pile::ResourcePile;
6+
use std::collections::HashMap;
7+
use std::iter;
8+
use std::ops::Add;
9+
10+
///
11+
/// # Panics
12+
///
13+
/// Panics if the action is illegal
14+
#[must_use]
15+
pub fn get_total_collection(
16+
game: &Game,
17+
player_index: usize,
18+
city_position: Position,
19+
collections: &[(Position, ResourcePile)],
20+
) -> Option<ResourcePile> {
21+
let player = &game.players[player_index];
22+
let city = player.get_city(city_position)?;
23+
if city.mood_modified_size() < collections.len() || city.player_index != player_index {
24+
return None;
25+
}
26+
let choices = possible_resource_collections(game, city_position, player_index, &HashMap::new());
27+
let possible = collections.iter().all(|(position, collect)| {
28+
choices
29+
.get(position)
30+
.is_some_and(|options| options.contains(collect))
31+
});
32+
33+
if possible {
34+
collections
35+
.iter()
36+
.map(|(_, collect)| collect.clone())
37+
.reduce(|a, b| a.clone().add(b.clone()))
38+
} else {
39+
None
40+
}
41+
}
42+
43+
pub(crate) fn collect(game: &mut Game, player_index: usize, c: &Collect) {
44+
let total_collect = get_total_collection(game, player_index, c.city_position, &c.collections)
45+
.expect("Illegal action");
46+
let city = game.players[player_index]
47+
.get_city_mut(c.city_position)
48+
.expect("Illegal action");
49+
assert!(city.can_activate(), "Illegal action");
50+
city.activate();
51+
game.players[player_index].gain_resources(total_collect);
52+
}
53+
54+
pub(crate) fn undo_collect(game: &mut Game, player_index: usize, c: Collect) {
55+
game.players[player_index]
56+
.get_city_mut(c.city_position)
57+
.expect("city should be owned by the player")
58+
.undo_activate();
59+
let total_collect = c.collections.into_iter().map(|(_, collect)| collect).sum();
60+
game.players[player_index].loose_resources(total_collect);
61+
}
62+
63+
pub(crate) struct CollectContext {
64+
pub city_position: Position,
65+
#[allow(dead_code)] // will need for other advances
66+
pub used: HashMap<Position, ResourcePile>,
67+
}
68+
69+
///
70+
/// # Panics
71+
/// Panics if the action is illegal
72+
#[must_use]
73+
pub fn possible_resource_collections(
74+
game: &Game,
75+
city_pos: Position,
76+
player_index: usize,
77+
used: &HashMap<Position, ResourcePile>,
78+
) -> HashMap<Position, Vec<ResourcePile>> {
79+
let terrain_options = HashMap::from([
80+
(Mountain, vec![ResourcePile::ore(1)]),
81+
(Fertile, vec![ResourcePile::food(1)]),
82+
(Forest, vec![ResourcePile::wood(1)]),
83+
]);
84+
85+
let mut collect_options = city_pos
86+
.neighbors()
87+
.into_iter()
88+
.chain(iter::once(city_pos))
89+
.filter_map(|pos| {
90+
if let Some(t) = game.map.get(pos) {
91+
if let Some(option) = terrain_options.get(t) {
92+
return Some((pos, option.clone()));
93+
}
94+
}
95+
None
96+
})
97+
.collect();
98+
game.players[player_index]
99+
.events
100+
.as_ref()
101+
.expect("events should be set")
102+
.collect_options
103+
.trigger(
104+
&mut collect_options,
105+
&CollectContext {
106+
city_position: city_pos,
107+
used: used.clone(),
108+
},
109+
game,
110+
);
111+
collect_options.retain(|p, _| {
112+
game.get_any_city(*p).is_none_or(|c| c.position == city_pos)
113+
&& game.enemy_player(player_index, *p).is_none()
114+
});
115+
collect_options
116+
}

server/src/consts.rs

+2-30
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use crate::content::advances::TACTICS;
12
use crate::{resource_pile::ResourcePile, unit::Units};
23

34
pub const MAX_CITY_SIZE: usize = 4;
@@ -11,7 +12,7 @@ pub const CAPTURED_LEADER_VICTORY_POINTS: f32 = 2.0;
1112
pub const STACK_LIMIT: usize = 4;
1213
pub const CITY_LIMIT: u8 = 7;
1314
pub const MOVEMENT_ACTIONS: u32 = 3;
14-
pub const ARMY_MOVEMENT_REQUIRED_ADVANCE: &str = "Tactics";
15+
pub const ARMY_MOVEMENT_REQUIRED_ADVANCE: &str = TACTICS;
1516
pub const CITY_PIECE_LIMIT: usize = 5;
1617

1718
pub const UNIT_LIMIT: Units = Units {
@@ -31,32 +32,3 @@ pub const CONSTRUCT_COST: ResourcePile = ResourcePile {
3132
mood_tokens: 0,
3233
culture_tokens: 0,
3334
};
34-
pub const PORT_CHOICES: [ResourcePile; 3] = [
35-
ResourcePile {
36-
food: 1,
37-
wood: 0,
38-
ore: 0,
39-
ideas: 0,
40-
gold: 0,
41-
mood_tokens: 0,
42-
culture_tokens: 0,
43-
},
44-
ResourcePile {
45-
food: 0,
46-
wood: 0,
47-
ore: 0,
48-
ideas: 0,
49-
gold: 1,
50-
mood_tokens: 0,
51-
culture_tokens: 0,
52-
},
53-
ResourcePile {
54-
food: 0,
55-
wood: 0,
56-
ore: 0,
57-
ideas: 0,
58-
gold: 0,
59-
mood_tokens: 1,
60-
culture_tokens: 0,
61-
},
62-
];

0 commit comments

Comments
 (0)