Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

incidents & test cleanup #174

Merged
merged 1 commit into from
Mar 8, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions client/src/city_ui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ pub fn city_labels(game: &Game, city: &City) -> Vec<String> {
[
vec![format!(
"City: {}, {}, {} {}",
game.get_player(city.player_index).get_name(),
game.player_name(city.player_index),
city.size(),
match city.mood_state {
MoodState::Happy => "Happy",
Expand All @@ -234,7 +234,7 @@ pub fn city_labels(game: &Game, city: &City) -> Vec<String> {
if city.player_index == *o {
b.name().to_string()
} else {
format!("{} (owned by {})", b.name(), game.get_player(*o).get_name())
format!("{} (owned by {})", b.name(), game.player_name(*o))
}
})
})
Expand Down
162 changes: 90 additions & 72 deletions server/src/ability_initializer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ pub(crate) trait AbilityInitializerSetup: Sized {
self
}

fn add_player_event_listener<T, U, V, E, F>(self, event: E, listener: F, priority: i32) -> Self
fn add_player_event_listener<T, U, V, E, F>(self, event: E, priority: i32, listener: F) -> Self
where
T: Clone + PartialEq,
E: Fn(&mut PlayerEvents) -> &mut Event<T, U, V> + 'static + Clone,
Expand Down Expand Up @@ -190,16 +190,12 @@ pub(crate) trait AbilityInitializerSetup: Sized {
F: Fn(&mut T, &U, &V) + 'static + Clone,
{
let id = self.get_key().id();
self.add_player_event_listener(
event,
move |value, u, v| {
if !get_info(value).contains_key(&id) {
listener(value, u, v);
get_info(value).insert(id.clone(), "used".to_string());
}
},
priority,
)
self.add_player_event_listener(event, priority, move |value, u, v| {
if !get_info(value).contains_key(&id) {
listener(value, u, v);
get_info(value).insert(id.clone(), "used".to_string());
}
})
}

fn add_current_event_listener<E, V>(
Expand All @@ -217,72 +213,89 @@ pub(crate) trait AbilityInitializerSetup: Sized {
E: Fn(&mut PlayerEvents) -> &mut CurrentEvent<V> + 'static + Clone,
{
let origin = self.get_key();
self.add_player_event_listener(
event,
move |game, i, details| {
let player_index = i.player;
let player_name = game.players[player_index].get_name();

if let Some(mut phase) = game.current_events.pop() {
if let Some(ref c) = phase.player.handler {
if let Some(ref action) = c.response {
if c.priority == priority {
let mut current = phase.clone();
current
.player
.handler
.as_mut()
.expect("current missing")
.response = None;
if can_undo(&current.event_type) {
game.undo_context_stack
.push(UndoContext::Event(Box::new(current)));
}
let r = c.request.clone();
let a = action.clone();
phase.player.handler = None;
game.current_events.push(phase);
end_custom_phase.clone()(
game,
player_index,
&player_name,
a,
r,
details,
);
return;
self.add_player_event_listener(event, priority, move |game, i, details| {
let player_index = i.player;
let player_name = game.player_name(player_index);

if let Some(mut phase) = game.current_events.pop() {
if let Some(ref c) = phase.player.handler {
if let Some(ref action) = c.response {
if c.priority == priority {
let mut current = phase.clone();
current
.player
.handler
.as_mut()
.expect("current missing")
.response = None;
if can_undo(&current.event_type) {
game.undo_context_stack
.push(UndoContext::Event(Box::new(current)));
}
let r = c.request.clone();
let a = action.clone();
phase.player.handler = None;
game.current_events.push(phase);
end_custom_phase.clone()(
game,
player_index,
&player_name,
a,
r,
details,
);
return;
}
}
let is_current = phase.player.handler.is_some();
game.current_events.push(phase);
if is_current {
return;
}
}

if game
.current_event_player()
.last_priority_used
.is_some_and(|last| last < priority)
{
// already handled before
let is_current = phase.player.handler.is_some();
game.current_events.push(phase);
if is_current {
return;
}
}

if game
.current_event_player()
.last_priority_used
.is_some_and(|last| last < priority)
{
// already handled before
return;
}

if let Some(request) = start_custom_phase(game, player_index, &player_name, details) {
let s = game.current_event_mut();
s.player.last_priority_used = Some(priority);
s.player.handler = Some(CurrentEventHandler {
priority,
request: request.clone(),
response: None,
origin: origin.clone(),
});
};
})
}

if let Some(request) = start_custom_phase(game, player_index, &player_name, details)
{
let s = game.current_event_mut();
s.player.last_priority_used = Some(priority);
s.player.handler = Some(CurrentEventHandler {
priority,
request: request.clone(),
response: None,
origin: origin.clone(),
});
};
},
fn add_simple_current_event_listener<V, E, F>(
self,
event: E,
priority: i32,
listener: F,
) -> Self
where
E: Fn(&mut PlayerEvents) -> &mut CurrentEvent<V> + 'static + Clone,
F: Fn(&mut Game, usize, &str, &V) + 'static + Clone,
{
self.add_current_event_listener(
event,
priority,
move |game, player_index, player_name, details| {
// only for the listener
listener(game, player_index, player_name, details);
None
},
|_, _, _, _, _, _| {},
)
}

Expand All @@ -300,7 +313,12 @@ pub(crate) trait AbilityInitializerSetup: Sized {
event,
priority,
move |game, player_index, _player_name, details| {
request(game, player_index, details).map(CurrentEventRequest::Payment)
request(game, player_index, details)
.filter(|r| {
r.iter()
.any(|r| game.get_player(player_index).can_afford(&r.cost))
})
.map(CurrentEventRequest::Payment)
},
move |game, player_index, player_name, action, request, details| {
if let CurrentEventRequest::Payment(requests) = &request {
Expand Down Expand Up @@ -350,7 +368,7 @@ pub(crate) trait AbilityInitializerSetup: Sized {
let req = request(game, player_index, details);
if let Some(r) = &req {
if r.reward.possible_resource_types().len() == 1 {
let player_name = game.players[player_index].get_name();
let player_name = game.player_name(player_index);
let r = r.reward.default_payment();
for log in g(
game,
Expand Down Expand Up @@ -689,7 +707,7 @@ pub(crate) trait AbilityInitializerSetup: Sized {
game,
&SelectedChoice::new(
player_index,
&game.get_player(player_index).get_name(),
&game.player_name(player_index),
false,
m.choices.clone(),
details,
Expand Down
4 changes: 4 additions & 0 deletions server/src/action.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ use crate::status_phase::play_status_phase;
use crate::undo::{redo, undo, DisembarkUndoContext, UndoContext};
use crate::unit::MovementAction::{Move, Stop};
use crate::unit::{get_current_move, MovementAction};
use crate::wonder::draw_wonder_card;
use itertools::Itertools;
use serde::{Deserialize, Serialize};

Expand Down Expand Up @@ -124,6 +125,9 @@ pub(crate) fn execute_custom_phase_action(
) {
use CurrentEventType::*;
match details {
DrawWonderCard => {
draw_wonder_card(game, player_index);
}
ExploreResolution(r) => {
ask_explore_resolution(game, player_index, r);
}
Expand Down
3 changes: 1 addition & 2 deletions server/src/advance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,6 @@ impl Bonus {
///
/// Panics if advance does not exist
pub fn do_advance(game: &mut Game, advance: &Advance, player_index: usize) {
game.trigger_command_event(player_index, |e| &mut e.on_advance, &advance.name);
(advance.listeners.initializer)(game, player_index);
(advance.listeners.one_time_initializer)(game, player_index);
let name = advance.name.clone();
Expand Down Expand Up @@ -190,7 +189,7 @@ pub(crate) fn advance_with_incident_token(
pub(crate) fn gain_advance(game: &mut Game, player_index: usize, info: &AdvanceInfo) {
if game.trigger_current_event(
&[player_index],
|e| &mut e.on_advance_custom_phase,
|e| &mut e.on_advance,
info,
CurrentEventType::Advance,
None,
Expand Down
34 changes: 24 additions & 10 deletions server/src/barbarians.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,12 @@ pub(crate) fn barbarians_spawn(mut builder: IncidentBuilder) -> IncidentBuilder
IncidentTarget::ActivePlayer,
BASE_EFFECT_PRIORITY + 1,
|game, _player_index, _i| {
let r = possible_barbarians_reinforcements(game);
if r.is_empty() {
game.add_info_log_item("Barbarians cannot reinforce");
}
Some(new_position_request(
possible_barbarians_reinforcements(game),
r,
1..=1,
"Select a position for the additional Barbarian unit",
))
Expand Down Expand Up @@ -140,7 +144,9 @@ pub(crate) fn barbarians_spawn(mut builder: IncidentBuilder) -> IncidentBuilder

pub(crate) fn barbarians_move(mut builder: IncidentBuilder) -> IncidentBuilder {
builder = set_info(builder, "Barbarians move", |state, game, human| {
if !get_movable_units(game, human, state).is_empty() {
if get_movable_units(game, human, state).is_empty() {
game.add_info_log_item("Barbarians cannot move - will try to spawn a new city instead");
} else {
state.move_units = true;
}
});
Expand Down Expand Up @@ -221,10 +227,10 @@ pub(crate) fn barbarians_move(mut builder: IncidentBuilder) -> IncidentBuilder {
},
);
}
builder.add_incident_listener(
builder.add_simple_incident_listener(
IncidentTarget::ActivePlayer,
BASE_EFFECT_PRIORITY,
|game, player| {
|game, player, _| {
let s = get_barbarian_state(game);
if s.move_units && get_movable_units(game, player, &s).is_empty() {
// after all moves are done
Expand Down Expand Up @@ -301,18 +307,18 @@ fn barbarian_march_steps(
pub(crate) fn set_info(
builder: IncidentBuilder,
event_name: &str,
init: impl Fn(&mut BarbariansEventState, &Game, usize) + 'static + Clone,
init: impl Fn(&mut BarbariansEventState, &mut Game, usize) + 'static + Clone,
) -> IncidentBuilder {
let name = event_name.to_string();
builder.add_incident_listener(
builder.add_simple_incident_listener(
IncidentTarget::ActivePlayer,
BASE_EFFECT_PRIORITY + 200,
move |game, player| {
move |game, player, _| {
if game.current_event().barbarians.is_none() {
game.add_info_log_item(&format!("Base effect: {name}"));
let mut state = BarbariansEventState::new();
init(&mut state, game, player);
game.current_event_mut().barbarians = Some(state);
game.add_info_log_item(&format!("Base effect: {name}"));
}
},
)
Expand All @@ -323,8 +329,16 @@ fn add_barbarians_city(builder: IncidentBuilder) -> IncidentBuilder {
IncidentTarget::ActivePlayer,
BASE_EFFECT_PRIORITY + 100,
move |game, player_index, _i| {
(!get_barbarian_state(game).move_units).then_some(new_position_request(
possible_barbarians_spawns(game, game.get_player(player_index)),
if get_barbarian_state(game).move_units {
return None;
}

let choices = possible_barbarians_spawns(game, game.get_player(player_index));
if choices.is_empty() {
game.add_info_log_item("Barbarians cannot spawn a new city");
}
Some(new_position_request(
choices,
1..=1,
"Select a position for the new city and infantry unit",
))
Expand Down
Loading