diff --git a/server/src/action.rs b/server/src/action.rs index b901a261..419ef67f 100644 --- a/server/src/action.rs +++ b/server/src/action.rs @@ -172,7 +172,7 @@ pub(crate) fn execute_custom_phase_action( on_recruit(game, player_index, r); } Incident(i) => { - trigger_incident(game, player_index, i); + trigger_incident(game, i.clone()); } } } diff --git a/server/src/advance.rs b/server/src/advance.rs index 2124cb98..f6acb419 100644 --- a/server/src/advance.rs +++ b/server/src/advance.rs @@ -200,7 +200,7 @@ pub(crate) fn gain_advance(game: &mut Game, player_index: usize, info: &AdvanceI player.incident_tokens -= 1; if player.incident_tokens == 0 { player.incident_tokens = 3; - trigger_incident(game, player_index, &IncidentInfo::new(player_index)); + trigger_incident(game, IncidentInfo::new(player_index)); } } diff --git a/server/src/barbarians.rs b/server/src/barbarians.rs index 8d0889ad..5b6a862b 100644 --- a/server/src/barbarians.rs +++ b/server/src/barbarians.rs @@ -7,7 +7,7 @@ use crate::content::custom_phase_actions::{ new_position_request, ResourceRewardRequest, UnitTypeRequest, }; use crate::game::Game; -use crate::incident::{IncidentBuilder, BASE_EFFECT_PRIORITY}; +use crate::incident::{play_base_effect, IncidentBuilder, BASE_EFFECT_PRIORITY}; use crate::map::Terrain; use crate::payment::PaymentOptions; use crate::player::Player; @@ -230,7 +230,7 @@ pub(crate) fn barbarians_move(mut builder: IncidentBuilder) -> IncidentBuilder { 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 @@ -313,8 +313,8 @@ pub(crate) fn set_info( builder.add_simple_incident_listener( IncidentTarget::ActivePlayer, BASE_EFFECT_PRIORITY + 200, - move |game, player, _| { - if game.current_event().barbarians.is_none() { + move |game, player, _, _| { + if play_base_effect(game) && 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); @@ -417,9 +417,9 @@ fn possible_barbarians_reinforcements(game: &Game) -> Vec { fn get_barbarian_reinforcement_choices(game: &Game) -> Vec { let barbarian = get_barbarians_player(game); - let pos = get_barbarian_state(game) - .selected_position - .expect("selected position should exist"); + let Some(pos) = get_barbarian_state(game).selected_position else { + return vec![]; + }; let possible = if barbarian .get_units(pos) .iter() diff --git a/server/src/content.rs b/server/src/content.rs index 95ef5d25..f2dd1db3 100644 --- a/server/src/content.rs +++ b/server/src/content.rs @@ -22,6 +22,7 @@ mod incidents_earthquake; mod incidents_famine; mod incidents_good_year; mod incidents_population_boom; +mod incidents_trade; mod incidents_trojan; pub mod trade_routes; pub mod wonders; diff --git a/server/src/content/advances_autocracy.rs b/server/src/content/advances_autocracy.rs index 1f9c63b9..e1bf3f30 100644 --- a/server/src/content/advances_autocracy.rs +++ b/server/src/content/advances_autocracy.rs @@ -4,7 +4,6 @@ use crate::content::advances::{advance_group_builder, AdvanceGroup}; use crate::content::custom_actions::CustomActionType::{AbsolutePower, ForcedLabor}; use crate::content::custom_phase_actions::ResourceRewardRequest; use crate::payment::PaymentOptions; -use crate::resource::ResourceType; pub(crate) fn autocracy() -> AdvanceGroup { advance_group_builder( @@ -35,10 +34,7 @@ fn nationalism() -> AdvanceBuilder { .any(|u| u.is_army_unit() || u.is_ship()) { Some(ResourceRewardRequest::new( - PaymentOptions::sum( - 1, - &[ResourceType::MoodTokens, ResourceType::CultureTokens], - ), + PaymentOptions::tokens(1), "Select token to gain".to_string(), )) } else { diff --git a/server/src/content/incidects_civil_war.rs b/server/src/content/incidects_civil_war.rs index a0cdc8db..90d63931 100644 --- a/server/src/content/incidects_civil_war.rs +++ b/server/src/content/incidects_civil_war.rs @@ -8,7 +8,6 @@ use crate::payment::{PaymentConversion, PaymentConversionType, PaymentOptions}; use crate::player::Player; use crate::player_events::IncidentTarget; use crate::position::Position; -use crate::resource::ResourceType; use crate::resource_pile::ResourcePile; use crate::status_phase::{add_change_government, can_change_government_for_free}; use crate::unit::UnitType; @@ -140,7 +139,7 @@ fn revolution() -> Incident { "Kill a unit to avoid losing an action", |game, _player| can_loose_action(game), ); - b = b.add_simple_incident_listener(IncidentTarget::ActivePlayer, 2, |game, player, _| { + b = b.add_simple_incident_listener(IncidentTarget::ActivePlayer, 2, |game, player, _, _| { if can_loose_action(game) && game.current_event_player().sacrifice == 0 { loose_action(game, player); } @@ -229,7 +228,7 @@ fn uprising() -> Incident { 0, |game, player_index, _incident| { let player = game.get_player(player_index); - let mut cost = PaymentOptions::sum(4, &[ResourceType::MoodTokens, ResourceType::CultureTokens]); + let mut cost = PaymentOptions::tokens(4); cost.conversions.push(PaymentConversion::new( vec![ResourcePile::mood_tokens(1), ResourcePile::culture_tokens(1)], ResourcePile::empty(), @@ -258,7 +257,7 @@ fn envoy() -> Incident { .add_simple_incident_listener( IncidentTarget::ActivePlayer, 1, - |game, player, player_name | { + |game, player, player_name, _| { game.add_info_log_item(&format!("{player_name} gained 1 idea and 1 culture token")); game.get_player_mut(player).gain_resources( ResourcePile::culture_tokens(1) + ResourcePile::ideas(1)); @@ -274,7 +273,7 @@ fn envoy() -> Incident { ) .add_incident_player_request( "Select a player to gain 1 culture token", - |_p| true, + |_p, _| true, 0, |game, s| { let p = s.choice; diff --git a/server/src/content/incidents.rs b/server/src/content/incidents.rs index 8e499592..c8481837 100644 --- a/server/src/content/incidents.rs +++ b/server/src/content/incidents.rs @@ -4,6 +4,7 @@ use crate::content::incidents_earthquake::earthquakes; use crate::content::incidents_famine::{epidemics, famines, pestilence}; use crate::content::incidents_good_year::{awesome_years, fantastic_years, good_years}; use crate::content::incidents_population_boom::population_booms; +use crate::content::incidents_trade::trades; use crate::content::incidents_trojan::trojan_incidents; use crate::incident::Incident; use itertools::Itertools; @@ -30,6 +31,8 @@ pub(crate) fn get_all() -> Vec { civil_wars(), // 41+ trojan_incidents(), + // 45+ + trades(), // 51+ successful_year(), ] diff --git a/server/src/content/incidents_famine.rs b/server/src/content/incidents_famine.rs index c53ef89a..0f1a096d 100644 --- a/server/src/content/incidents_famine.rs +++ b/server/src/content/incidents_famine.rs @@ -32,7 +32,7 @@ pub(crate) fn pestilence() -> Vec { }); builder = pestilence_city(builder, 1); builder = - builder.add_simple_incident_listener(IncidentTarget::ActivePlayer, 0, |game, _, _| { + builder.add_simple_incident_listener(IncidentTarget::ActivePlayer, 0, |game, _, _, _| { game.permanent_incident_effects .push(PermanentIncidentEffect::Pestilence); }); diff --git a/server/src/content/incidents_good_year.rs b/server/src/content/incidents_good_year.rs index 8bf9a73e..ed6aa55c 100644 --- a/server/src/content/incidents_good_year.rs +++ b/server/src/content/incidents_good_year.rs @@ -141,7 +141,7 @@ fn good_year(mut builder: IncidentBuilder, amount: u32, good_year_type: &GoodYea builder = builder.add_incident_player_request( "Select a player to gain 1 food", - |p| p.resources.food < p.resource_limit.food, + |p, _| p.resources.food < p.resource_limit.food, i as i32, move |game, c| { game.add_info_log_item(&format!( diff --git a/server/src/content/incidents_population_boom.rs b/server/src/content/incidents_population_boom.rs index be6c683d..ecc88f9e 100644 --- a/server/src/content/incidents_population_boom.rs +++ b/server/src/content/incidents_population_boom.rs @@ -1,7 +1,6 @@ use crate::content::custom_phase_actions::new_position_request; -use crate::game::Game; use crate::incident::{Incident, IncidentBaseEffect, IncidentBuilder}; -use crate::player_events::{IncidentInfo, IncidentTarget}; +use crate::player_events::IncidentTarget; use crate::unit::UnitType; pub(crate) fn population_booms() -> Vec { @@ -13,16 +12,14 @@ pub(crate) fn population_booms() -> Vec { fn population_boom(id: u8, effect: IncidentBaseEffect) -> Incident { let mut b = Incident::builder(id, "Population Boom", "-", effect); - b = select_settler(b, 13, |_game, player, i| { - i.is_active(IncidentTarget::ActivePlayer, player) - }); + b = select_settler(b, 13, IncidentTarget::ActivePlayer); select_player_to_gain_settler(b).build() } pub(crate) fn select_player_to_gain_settler(mut b: IncidentBuilder) -> IncidentBuilder { b = b.add_incident_player_request( "Select a player to gain 1 settler", - |p| p.available_units().settlers > 0 && !p.cities.is_empty(), + |p, _| p.available_units().settlers > 0 && !p.cities.is_empty(), 12, |game, c| { game.add_info_log_item(&format!( @@ -32,22 +29,16 @@ pub(crate) fn select_player_to_gain_settler(mut b: IncidentBuilder) -> IncidentB game.current_event_mut().selected_player = Some(c.choice); }, ); - select_settler(b, 11, |game, player, _| { - game.current_event().selected_player == Some(player) - }) + select_settler(b, 11, IncidentTarget::SelectedPlayer) } -fn select_settler( - b: IncidentBuilder, - priority: i32, - pred: impl Fn(&Game, usize, &IncidentInfo) -> bool + 'static + Clone, -) -> IncidentBuilder { +fn select_settler(b: IncidentBuilder, priority: i32, target: IncidentTarget) -> IncidentBuilder { b.add_incident_position_request( - IncidentTarget::AllPlayers, + target, priority, - move |game, player_index, incident| { + move |game, player_index, _| { let p = game.get_player(player_index); - if pred(game, player_index, incident) && p.available_units().settlers > 0 { + if p.available_units().settlers > 0 { Some(new_position_request( p.cities.iter().map(|c| c.position).collect(), 1..=1, diff --git a/server/src/content/incidents_trade.rs b/server/src/content/incidents_trade.rs new file mode 100644 index 00000000..e78996e7 --- /dev/null +++ b/server/src/content/incidents_trade.rs @@ -0,0 +1,207 @@ +use crate::city::City; +use crate::city_pieces::Building; +use crate::content::custom_phase_actions::{new_position_request, ResourceRewardRequest}; +use crate::game::Game; +use crate::incident::{Incident, IncidentBaseEffect, PassedIncident, PermanentIncidentEffect}; +use crate::payment::PaymentOptions; +use crate::player::Player; +use crate::player_events::IncidentTarget; +use crate::resource_pile::ResourcePile; + +pub(crate) fn trades() -> Vec { + vec![ + scientific_trade(), + flourishing_trade(), + era_of_stability(), + reformation(), + ] +} + +fn scientific_trade() -> Incident { + Incident::builder( + 45, + "Scientific Trade", + "Every player gains an amount of ideas equal to the number of cities that have an Academy or Observatory. You gain at least 2 ideas.", + IncidentBaseEffect::PiratesSpawnAndRaid, + ) + .add_simple_incident_listener( + IncidentTarget::AllPlayers, + 0, + |game, p, name, i| { + let player = game.get_player_mut(p); + let mut ideas = player + .cities + .iter() + .filter(|c| { + let b = c.pieces.buildings(None); + b.contains(&Building::Academy) || b.contains(&Building::Observatory) + }) + .count(); + if i.active_player == p { + ideas = ideas.max(2); + } + + let pile = ResourcePile::ideas(ideas as u32); + player.gain_resources(pile.clone()); + game.add_info_log_item(&format!("{name} gained {pile}")); + }).build() +} + +fn flourishing_trade() -> Incident { + Incident::builder( + 46, + "Flourishing Trade", + "Every player gains an amount of gold equal to the number of cities that have a Market or Port (up to a maximum of 3). You gain at least 1 gold.", + IncidentBaseEffect::PiratesSpawnAndRaid, + ) + .add_simple_incident_listener( + IncidentTarget::AllPlayers, + 0, + |game, p, name, i| { + let player = game.get_player_mut(p); + let mut gold = player + .cities + .iter() + .filter(|c| { + let b = c.pieces.buildings(None); + b.contains(&Building::Market) || b.contains(&Building::Port) + }) + .count(); + + gold = gold.min(3); + + if i.active_player == p { + gold = gold.max(1); + } + + let pile = ResourcePile::gold(gold as u32); + player.gain_resources(pile.clone()); + game.add_info_log_item(&format!("{name} gained {pile}")); + }).build() +} + +fn era_of_stability() -> Incident { + Incident::builder( + 47, + "Era of Stability", + "Every player gains an amount of mood or culture tokens equal to the number of cities that have a Temple or Obelisk (up to a maximum of 3). You gain at least 1 token.", + IncidentBaseEffect::ExhaustedLand, + ) + .add_incident_resource_request( + IncidentTarget::AllPlayers, + 0, + |game, p, i| { + let player = game.get_player(p); + let mut tokens = player + .cities + .iter() + .filter(|c| { + let b = c.pieces.buildings(None); + b.contains(&Building::Temple) || b.contains(&Building::Obelisk) + }) + .count(); + + tokens = tokens.min(3); + + if i.active_player == p { + tokens = tokens.max(1); + } + Some(ResourceRewardRequest::new(PaymentOptions::tokens(tokens as u32), + "Select token to gain".to_string())) + }, + |_game, s| { + vec![format!("{} gained {}", s.player_name, s.choice)] + }, + ).build() +} + +fn reformation() -> Incident { + Incident::builder( + 48, + "Reformation", + "Select another player to replace one of your Temples with one of their Temples (this can't be prevented). If you don't own any Temples, select a player that has a Temple: they execute this event instead.", + IncidentBaseEffect::BarbariansSpawn, + ) + .add_simple_incident_listener( + IncidentTarget::ActivePlayer, + 4, + |game, p, player_name, _i| { + // if game.permanent_incident_effects.iter().any( + // |e| matches!(e, PermanentIncidentEffect::PassedIncident(PassedIncident::AlreadyPassed))) { + // // can't pass the event again + // return; + // } + + if has_temple(game, p) { + game.current_event_mut().selected_player = Some(p); + } else if game.players.iter().filter( + |p| has_temple(game, p.index) && p.is_human()).count() > 1 { + game.add_info_log_item(&format!("{player_name} has no temples - and must select a player to execute the event")); + } + }, + ) + // select a player to execute the incident + .add_incident_player_request( + "Select a player to execute the event", + |p, game| has_temple(game, p.index) && game.current_event().selected_player.is_none(), + 3, + |game, s| { + // pass the event to the player itself + game.permanent_incident_effects.push(PermanentIncidentEffect::PassedIncident( + PassedIncident::NewPlayer(s.choice))); + game.add_info_log_item( + &format!("{} selected {} to execute the event", + s.player_name, game.player_name(s.choice))); + }, + ) + // select a player to gain a temple + .add_incident_player_request( + "Select a player to gain a Temple", + |p, game| can_gain_temple(game, p), + 2, + |game, s| { + game.current_event_mut().selected_player = Some(s.choice); + }, + ) + .add_incident_position_request( + IncidentTarget::SelectedPlayer, + 1, + |game, _p, i| { + let donor = i.active_player; + let choices = game.get_player(donor).cities.iter() + .filter(|c| city_has_temple(c, donor)) + .map(|c| c.position).collect(); + Some(new_position_request( + choices, + 1..=1, + "Select a city to gain a Temple", + )) + }, + |game, s| { + let donor = game.get_player_mut(s.details.active_player); + let pos = s.choice[0]; + donor.get_city_mut(pos).pieces.set_building(Building::Temple, s.player_index); + let donor_name = donor.get_name(); + game.add_info_log_item(&format!( + "{} gained a Temple from {donor_name} in {pos}", + s.player_name, + )); + }, + ) + .build() +} + +fn has_temple(game: &Game, player: usize) -> bool { + game.get_player(player) + .cities + .iter() + .any(|c| city_has_temple(c, player)) +} + +fn city_has_temple(c: &City, player: usize) -> bool { + c.pieces.buildings(Some(player)).contains(&Building::Temple) +} + +fn can_gain_temple(game: &Game, player: &Player) -> bool { + player.is_building_available(Building::Temple, game) +} diff --git a/server/src/content/incidents_trojan.rs b/server/src/content/incidents_trojan.rs index 7634d526..bd3f4594 100644 --- a/server/src/content/incidents_trojan.rs +++ b/server/src/content/incidents_trojan.rs @@ -24,7 +24,7 @@ fn trojan_horse() -> Incident { &format!("The following is available to all players: {TROJAN_DESCRIPTION}"), IncidentBaseEffect::BarbariansMove, ) - .add_simple_incident_listener(IncidentTarget::ActivePlayer, 0, |game, _, _| { + .add_simple_incident_listener(IncidentTarget::ActivePlayer, 0, |game, _, _, _| { game.permanent_incident_effects .push(PermanentIncidentEffect::TrojanHorse); }) @@ -108,7 +108,7 @@ fn solar_eclipse() -> Incident { "The next land battle will end after the first round (retreat if not finished). The winner gains 1 victory point (defender if draw).", IncidentBaseEffect::PiratesSpawnAndRaid, ) - .add_simple_incident_listener(IncidentTarget::ActivePlayer, 0, |game, _, _| { + .add_simple_incident_listener(IncidentTarget::ActivePlayer, 0, |game, _, _, _| { game.permanent_incident_effects .push(PermanentIncidentEffect::SolarEclipse); }) @@ -173,7 +173,7 @@ fn anarchy() -> Incident { "Set aside all government advances. Whenever you research a new government advance, take a game event token from there instead of the supply (thereby not triggering game events). Each advance left in the government advances area at the end of the game is worth 1 victory point.", IncidentBaseEffect::None, ) - .add_simple_incident_listener(IncidentTarget::ActivePlayer, 0, |game, player_index, player_name| { + .add_simple_incident_listener(IncidentTarget::ActivePlayer, 0, |game, player_index, player_name, _| { let p = game.get_player_mut(player_index); let old = p.advances.len(); p.advances.retain(|a| a.government.is_none()); @@ -204,9 +204,9 @@ pub(crate) fn anarchy_advance() -> Builtin { return; } - if let Some(PermanentIncidentEffect::Anarchy(mut a)) = remove_element_by(&mut game + if let Some(mut a) = remove_element_by(&mut game .permanent_incident_effects, - |e| matches!(e, PermanentIncidentEffect::Anarchy(_))) + |e| if let PermanentIncidentEffect::Anarchy(a) = e { Some(a.clone()) } else { None }) { if player_index == a.player { game.add_info_log_item(&format!( diff --git a/server/src/incident.rs b/server/src/incident.rs index 544fe56a..02902c60 100644 --- a/server/src/incident.rs +++ b/server/src/incident.rs @@ -7,7 +7,7 @@ use crate::content::custom_phase_actions::{ }; use crate::content::incidents; use crate::events::EventOrigin; -use crate::game::{Game, GameState}; +use crate::game::Game; use crate::map::Terrain; use crate::payment::{PaymentConversion, PaymentConversionType, PaymentOptions}; use crate::pirates::pirates_spawn_and_raid; @@ -16,9 +16,8 @@ use crate::player_events::{IncidentInfo, IncidentTarget}; use crate::position::Position; use crate::resource::ResourceType; use crate::resource_pile::ResourcePile; -use crate::status_phase::play_status_phase; use crate::unit::UnitType; -use crate::utils::Shuffle; +use crate::utils::{remove_element_by, Shuffle}; use itertools::Itertools; use serde::{Deserialize, Serialize}; @@ -90,6 +89,12 @@ pub struct Anarchy { pub advances_lost: usize, } +#[derive(Serialize, Deserialize, Clone, PartialEq, Debug)] +pub enum PassedIncident { + NewPlayer(usize), + AlreadyPassed, +} + #[derive(Serialize, Deserialize, Clone, PartialEq, Debug)] pub enum PermanentIncidentEffect { Pestilence, @@ -98,6 +103,7 @@ pub enum PermanentIncidentEffect { TrojanHorse, SolarEclipse, Anarchy(Anarchy), + PassedIncident(PassedIncident), } #[derive(Clone)] @@ -187,14 +193,14 @@ impl IncidentBuilder { listener: F, ) -> Self where - F: Fn(&mut Game, usize, &str) + 'static + Clone, + F: Fn(&mut Game, usize, &str, &IncidentInfo) + 'static + Clone, { self.add_simple_current_event_listener( |event| &mut event.on_incident, priority, move |game, player_index, player_name, i| { - if i.is_active(role, player_index) { - listener(game, player_index, player_name); + if i.is_active(role, player_index, game) { + listener(game, player_index, player_name, i); } }, ) @@ -356,7 +362,7 @@ impl IncidentBuilder { pub(crate) fn add_incident_player_request( self, description: &str, - player_pred: impl Fn(&Player) -> bool + 'static + Clone, + player_pred: impl Fn(&Player, &Game) -> bool + 'static + Clone, priority: i32, gain_reward: impl Fn(&mut Game, &SelectedChoice) + 'static + Clone, ) -> Self { @@ -370,7 +376,7 @@ impl IncidentBuilder { let choices = game .players .iter() - .filter(|p| p.is_human() && player_pred(p) && p.index != player_index) + .filter(|p| p.is_human() && p.index != player_index && player_pred(p, game)) .map(|p| p.index) .collect_vec(); @@ -439,7 +445,7 @@ impl AbilityInitializerSetup for IncidentBuilder { } } -pub(crate) fn trigger_incident(game: &mut Game, player_index: usize, info: &IncidentInfo) { +pub(crate) fn trigger_incident(game: &mut Game, mut info: IncidentInfo) { game.lock_undo(); // new information is revealed if game.incidents_left.is_empty() { @@ -450,25 +456,73 @@ pub(crate) fn trigger_incident(game: &mut Game, player_index: usize, info: &Inci let id = *game.incidents_left.first().expect("incident should exist"); let incident = incidents::get_incident(id); - game.trigger_current_event_with_listener( - &game.human_players(player_index), - |events| &mut events.on_incident, - &incident.listeners, - info, - CurrentEventType::Incident, - Some(&format!( - "A new game event has been triggered: {}", - incident.name - )), - ); - - if game.current_events.is_empty() { - game.incidents_left.remove(0); - - if matches!(game.state(), GameState::StatusPhase(_)) { - play_status_phase(game); + loop { + if game.trigger_current_event_with_listener( + &game.human_players(info.active_player), + |events| &mut events.on_incident, + &incident.listeners, + &info, + CurrentEventType::Incident, + play_base_effect(game).then_some(&format!( + "A new game event has been triggered: {}", + incident.name + )), + ) { + return; + } + + if !game + .current_events + .iter() + .any(|e| matches!(e.event_type, CurrentEventType::Incident(_))) + { + if let Some(p) = passed_to_player(game, info.active_player) { + info.active_player = p; + game.permanent_incident_effects + .push(PermanentIncidentEffect::PassedIncident( + PassedIncident::AlreadyPassed, + )); + continue; + } + break; + } + } + + game.incidents_left.remove(0); + + remove_element_by(&mut game.permanent_incident_effects, |e| { + matches!( + e, + PermanentIncidentEffect::PassedIncident(PassedIncident::AlreadyPassed) + ) + .then_some(true) + }); +} + +pub(crate) fn play_base_effect(game: &Game) -> bool { + !game + .permanent_incident_effects + .iter() + .any(|e| matches!(e, PermanentIncidentEffect::PassedIncident(_))) +} + +fn passed_to_player(game: &mut Game, player_index: usize) -> Option { + let p = remove_element_by(&mut game.permanent_incident_effects, |e| { + if let PermanentIncidentEffect::PassedIncident(PassedIncident::NewPlayer(p)) = e { + Some(*p) + } else { + None } + }); + + if let Some(p) = p { + game.add_info_log_item(&format!( + "Player {} has passed the incident to {}", + game.player_name(player_index), + game.player_name(p) + )); } + p } #[must_use] @@ -480,13 +534,13 @@ pub fn is_active( role: IncidentTarget, player: usize, ) -> bool { - if !i.is_active(role, player) { + if !i.is_active(role, player, game) { return false; } if priority >= BASE_EFFECT_PRIORITY { - // protection advance does not protect against base effects - return true; + return play_base_effect(game); } + // protection advance does not protect against base effects if let Some(advance) = &protection_advance { if game.players[player].has_advance(advance) { return false; diff --git a/server/src/payment.rs b/server/src/payment.rs index 212a46a6..fdf84141 100644 --- a/server/src/payment.rs +++ b/server/src/payment.rs @@ -126,6 +126,14 @@ impl PaymentOptions { } } + #[must_use] + pub fn tokens(cost: u32) -> Self { + Self::sum( + cost, + &[ResourceType::MoodTokens, ResourceType::CultureTokens], + ) + } + #[must_use] pub fn resources_with_discount( cost: ResourcePile, diff --git a/server/src/pirates.rs b/server/src/pirates.rs index 7ca035c6..e0a6c3fe 100644 --- a/server/src/pirates.rs +++ b/server/src/pirates.rs @@ -60,10 +60,7 @@ pub(crate) fn pirates_bonus() -> Builtin { .is_pirates() { Some(ResourceRewardRequest::new( - PaymentOptions::sum( - 1, - &[ResourceType::MoodTokens, ResourceType::CultureTokens], - ), + PaymentOptions::tokens(1), "Select a reward for fighting the Pirates".to_string(), )) } else { @@ -165,7 +162,7 @@ fn remove_pirate_ships(builder: IncidentBuilder) -> IncidentBuilder { |event| &mut event.on_incident, BASE_EFFECT_PRIORITY + 5, |game, player_index, i| { - if !i.is_active(IncidentTarget::ActivePlayer, player_index) { + if !i.is_active(IncidentTarget::ActivePlayer, player_index, game) { return None; } diff --git a/server/src/player_events.rs b/server/src/player_events.rs index b71e03b6..482074f9 100644 --- a/server/src/player_events.rs +++ b/server/src/player_events.rs @@ -5,6 +5,7 @@ use crate::combat_listeners::{CombatResultInfo, CombatRoundResult, CombatStrengt use crate::events::Event; use crate::explore::ExploreResolutionState; use crate::game::Game; +use crate::incident::{PassedIncident, PermanentIncidentEffect}; use crate::map::Terrain; use crate::payment::PaymentOptions; use crate::playing_actions::{PlayingActionType, Recruit}; @@ -90,6 +91,7 @@ impl ActionInfo { #[derive(Clone, PartialEq, Eq, Copy)] pub enum IncidentTarget { ActivePlayer, + SelectedPlayer, AllPlayers, } @@ -107,8 +109,22 @@ impl IncidentInfo { } #[must_use] - pub fn is_active(&self, role: IncidentTarget, player: usize) -> bool { - role == IncidentTarget::AllPlayers || self.active_player == player + pub fn is_active(&self, role: IncidentTarget, player: usize, game: &Game) -> bool { + if game.permanent_incident_effects.iter().any(|e| { + matches!( + e, + PermanentIncidentEffect::PassedIncident(PassedIncident::NewPlayer(_)) + ) + }) { + // wait until the new player is playing the advance + return false; + } + + match role { + IncidentTarget::ActivePlayer => self.active_player == player, + IncidentTarget::SelectedPlayer => game.current_event().selected_player == Some(player), + IncidentTarget::AllPlayers => true, + } } } diff --git a/server/src/utils.rs b/server/src/utils.rs index 87759a4e..c28eaa2d 100644 --- a/server/src/utils.rs +++ b/server/src/utils.rs @@ -24,15 +24,23 @@ where None } -pub fn remove_element_by(list: &mut Vec, f: F) -> Option +pub fn remove_element_by(list: &mut Vec, f: F) -> Option where - F: Fn(&T) -> bool, + F: Fn(&T) -> Option, { - let index = list.iter().position(f); + let mut u: Option = None; + let index = list.iter().position(|e| { + let option = f(e); + if option.is_some() { + u = option; + return true; + } + false + }); if let Some(index) = index { - return Some(list.remove(index)); + list.remove(index); } - None + u } pub fn ordinal_number(value: u32) -> String { diff --git a/server/tests/common/mod.rs b/server/tests/common/mod.rs index 9f2ae595..949d54e2 100644 --- a/server/tests/common/mod.rs +++ b/server/tests/common/mod.rs @@ -89,7 +89,7 @@ fn game_path(name: &str) -> String { pub(crate) type TestAssert = Vec>; -pub(crate) struct TestAction { +pub struct TestAction { action: Action, undoable: bool, illegal_action_test: bool, diff --git a/server/tests/incident_tests.rs b/server/tests/incident_tests.rs index 5a62533e..43e3e173 100644 --- a/server/tests/incident_tests.rs +++ b/server/tests/incident_tests.rs @@ -492,3 +492,76 @@ fn test_anarchy() { ], ); } + +#[test] +fn test_scientific_trade() { + JSON.test( + "scientific_trade", + vec![TestAction::not_undoable( + 0, + Action::Playing(Advance { + advance: String::from("Storage"), + payment: ResourcePile::gold(2), + }), + )], + ); +} + +#[test] +fn test_flourishing_trade() { + JSON.test( + "flourishing_trade", + vec![TestAction::not_undoable( + 0, + Action::Playing(Advance { + advance: String::from("Storage"), + payment: ResourcePile::gold(2), + }), + )], + ); +} + +#[test] +fn test_era_of_stability() { + JSON.test( + "era_of_stability", + vec![ + TestAction::not_undoable( + 0, + Action::Playing(Advance { + advance: String::from("Storage"), + payment: ResourcePile::gold(2), + }), + ), + TestAction::not_undoable( + 0, + Action::Response(CurrentEventResponse::ResourceReward( + ResourcePile::culture_tokens(1), + )), + ), + TestAction::not_undoable( + 1, + Action::Response(CurrentEventResponse::ResourceReward( + ResourcePile::culture_tokens(1), + )), + ), + ], + ); +} + +#[test] +fn test_reformation() { + JSON.test( + "reformation", + vec![ + TestAction::not_undoable( + 0, + Action::Playing(Advance { + advance: String::from("Storage"), + payment: ResourcePile::gold(2), + }), + ), + TestAction::not_undoable(2, Action::Response(CurrentEventResponse::SelectPlayer(1))), + ], + ); +} diff --git a/server/tests/test_games/incidents/barbarians_attack.outcome.json b/server/tests/test_games/incidents/barbarians_attack.outcome.json index a4938856..c6308faa 100644 --- a/server/tests/test_games/incidents/barbarians_attack.outcome.json +++ b/server/tests/test_games/incidents/barbarians_attack.outcome.json @@ -340,8 +340,5 @@ "wonders_left": [ "Pyramids" ], - "wonder_amount_left": 1, - "incidents_left": [ - 28 - ] + "wonder_amount_left": 1 } \ No newline at end of file diff --git a/server/tests/test_games/incidents/barbarians_move.outcome1.json b/server/tests/test_games/incidents/barbarians_move.outcome1.json index 4c8e2136..9ba50374 100644 --- a/server/tests/test_games/incidents/barbarians_move.outcome1.json +++ b/server/tests/test_games/incidents/barbarians_move.outcome1.json @@ -347,8 +347,5 @@ "wonders_left": [ "Pyramids" ], - "wonder_amount_left": 1, - "incidents_left": [ - 28 - ] + "wonder_amount_left": 1 } \ No newline at end of file diff --git a/server/tests/test_games/incidents/barbarians_spawn.outcome2.json b/server/tests/test_games/incidents/barbarians_spawn.outcome2.json index 8808627c..5fd40c12 100644 --- a/server/tests/test_games/incidents/barbarians_spawn.outcome2.json +++ b/server/tests/test_games/incidents/barbarians_spawn.outcome2.json @@ -332,8 +332,5 @@ "wonders_left": [ "Pyramids" ], - "wonder_amount_left": 1, - "incidents_left": [ - 9 - ] + "wonder_amount_left": 1 } \ No newline at end of file diff --git a/server/tests/test_games/incidents/era_of_stability.json b/server/tests/test_games/incidents/era_of_stability.json new file mode 100644 index 00000000..f3d9fda3 --- /dev/null +++ b/server/tests/test_games/incidents/era_of_stability.json @@ -0,0 +1,258 @@ +{ + "state": [ + "Playing" + ], + "players": [ + { + "name": null, + "id": 0, + "resources": { + "food": 2, + "wood": 7, + "ore": 7, + "ideas": 3, + "gold": 7, + "mood_tokens": 7, + "culture_tokens": 7 + }, + "resource_limit": { + "food": 2, + "wood": 7, + "ore": 7, + "ideas": 7, + "gold": 7, + "mood_tokens": 7, + "culture_tokens": 7 + }, + "cities": [ + { + "city_pieces": {}, + "mood_state": "Happy", + "activations": 0, + "angry_activation": false, + "position": "A1" + } + ], + "units": [ + { + "position": "C2", + "unit_type": "Infantry", + "id": 0 + }, + { + "position": "C2", + "unit_type": "Cavalry", + "id": 1 + }, + { + "position": "C2", + "unit_type": "Leader", + "id": 2 + }, + { + "position": "C2", + "unit_type": "Elephant", + "id": 3 + }, + { + "position": "C2", + "unit_type": "Settler", + "id": 4 + }, + { + "position": "C2", + "unit_type": "Settler", + "id": 5 + }, + { + "position": "C2", + "unit_type": "Settler", + "id": 6 + }, + { + "position": "C2", + "unit_type": "Settler", + "id": 7 + } + ], + "civilization": "test0", + "active_leader": null, + "available_leaders": [], + "advances": [ + "Farming", + "Mining", + "Tactics" + ], + "unlocked_special_advance": [], + "wonders_build": [], + "incident_tokens": 1, + "completed_objectives": [], + "captured_leaders": [], + "event_victory_points": 0.0, + "wonder_cards": [], + "next_unit_id": 8 + }, + { + "name": null, + "id": 1, + "resources": { + "food": 2, + "wood": 7, + "ore": 7, + "ideas": 7, + "gold": 7, + "mood_tokens": 7, + "culture_tokens": 7 + }, + "resource_limit": { + "food": 2, + "wood": 7, + "ore": 7, + "ideas": 7, + "gold": 7, + "mood_tokens": 7, + "culture_tokens": 7 + }, + "cities": [ + { + "city_pieces": { + "temple": 1 + }, + "mood_state": "Angry", + "activations": 0, + "angry_activation": false, + "position": "C1" + }, + { + "city_pieces": {}, + "mood_state": "Neutral", + "activations": 0, + "angry_activation": false, + "position": "C2" + } + ], + "units": [ + { + "position": "C1", + "unit_type": "Infantry", + "id": 0 + }, + { + "position": "C1", + "unit_type": "Infantry", + "id": 1 + } + ], + "civilization": "test1", + "active_leader": null, + "available_leaders": [], + "advances": [ + "Fanaticism", + "Farming", + "Mining", + "Tactics" + ], + "unlocked_special_advance": [], + "wonders_build": [], + "incident_tokens": 3, + "completed_objectives": [], + "captured_leaders": [], + "event_victory_points": 0.0, + "wonder_cards": [], + "next_unit_id": 2 + }, + { + "name": null, + "id": 2, + "resources": {}, + "resource_limit": { + "food": 2, + "wood": 7, + "ore": 7, + "ideas": 7, + "gold": 7 + }, + "cities": [], + "units": [], + "civilization": "Pirates", + "active_leader": null, + "available_leaders": [], + "advances": [], + "unlocked_special_advance": [], + "wonders_build": [], + "incident_tokens": 3, + "completed_objectives": [], + "captured_leaders": [], + "event_victory_points": 0.0, + "wonder_cards": [], + "next_unit_id": 0 + } + ], + "map": { + "tiles": [ + [ + "A1", + "Fertile" + ], + [ + "A3", + { + "Exhausted": "Forest" + } + ], + [ + "B1", + "Mountain" + ], + [ + "B2", + "Forest" + ], + [ + "B3", + "Fertile" + ], + [ + "C1", + "Barren" + ], + [ + "C2", + "Forest" + ] + ] + }, + "starting_player_index": 0, + "current_player_index": 0, + "action_log": [], + "action_log_index": 0, + "log": [], + "undo_limit": 0, + "actions_left": 3, + "successful_cultural_influence": false, + "round": 2, + "age": 1, + "messages": [ + "The game has started" + ], + "dice_roll_outcomes": [ + 1, + 1, + 1, + 1, + 1, + 11, + 11, + 11, + 11 + ], + "dice_roll_log": [], + "dropped_players": [], + "wonders_left": [ + "Pyramids" + ], + "wonder_amount_left": 1, + "incidents_left": [ + 47 + ] +} \ No newline at end of file diff --git a/server/tests/test_games/incidents/era_of_stability.outcome.json b/server/tests/test_games/incidents/era_of_stability.outcome.json new file mode 100644 index 00000000..2f97e660 --- /dev/null +++ b/server/tests/test_games/incidents/era_of_stability.outcome.json @@ -0,0 +1,323 @@ +{ + "state": [ + "Playing" + ], + "current_events": [ + { + "event_type": { + "Incident": { + "active_player": 0 + } + }, + "player": 0, + "last_priority_used": 0, + "handler": { + "priority": 0, + "request": { + "ResourceReward": { + "reward": { + "default": { + "mood_tokens": 1 + }, + "conversions": [ + { + "from": [ + { + "mood_tokens": 1 + } + ], + "to": { + "culture_tokens": 1 + }, + "type": "Unlimited" + } + ] + }, + "name": "Select token to gain" + } + }, + "origin": { + "Incident": 47 + } + } + } + ], + "players": [ + { + "name": null, + "id": 0, + "resources": { + "food": 2, + "wood": 7, + "ore": 7, + "ideas": 3, + "gold": 5, + "mood_tokens": 8, + "culture_tokens": 7 + }, + "resource_limit": { + "food": 7, + "wood": 7, + "ore": 7, + "ideas": 7, + "gold": 7, + "mood_tokens": 7, + "culture_tokens": 7 + }, + "cities": [ + { + "city_pieces": {}, + "mood_state": "Happy", + "activations": 0, + "angry_activation": false, + "position": "A1" + } + ], + "units": [ + { + "position": "C2", + "unit_type": "Infantry", + "id": 0 + }, + { + "position": "C2", + "unit_type": "Cavalry", + "id": 1 + }, + { + "position": "C2", + "unit_type": "Leader", + "id": 2 + }, + { + "position": "C2", + "unit_type": "Elephant", + "id": 3 + }, + { + "position": "C2", + "unit_type": "Settler", + "id": 4 + }, + { + "position": "C2", + "unit_type": "Settler", + "id": 5 + }, + { + "position": "C2", + "unit_type": "Settler", + "id": 6 + }, + { + "position": "C2", + "unit_type": "Settler", + "id": 7 + } + ], + "civilization": "test0", + "active_leader": null, + "available_leaders": [], + "advances": [ + "Farming", + "Mining", + "Storage", + "Tactics" + ], + "unlocked_special_advance": [], + "wonders_build": [], + "incident_tokens": 3, + "completed_objectives": [], + "captured_leaders": [], + "event_victory_points": 0.0, + "wonder_cards": [], + "next_unit_id": 8 + }, + { + "name": null, + "id": 1, + "resources": { + "food": 2, + "wood": 7, + "ore": 7, + "ideas": 7, + "gold": 7, + "mood_tokens": 7, + "culture_tokens": 7 + }, + "resource_limit": { + "food": 2, + "wood": 7, + "ore": 7, + "ideas": 7, + "gold": 7, + "mood_tokens": 7, + "culture_tokens": 7 + }, + "cities": [ + { + "city_pieces": { + "temple": 1 + }, + "mood_state": "Angry", + "activations": 0, + "angry_activation": false, + "position": "C1" + }, + { + "city_pieces": {}, + "mood_state": "Neutral", + "activations": 0, + "angry_activation": false, + "position": "C2" + } + ], + "units": [ + { + "position": "C1", + "unit_type": "Infantry", + "id": 0 + }, + { + "position": "C1", + "unit_type": "Infantry", + "id": 1 + } + ], + "civilization": "test1", + "active_leader": null, + "available_leaders": [], + "advances": [ + "Fanaticism", + "Farming", + "Mining", + "Tactics" + ], + "unlocked_special_advance": [], + "wonders_build": [], + "incident_tokens": 3, + "completed_objectives": [], + "captured_leaders": [], + "event_victory_points": 0.0, + "wonder_cards": [], + "next_unit_id": 2 + }, + { + "name": null, + "id": 2, + "resources": {}, + "resource_limit": { + "food": 2, + "wood": 7, + "ore": 7, + "ideas": 7, + "gold": 7 + }, + "cities": [], + "units": [], + "civilization": "Pirates", + "active_leader": null, + "available_leaders": [], + "advances": [], + "unlocked_special_advance": [], + "wonders_build": [], + "incident_tokens": 3, + "completed_objectives": [], + "captured_leaders": [], + "event_victory_points": 0.0, + "wonder_cards": [], + "next_unit_id": 0 + } + ], + "map": { + "tiles": [ + [ + "A1", + "Fertile" + ], + [ + "A3", + { + "Exhausted": "Forest" + } + ], + [ + "B1", + { + "Exhausted": "Mountain" + } + ], + [ + "B2", + "Forest" + ], + [ + "B3", + "Fertile" + ], + [ + "C1", + "Barren" + ], + [ + "C2", + "Forest" + ] + ] + }, + "starting_player_index": 0, + "current_player_index": 0, + "action_log": [ + { + "action": { + "Playing": { + "Advance": { + "advance": "Storage", + "payment": { + "gold": 2 + } + } + } + } + } + ], + "action_log_index": 1, + "log": [ + [ + "Player1 paid 2 gold to get the Storage advance", + "Player gained 1 mood token as advance bonus" + ], + [ + "A new game event has been triggered: Era of Stability", + "Player1 exhausted the land in position B1" + ] + ], + "undo_limit": 1, + "actions_left": 2, + "successful_cultural_influence": false, + "round": 2, + "age": 1, + "messages": [ + "The game has started" + ], + "dice_roll_outcomes": [ + 1, + 1, + 1, + 1, + 1, + 11, + 11, + 11, + 11 + ], + "dice_roll_log": [], + "dropped_players": [], + "wonders_left": [ + "Pyramids" + ], + "wonder_amount_left": 1, + "incidents_left": [ + 47 + ] +} \ No newline at end of file diff --git a/server/tests/test_games/incidents/era_of_stability.outcome1.json b/server/tests/test_games/incidents/era_of_stability.outcome1.json new file mode 100644 index 00000000..c54484ea --- /dev/null +++ b/server/tests/test_games/incidents/era_of_stability.outcome1.json @@ -0,0 +1,338 @@ +{ + "state": [ + "Playing" + ], + "current_events": [ + { + "event_type": { + "Incident": { + "active_player": 0 + } + }, + "players_used": [ + 0 + ], + "player": 1, + "last_priority_used": 0, + "handler": { + "priority": 0, + "request": { + "ResourceReward": { + "reward": { + "default": { + "mood_tokens": 1 + }, + "conversions": [ + { + "from": [ + { + "mood_tokens": 1 + } + ], + "to": { + "culture_tokens": 1 + }, + "type": "Unlimited" + } + ] + }, + "name": "Select token to gain" + } + }, + "origin": { + "Incident": 47 + } + } + } + ], + "players": [ + { + "name": null, + "id": 0, + "resources": { + "food": 2, + "wood": 7, + "ore": 7, + "ideas": 3, + "gold": 5, + "mood_tokens": 8, + "culture_tokens": 8 + }, + "resource_limit": { + "food": 7, + "wood": 7, + "ore": 7, + "ideas": 7, + "gold": 7, + "mood_tokens": 7, + "culture_tokens": 7 + }, + "cities": [ + { + "city_pieces": {}, + "mood_state": "Happy", + "activations": 0, + "angry_activation": false, + "position": "A1" + } + ], + "units": [ + { + "position": "C2", + "unit_type": "Infantry", + "id": 0 + }, + { + "position": "C2", + "unit_type": "Cavalry", + "id": 1 + }, + { + "position": "C2", + "unit_type": "Leader", + "id": 2 + }, + { + "position": "C2", + "unit_type": "Elephant", + "id": 3 + }, + { + "position": "C2", + "unit_type": "Settler", + "id": 4 + }, + { + "position": "C2", + "unit_type": "Settler", + "id": 5 + }, + { + "position": "C2", + "unit_type": "Settler", + "id": 6 + }, + { + "position": "C2", + "unit_type": "Settler", + "id": 7 + } + ], + "civilization": "test0", + "active_leader": null, + "available_leaders": [], + "advances": [ + "Farming", + "Mining", + "Storage", + "Tactics" + ], + "unlocked_special_advance": [], + "wonders_build": [], + "incident_tokens": 3, + "completed_objectives": [], + "captured_leaders": [], + "event_victory_points": 0.0, + "wonder_cards": [], + "next_unit_id": 8 + }, + { + "name": null, + "id": 1, + "resources": { + "food": 2, + "wood": 7, + "ore": 7, + "ideas": 7, + "gold": 7, + "mood_tokens": 7, + "culture_tokens": 7 + }, + "resource_limit": { + "food": 2, + "wood": 7, + "ore": 7, + "ideas": 7, + "gold": 7, + "mood_tokens": 7, + "culture_tokens": 7 + }, + "cities": [ + { + "city_pieces": { + "temple": 1 + }, + "mood_state": "Angry", + "activations": 0, + "angry_activation": false, + "position": "C1" + }, + { + "city_pieces": {}, + "mood_state": "Neutral", + "activations": 0, + "angry_activation": false, + "position": "C2" + } + ], + "units": [ + { + "position": "C1", + "unit_type": "Infantry", + "id": 0 + }, + { + "position": "C1", + "unit_type": "Infantry", + "id": 1 + } + ], + "civilization": "test1", + "active_leader": null, + "available_leaders": [], + "advances": [ + "Fanaticism", + "Farming", + "Mining", + "Tactics" + ], + "unlocked_special_advance": [], + "wonders_build": [], + "incident_tokens": 3, + "completed_objectives": [], + "captured_leaders": [], + "event_victory_points": 0.0, + "wonder_cards": [], + "next_unit_id": 2 + }, + { + "name": null, + "id": 2, + "resources": {}, + "resource_limit": { + "food": 2, + "wood": 7, + "ore": 7, + "ideas": 7, + "gold": 7 + }, + "cities": [], + "units": [], + "civilization": "Pirates", + "active_leader": null, + "available_leaders": [], + "advances": [], + "unlocked_special_advance": [], + "wonders_build": [], + "incident_tokens": 3, + "completed_objectives": [], + "captured_leaders": [], + "event_victory_points": 0.0, + "wonder_cards": [], + "next_unit_id": 0 + } + ], + "map": { + "tiles": [ + [ + "A1", + "Fertile" + ], + [ + "A3", + { + "Exhausted": "Forest" + } + ], + [ + "B1", + { + "Exhausted": "Mountain" + } + ], + [ + "B2", + "Forest" + ], + [ + "B3", + "Fertile" + ], + [ + "C1", + "Barren" + ], + [ + "C2", + "Forest" + ] + ] + }, + "starting_player_index": 0, + "current_player_index": 0, + "action_log": [ + { + "action": { + "Playing": { + "Advance": { + "advance": "Storage", + "payment": { + "gold": 2 + } + } + } + } + }, + { + "action": { + "Response": { + "ResourceReward": { + "culture_tokens": 1 + } + } + } + } + ], + "action_log_index": 2, + "log": [ + [ + "Player1 paid 2 gold to get the Storage advance", + "Player gained 1 mood token as advance bonus" + ], + [ + "A new game event has been triggered: Era of Stability", + "Player1 exhausted the land in position B1" + ], + [ + "Player1 gained 1 culture token" + ] + ], + "undo_limit": 2, + "actions_left": 2, + "successful_cultural_influence": false, + "round": 2, + "age": 1, + "messages": [ + "The game has started" + ], + "dice_roll_outcomes": [ + 1, + 1, + 1, + 1, + 1, + 11, + 11, + 11, + 11 + ], + "dice_roll_log": [], + "dropped_players": [], + "wonders_left": [ + "Pyramids" + ], + "wonder_amount_left": 1, + "incidents_left": [ + 47 + ] +} \ No newline at end of file diff --git a/server/tests/test_games/incidents/era_of_stability.outcome2.json b/server/tests/test_games/incidents/era_of_stability.outcome2.json new file mode 100644 index 00000000..7a9ae8ab --- /dev/null +++ b/server/tests/test_games/incidents/era_of_stability.outcome2.json @@ -0,0 +1,304 @@ +{ + "state": [ + "Playing" + ], + "players": [ + { + "name": null, + "id": 0, + "resources": { + "food": 2, + "wood": 7, + "ore": 7, + "ideas": 3, + "gold": 5, + "mood_tokens": 8, + "culture_tokens": 8 + }, + "resource_limit": { + "food": 7, + "wood": 7, + "ore": 7, + "ideas": 7, + "gold": 7, + "mood_tokens": 7, + "culture_tokens": 7 + }, + "cities": [ + { + "city_pieces": {}, + "mood_state": "Happy", + "activations": 0, + "angry_activation": false, + "position": "A1" + } + ], + "units": [ + { + "position": "C2", + "unit_type": "Infantry", + "id": 0 + }, + { + "position": "C2", + "unit_type": "Cavalry", + "id": 1 + }, + { + "position": "C2", + "unit_type": "Leader", + "id": 2 + }, + { + "position": "C2", + "unit_type": "Elephant", + "id": 3 + }, + { + "position": "C2", + "unit_type": "Settler", + "id": 4 + }, + { + "position": "C2", + "unit_type": "Settler", + "id": 5 + }, + { + "position": "C2", + "unit_type": "Settler", + "id": 6 + }, + { + "position": "C2", + "unit_type": "Settler", + "id": 7 + } + ], + "civilization": "test0", + "active_leader": null, + "available_leaders": [], + "advances": [ + "Farming", + "Mining", + "Storage", + "Tactics" + ], + "unlocked_special_advance": [], + "wonders_build": [], + "incident_tokens": 3, + "completed_objectives": [], + "captured_leaders": [], + "event_victory_points": 0.0, + "wonder_cards": [], + "next_unit_id": 8 + }, + { + "name": null, + "id": 1, + "resources": { + "food": 2, + "wood": 7, + "ore": 7, + "ideas": 7, + "gold": 7, + "mood_tokens": 7, + "culture_tokens": 8 + }, + "resource_limit": { + "food": 2, + "wood": 7, + "ore": 7, + "ideas": 7, + "gold": 7, + "mood_tokens": 7, + "culture_tokens": 7 + }, + "cities": [ + { + "city_pieces": { + "temple": 1 + }, + "mood_state": "Angry", + "activations": 0, + "angry_activation": false, + "position": "C1" + }, + { + "city_pieces": {}, + "mood_state": "Neutral", + "activations": 0, + "angry_activation": false, + "position": "C2" + } + ], + "units": [ + { + "position": "C1", + "unit_type": "Infantry", + "id": 0 + }, + { + "position": "C1", + "unit_type": "Infantry", + "id": 1 + } + ], + "civilization": "test1", + "active_leader": null, + "available_leaders": [], + "advances": [ + "Fanaticism", + "Farming", + "Mining", + "Tactics" + ], + "unlocked_special_advance": [], + "wonders_build": [], + "incident_tokens": 3, + "completed_objectives": [], + "captured_leaders": [], + "event_victory_points": 0.0, + "wonder_cards": [], + "next_unit_id": 2 + }, + { + "name": null, + "id": 2, + "resources": {}, + "resource_limit": { + "food": 2, + "wood": 7, + "ore": 7, + "ideas": 7, + "gold": 7 + }, + "cities": [], + "units": [], + "civilization": "Pirates", + "active_leader": null, + "available_leaders": [], + "advances": [], + "unlocked_special_advance": [], + "wonders_build": [], + "incident_tokens": 3, + "completed_objectives": [], + "captured_leaders": [], + "event_victory_points": 0.0, + "wonder_cards": [], + "next_unit_id": 0 + } + ], + "map": { + "tiles": [ + [ + "A1", + "Fertile" + ], + [ + "A3", + { + "Exhausted": "Forest" + } + ], + [ + "B1", + { + "Exhausted": "Mountain" + } + ], + [ + "B2", + "Forest" + ], + [ + "B3", + "Fertile" + ], + [ + "C1", + "Barren" + ], + [ + "C2", + "Forest" + ] + ] + }, + "starting_player_index": 0, + "current_player_index": 0, + "action_log": [ + { + "action": { + "Playing": { + "Advance": { + "advance": "Storage", + "payment": { + "gold": 2 + } + } + } + } + }, + { + "action": { + "Response": { + "ResourceReward": { + "culture_tokens": 1 + } + } + } + }, + { + "action": { + "Response": { + "ResourceReward": { + "culture_tokens": 1 + } + } + } + } + ], + "action_log_index": 3, + "log": [ + [ + "Player1 paid 2 gold to get the Storage advance", + "Player gained 1 mood token as advance bonus" + ], + [ + "A new game event has been triggered: Era of Stability", + "Player1 exhausted the land in position B1" + ], + [ + "Player1 gained 1 culture token" + ], + [ + "Player2 gained 1 culture token" + ] + ], + "undo_limit": 3, + "actions_left": 2, + "successful_cultural_influence": false, + "round": 2, + "age": 1, + "messages": [ + "The game has started" + ], + "dice_roll_outcomes": [ + 1, + 1, + 1, + 1, + 1, + 11, + 11, + 11, + 11 + ], + "dice_roll_log": [], + "dropped_players": [], + "wonders_left": [ + "Pyramids" + ], + "wonder_amount_left": 1 +} \ No newline at end of file diff --git a/server/tests/test_games/incidents/exhausted_land.outcome1.json b/server/tests/test_games/incidents/exhausted_land.outcome1.json index ed9a62c2..f86f586e 100644 --- a/server/tests/test_games/incidents/exhausted_land.outcome1.json +++ b/server/tests/test_games/incidents/exhausted_land.outcome1.json @@ -304,8 +304,5 @@ "wonders_left": [ "Pyramids" ], - "wonder_amount_left": 1, - "incidents_left": [ - 13 - ] + "wonder_amount_left": 1 } \ No newline at end of file diff --git a/server/tests/test_games/incidents/flourishing_trade.json b/server/tests/test_games/incidents/flourishing_trade.json new file mode 100644 index 00000000..21f4b6c5 --- /dev/null +++ b/server/tests/test_games/incidents/flourishing_trade.json @@ -0,0 +1,258 @@ +{ + "state": [ + "Playing" + ], + "players": [ + { + "name": null, + "id": 0, + "resources": { + "food": 2, + "wood": 7, + "ore": 7, + "ideas": 3, + "gold": 7, + "mood_tokens": 7, + "culture_tokens": 7 + }, + "resource_limit": { + "food": 2, + "wood": 7, + "ore": 7, + "ideas": 7, + "gold": 7, + "mood_tokens": 7, + "culture_tokens": 7 + }, + "cities": [ + { + "city_pieces": {}, + "mood_state": "Happy", + "activations": 0, + "angry_activation": false, + "position": "A1" + } + ], + "units": [ + { + "position": "C2", + "unit_type": "Infantry", + "id": 0 + }, + { + "position": "C2", + "unit_type": "Cavalry", + "id": 1 + }, + { + "position": "C2", + "unit_type": "Leader", + "id": 2 + }, + { + "position": "C2", + "unit_type": "Elephant", + "id": 3 + }, + { + "position": "C2", + "unit_type": "Settler", + "id": 4 + }, + { + "position": "C2", + "unit_type": "Settler", + "id": 5 + }, + { + "position": "C2", + "unit_type": "Settler", + "id": 6 + }, + { + "position": "C2", + "unit_type": "Settler", + "id": 7 + } + ], + "civilization": "test0", + "active_leader": null, + "available_leaders": [], + "advances": [ + "Farming", + "Mining", + "Tactics" + ], + "unlocked_special_advance": [], + "wonders_build": [], + "incident_tokens": 1, + "completed_objectives": [], + "captured_leaders": [], + "event_victory_points": 0.0, + "wonder_cards": [], + "next_unit_id": 8 + }, + { + "name": null, + "id": 1, + "resources": { + "food": 2, + "wood": 7, + "ore": 7, + "ideas": 7, + "gold": 7, + "mood_tokens": 7, + "culture_tokens": 7 + }, + "resource_limit": { + "food": 2, + "wood": 7, + "ore": 7, + "ideas": 7, + "gold": 7, + "mood_tokens": 7, + "culture_tokens": 7 + }, + "cities": [ + { + "city_pieces": { + "market": 1 + }, + "mood_state": "Angry", + "activations": 0, + "angry_activation": false, + "position": "C1" + }, + { + "city_pieces": {}, + "mood_state": "Neutral", + "activations": 0, + "angry_activation": false, + "position": "C2" + } + ], + "units": [ + { + "position": "C1", + "unit_type": "Infantry", + "id": 0 + }, + { + "position": "C1", + "unit_type": "Infantry", + "id": 1 + } + ], + "civilization": "test1", + "active_leader": null, + "available_leaders": [], + "advances": [ + "Fanaticism", + "Farming", + "Mining", + "Tactics" + ], + "unlocked_special_advance": [], + "wonders_build": [], + "incident_tokens": 3, + "completed_objectives": [], + "captured_leaders": [], + "event_victory_points": 0.0, + "wonder_cards": [], + "next_unit_id": 2 + }, + { + "name": null, + "id": 2, + "resources": {}, + "resource_limit": { + "food": 2, + "wood": 7, + "ore": 7, + "ideas": 7, + "gold": 7 + }, + "cities": [], + "units": [], + "civilization": "Pirates", + "active_leader": null, + "available_leaders": [], + "advances": [], + "unlocked_special_advance": [], + "wonders_build": [], + "incident_tokens": 3, + "completed_objectives": [], + "captured_leaders": [], + "event_victory_points": 0.0, + "wonder_cards": [], + "next_unit_id": 0 + } + ], + "map": { + "tiles": [ + [ + "A1", + "Fertile" + ], + [ + "A3", + { + "Exhausted": "Forest" + } + ], + [ + "B1", + "Mountain" + ], + [ + "B2", + "Forest" + ], + [ + "B3", + "Fertile" + ], + [ + "C1", + "Barren" + ], + [ + "C2", + "Forest" + ] + ] + }, + "starting_player_index": 0, + "current_player_index": 0, + "action_log": [], + "action_log_index": 0, + "log": [], + "undo_limit": 0, + "actions_left": 3, + "successful_cultural_influence": false, + "round": 2, + "age": 1, + "messages": [ + "The game has started" + ], + "dice_roll_outcomes": [ + 1, + 1, + 1, + 1, + 1, + 11, + 11, + 11, + 11 + ], + "dice_roll_log": [], + "dropped_players": [], + "wonders_left": [ + "Pyramids" + ], + "wonder_amount_left": 1, + "incidents_left": [ + 46 + ] +} \ No newline at end of file diff --git a/server/tests/test_games/incidents/flourishing_trade.outcome.json b/server/tests/test_games/incidents/flourishing_trade.outcome.json new file mode 100644 index 00000000..a4f70fba --- /dev/null +++ b/server/tests/test_games/incidents/flourishing_trade.outcome.json @@ -0,0 +1,282 @@ +{ + "state": [ + "Playing" + ], + "players": [ + { + "name": null, + "id": 0, + "resources": { + "food": 2, + "wood": 7, + "ore": 7, + "ideas": 3, + "gold": 6, + "mood_tokens": 8, + "culture_tokens": 7 + }, + "resource_limit": { + "food": 7, + "wood": 7, + "ore": 7, + "ideas": 7, + "gold": 7, + "mood_tokens": 7, + "culture_tokens": 7 + }, + "cities": [ + { + "city_pieces": {}, + "mood_state": "Happy", + "activations": 0, + "angry_activation": false, + "position": "A1" + } + ], + "units": [ + { + "position": "C2", + "unit_type": "Infantry", + "id": 0 + }, + { + "position": "C2", + "unit_type": "Cavalry", + "id": 1 + }, + { + "position": "C2", + "unit_type": "Leader", + "id": 2 + }, + { + "position": "C2", + "unit_type": "Elephant", + "id": 3 + }, + { + "position": "C2", + "unit_type": "Settler", + "id": 4 + }, + { + "position": "C2", + "unit_type": "Settler", + "id": 5 + }, + { + "position": "C2", + "unit_type": "Settler", + "id": 6 + }, + { + "position": "C2", + "unit_type": "Settler", + "id": 7 + } + ], + "civilization": "test0", + "active_leader": null, + "available_leaders": [], + "advances": [ + "Farming", + "Mining", + "Storage", + "Tactics" + ], + "unlocked_special_advance": [], + "wonders_build": [], + "incident_tokens": 3, + "completed_objectives": [], + "captured_leaders": [], + "event_victory_points": 0.0, + "wonder_cards": [], + "next_unit_id": 8 + }, + { + "name": null, + "id": 1, + "resources": { + "food": 2, + "wood": 7, + "ore": 7, + "ideas": 7, + "gold": 7, + "mood_tokens": 7, + "culture_tokens": 7 + }, + "resource_limit": { + "food": 2, + "wood": 7, + "ore": 7, + "ideas": 7, + "gold": 7, + "mood_tokens": 7, + "culture_tokens": 7 + }, + "cities": [ + { + "city_pieces": { + "market": 1 + }, + "mood_state": "Angry", + "activations": 0, + "angry_activation": false, + "position": "C1" + }, + { + "city_pieces": {}, + "mood_state": "Neutral", + "activations": 0, + "angry_activation": false, + "position": "C2" + } + ], + "units": [ + { + "position": "C1", + "unit_type": "Infantry", + "id": 0 + }, + { + "position": "C1", + "unit_type": "Infantry", + "id": 1 + } + ], + "civilization": "test1", + "active_leader": null, + "available_leaders": [], + "advances": [ + "Fanaticism", + "Farming", + "Mining", + "Tactics" + ], + "unlocked_special_advance": [], + "wonders_build": [], + "incident_tokens": 3, + "completed_objectives": [], + "captured_leaders": [], + "event_victory_points": 0.0, + "wonder_cards": [], + "next_unit_id": 2 + }, + { + "name": null, + "id": 2, + "resources": {}, + "resource_limit": { + "food": 2, + "wood": 7, + "ore": 7, + "ideas": 7, + "gold": 7 + }, + "cities": [], + "units": [], + "civilization": "Pirates", + "active_leader": null, + "available_leaders": [], + "advances": [], + "unlocked_special_advance": [], + "wonders_build": [], + "incident_tokens": 3, + "completed_objectives": [], + "captured_leaders": [], + "event_victory_points": 0.0, + "wonder_cards": [], + "next_unit_id": 0 + } + ], + "map": { + "tiles": [ + [ + "A1", + "Fertile" + ], + [ + "A3", + { + "Exhausted": "Forest" + } + ], + [ + "B1", + "Mountain" + ], + [ + "B2", + "Forest" + ], + [ + "B3", + "Fertile" + ], + [ + "C1", + "Barren" + ], + [ + "C2", + "Forest" + ] + ] + }, + "starting_player_index": 0, + "current_player_index": 0, + "action_log": [ + { + "action": { + "Playing": { + "Advance": { + "advance": "Storage", + "payment": { + "gold": 2 + } + } + } + } + } + ], + "action_log_index": 1, + "log": [ + [ + "Player1 paid 2 gold to get the Storage advance", + "Player gained 1 mood token as advance bonus" + ], + [ + "A new game event has been triggered: Flourishing Trade", + "Base effect: Pirates spawn", + "No valid positions for Pirate Ship", + "Player1 gained 1 gold", + "Player2 gained 1 gold", + "Player2 could not store 1 gold" + ] + ], + "undo_limit": 1, + "actions_left": 2, + "successful_cultural_influence": false, + "round": 2, + "age": 1, + "messages": [ + "The game has started" + ], + "dice_roll_outcomes": [ + 1, + 1, + 1, + 1, + 1, + 11, + 11, + 11, + 11 + ], + "dice_roll_log": [], + "dropped_players": [], + "wonders_left": [ + "Pyramids" + ], + "wonder_amount_left": 1 +} \ No newline at end of file diff --git a/server/tests/test_games/incidents/good_year.outcome1.json b/server/tests/test_games/incidents/good_year.outcome1.json index 03a41105..e9aa05b6 100644 --- a/server/tests/test_games/incidents/good_year.outcome1.json +++ b/server/tests/test_games/incidents/good_year.outcome1.json @@ -320,8 +320,5 @@ "wonders_left": [ "Pyramids" ], - "wonder_amount_left": 1, - "incidents_left": [ - 11 - ] -} + "wonder_amount_left": 1 +} \ No newline at end of file diff --git a/server/tests/test_games/incidents/pirates_spawn.outcome4.json b/server/tests/test_games/incidents/pirates_spawn.outcome4.json index c7d3bcf3..762cfa13 100644 --- a/server/tests/test_games/incidents/pirates_spawn.outcome4.json +++ b/server/tests/test_games/incidents/pirates_spawn.outcome4.json @@ -349,8 +349,5 @@ "wonders_left": [ "Pyramids" ], - "wonder_amount_left": 1, - "incidents_left": [ - 54 - ] -} + "wonder_amount_left": 1 +} \ No newline at end of file diff --git a/server/tests/test_games/incidents/reformation.json b/server/tests/test_games/incidents/reformation.json new file mode 100644 index 00000000..0420a0cf --- /dev/null +++ b/server/tests/test_games/incidents/reformation.json @@ -0,0 +1,334 @@ +{ + "state": [ + "Playing" + ], + "players": [ + { + "name": null, + "id": 0, + "resources": { + "food": 1, + "wood": 6, + "ore": 6, + "ideas": 5, + "gold": 7, + "mood_tokens": 7, + "culture_tokens": 7 + }, + "resource_limit": { + "food": 2, + "wood": 7, + "ore": 7, + "ideas": 7, + "gold": 7, + "mood_tokens": 7, + "culture_tokens": 7 + }, + "cities": [ + { + "city_pieces": { + "fortress": 0 + }, + "mood_state": "Neutral", + "activations": 0, + "angry_activation": false, + "position": "C2" + }, + { + "city_pieces": {}, + "mood_state": "Happy", + "activations": 0, + "angry_activation": false, + "position": "C1" + }, + { + "city_pieces": {}, + "mood_state": "Happy", + "activations": 0, + "angry_activation": false, + "position": "B2" + }, + { + "city_pieces": {}, + "mood_state": "Happy", + "activations": 0, + "angry_activation": false, + "position": "B3" + } + ], + "units": [ + { + "position": "C2", + "unit_type": "Infantry", + "id": 0 + }, + { + "position": "C2", + "unit_type": "Infantry", + "movement_restrictions": [ + "Battle" + ], + "id": 1 + }, + { + "position": "C2", + "unit_type": "Elephant", + "id": 3 + }, + { + "position": "C2", + "unit_type": "Settler", + "id": 4 + }, + { + "position": "C2", + "unit_type": "Settler", + "id": 5 + }, + { + "position": "C2", + "unit_type": "Settler", + "id": 6 + }, + { + "position": "C2", + "unit_type": "Settler", + "id": 7 + } + ], + "civilization": "test0", + "active_leader": null, + "available_leaders": [], + "advances": [ + "Arts", + "Farming", + "Mining", + "State Religion", + "Tactics", + "Voting" + ], + "unlocked_special_advance": [], + "wonders_build": [], + "incident_tokens": 1, + "completed_objectives": [], + "captured_leaders": [], + "event_victory_points": 0.0, + "wonder_cards": [], + "next_unit_id": 8 + }, + { + "name": null, + "id": 1, + "resources": { + "food": 2, + "wood": 7, + "ore": 7, + "ideas": 7, + "gold": 7, + "mood_tokens": 7, + "culture_tokens": 7 + }, + "resource_limit": { + "food": 2, + "wood": 7, + "ore": 7, + "ideas": 7, + "gold": 7, + "mood_tokens": 7, + "culture_tokens": 7 + }, + "cities": [ + { + "city_pieces": {}, + "mood_state": "Happy", + "activations": 0, + "angry_activation": false, + "position": "A3" + }, + { + "city_pieces": { + "fortress": 0 + }, + "mood_state": "Happy", + "activations": 0, + "angry_activation": false, + "position": "A4" + } + ], + "units": [], + "civilization": "test1", + "active_leader": null, + "available_leaders": [], + "advances": [ + "Farming", + "Mining" + ], + "unlocked_special_advance": [], + "wonders_build": [], + "incident_tokens": 3, + "completed_objectives": [], + "captured_leaders": [], + "event_victory_points": 0.0, + "wonder_cards": [], + "next_unit_id": 2 + }, + { + "name": null, + "id": 2, + "resources": { + "food": 2, + "wood": 7, + "ore": 7, + "ideas": 7, + "gold": 7, + "mood_tokens": 7, + "culture_tokens": 7 + }, + "resource_limit": { + "food": 2, + "wood": 7, + "ore": 7, + "ideas": 7, + "gold": 7, + "mood_tokens": 7, + "culture_tokens": 7 + }, + "cities": [ + { + "city_pieces": { + "temple": 2 + }, + "mood_state": "Happy", + "activations": 0, + "angry_activation": false, + "position": "A1" + } + ], + "units": [], + "civilization": "test1", + "active_leader": null, + "available_leaders": [], + "advances": [ + "Farming", + "Mining" + ], + "unlocked_special_advance": [], + "wonders_build": [], + "incident_tokens": 3, + "completed_objectives": [], + "captured_leaders": [], + "event_victory_points": 0.0, + "wonder_cards": [], + "next_unit_id": 2 + }, + { + "name": null, + "id": 3, + "resources": {}, + "resource_limit": { + "food": 2, + "wood": 7, + "ore": 7, + "ideas": 7, + "gold": 7 + }, + "cities": [], + "units": [], + "civilization": "Barbarians", + "active_leader": null, + "available_leaders": [], + "advances": [], + "unlocked_special_advance": [], + "wonders_build": [], + "incident_tokens": 3, + "completed_objectives": [], + "captured_leaders": [], + "event_victory_points": 0.0, + "wonder_cards": [], + "next_unit_id": 0 + } + ], + "map": { + "tiles": [ + [ + "A1", + "Fertile" + ], + [ + "A2", + "Water" + ], + [ + "A3", + "Mountain" + ], + [ + "A4", + "Mountain" + ], + [ + "B1", + "Mountain" + ], + [ + "B2", + "Forest" + ], + [ + "B3", + "Fertile" + ], + [ + "C1", + "Barren" + ], + [ + "C2", + "Forest" + ], + [ + "C3", + "Water" + ], + [ + "D2", + "Water" + ] + ] + }, + "starting_player_index": 0, + "current_player_index": 0, + "action_log": [], + "action_log_index": 0, + "log": [], + "undo_limit": 0, + "actions_left": 3, + "successful_cultural_influence": false, + "round": 1, + "age": 1, + "messages": [ + "The game has started" + ], + "dice_roll_outcomes": [ + 1, + 1, + 10, + 10, + 10, + 10, + 10, + 10, + 10 + ], + "dice_roll_log": [ + 10 + ], + "dropped_players": [], + "wonders_left": [ + "Pyramids" + ], + "wonder_amount_left": 1, + "incidents_left": [ + 48 + ] +} \ No newline at end of file diff --git a/server/tests/test_games/incidents/reformation.outcome.json b/server/tests/test_games/incidents/reformation.outcome.json new file mode 100644 index 00000000..befbca54 --- /dev/null +++ b/server/tests/test_games/incidents/reformation.outcome.json @@ -0,0 +1,393 @@ +{ + "state": [ + "Playing" + ], + "current_events": [ + { + "event_type": { + "Incident": { + "active_player": 2 + } + }, + "player": 2, + "last_priority_used": 2, + "handler": { + "priority": 2, + "request": { + "SelectPlayer": { + "choices": [ + 0, + 1 + ], + "description": "Select a player to gain a Temple" + } + }, + "origin": { + "Incident": 48 + } + }, + "selected_player": 2 + } + ], + "players": [ + { + "name": null, + "id": 0, + "resources": { + "food": 1, + "wood": 6, + "ore": 6, + "ideas": 5, + "gold": 5, + "mood_tokens": 8, + "culture_tokens": 7 + }, + "resource_limit": { + "food": 7, + "wood": 7, + "ore": 7, + "ideas": 7, + "gold": 7, + "mood_tokens": 7, + "culture_tokens": 7 + }, + "cities": [ + { + "city_pieces": { + "fortress": 0 + }, + "mood_state": "Neutral", + "activations": 0, + "angry_activation": false, + "position": "C2" + }, + { + "city_pieces": {}, + "mood_state": "Happy", + "activations": 0, + "angry_activation": false, + "position": "C1" + }, + { + "city_pieces": {}, + "mood_state": "Happy", + "activations": 0, + "angry_activation": false, + "position": "B2" + }, + { + "city_pieces": {}, + "mood_state": "Happy", + "activations": 0, + "angry_activation": false, + "position": "B3" + } + ], + "units": [ + { + "position": "C2", + "unit_type": "Infantry", + "id": 0 + }, + { + "position": "C2", + "unit_type": "Infantry", + "movement_restrictions": [ + "Battle" + ], + "id": 1 + }, + { + "position": "C2", + "unit_type": "Elephant", + "id": 3 + }, + { + "position": "C2", + "unit_type": "Settler", + "id": 4 + }, + { + "position": "C2", + "unit_type": "Settler", + "id": 5 + }, + { + "position": "C2", + "unit_type": "Settler", + "id": 6 + }, + { + "position": "C2", + "unit_type": "Settler", + "id": 7 + } + ], + "civilization": "test0", + "active_leader": null, + "available_leaders": [], + "advances": [ + "Arts", + "Farming", + "Mining", + "State Religion", + "Storage", + "Tactics", + "Voting" + ], + "unlocked_special_advance": [], + "wonders_build": [], + "incident_tokens": 3, + "completed_objectives": [], + "captured_leaders": [], + "event_victory_points": 0.0, + "wonder_cards": [], + "next_unit_id": 8 + }, + { + "name": null, + "id": 1, + "resources": { + "food": 2, + "wood": 7, + "ore": 7, + "ideas": 7, + "gold": 7, + "mood_tokens": 7, + "culture_tokens": 7 + }, + "resource_limit": { + "food": 2, + "wood": 7, + "ore": 7, + "ideas": 7, + "gold": 7, + "mood_tokens": 7, + "culture_tokens": 7 + }, + "cities": [ + { + "city_pieces": {}, + "mood_state": "Happy", + "activations": 0, + "angry_activation": false, + "position": "A3" + }, + { + "city_pieces": { + "fortress": 0 + }, + "mood_state": "Happy", + "activations": 0, + "angry_activation": false, + "position": "A4" + } + ], + "units": [], + "civilization": "test1", + "active_leader": null, + "available_leaders": [], + "advances": [ + "Farming", + "Mining" + ], + "unlocked_special_advance": [], + "wonders_build": [], + "incident_tokens": 3, + "completed_objectives": [], + "captured_leaders": [], + "event_victory_points": 0.0, + "wonder_cards": [], + "next_unit_id": 2 + }, + { + "name": null, + "id": 2, + "resources": { + "food": 2, + "wood": 7, + "ore": 7, + "ideas": 7, + "gold": 7, + "mood_tokens": 7, + "culture_tokens": 7 + }, + "resource_limit": { + "food": 2, + "wood": 7, + "ore": 7, + "ideas": 7, + "gold": 7, + "mood_tokens": 7, + "culture_tokens": 7 + }, + "cities": [ + { + "city_pieces": { + "temple": 2 + }, + "mood_state": "Happy", + "activations": 0, + "angry_activation": false, + "position": "A1" + } + ], + "units": [], + "civilization": "test1", + "active_leader": null, + "available_leaders": [], + "advances": [ + "Farming", + "Mining" + ], + "unlocked_special_advance": [], + "wonders_build": [], + "incident_tokens": 3, + "completed_objectives": [], + "captured_leaders": [], + "event_victory_points": 0.0, + "wonder_cards": [], + "next_unit_id": 2 + }, + { + "name": null, + "id": 3, + "resources": {}, + "resource_limit": { + "food": 2, + "wood": 7, + "ore": 7, + "ideas": 7, + "gold": 7 + }, + "cities": [], + "units": [], + "civilization": "Barbarians", + "active_leader": null, + "available_leaders": [], + "advances": [], + "unlocked_special_advance": [], + "wonders_build": [], + "incident_tokens": 3, + "completed_objectives": [], + "captured_leaders": [], + "event_victory_points": 0.0, + "wonder_cards": [], + "next_unit_id": 0 + } + ], + "map": { + "tiles": [ + [ + "A1", + "Fertile" + ], + [ + "A2", + "Water" + ], + [ + "A3", + "Mountain" + ], + [ + "A4", + "Mountain" + ], + [ + "B1", + "Mountain" + ], + [ + "B2", + "Forest" + ], + [ + "B3", + "Fertile" + ], + [ + "C1", + "Barren" + ], + [ + "C2", + "Forest" + ], + [ + "C3", + "Water" + ], + [ + "D2", + "Water" + ] + ] + }, + "starting_player_index": 0, + "current_player_index": 0, + "action_log": [ + { + "action": { + "Playing": { + "Advance": { + "advance": "Storage", + "payment": { + "gold": 2 + } + } + } + } + } + ], + "action_log_index": 1, + "log": [ + [ + "Player1 paid 2 gold to get the Storage advance", + "Player gained 1 mood token as advance bonus" + ], + [ + "A new game event has been triggered: Reformation", + "Base effect: Barbarians spawn", + "Barbarians cannot spawn a new city", + "Barbarians cannot reinforce", + "Player1 selected Player3 to execute the event", + "Player Player1 has passed the incident to Player3" + ] + ], + "undo_limit": 1, + "actions_left": 2, + "successful_cultural_influence": false, + "round": 1, + "age": 1, + "messages": [ + "The game has started" + ], + "dice_roll_outcomes": [ + 1, + 1, + 10, + 10, + 10, + 10, + 10, + 10, + 10 + ], + "dice_roll_log": [ + 10 + ], + "dropped_players": [], + "wonders_left": [ + "Pyramids" + ], + "wonder_amount_left": 1, + "incidents_left": [ + 48 + ], + "permanent_incident_effects": [ + { + "PassedIncident": "AlreadyPassed" + } + ] +} \ No newline at end of file diff --git a/server/tests/test_games/incidents/reformation.outcome1.json b/server/tests/test_games/incidents/reformation.outcome1.json new file mode 100644 index 00000000..5a807229 --- /dev/null +++ b/server/tests/test_games/incidents/reformation.outcome1.json @@ -0,0 +1,368 @@ +{ + "state": [ + "Playing" + ], + "players": [ + { + "name": null, + "id": 0, + "resources": { + "food": 1, + "wood": 6, + "ore": 6, + "ideas": 5, + "gold": 5, + "mood_tokens": 8, + "culture_tokens": 7 + }, + "resource_limit": { + "food": 7, + "wood": 7, + "ore": 7, + "ideas": 7, + "gold": 7, + "mood_tokens": 7, + "culture_tokens": 7 + }, + "cities": [ + { + "city_pieces": { + "fortress": 0 + }, + "mood_state": "Neutral", + "activations": 0, + "angry_activation": false, + "position": "C2" + }, + { + "city_pieces": {}, + "mood_state": "Happy", + "activations": 0, + "angry_activation": false, + "position": "C1" + }, + { + "city_pieces": {}, + "mood_state": "Happy", + "activations": 0, + "angry_activation": false, + "position": "B2" + }, + { + "city_pieces": {}, + "mood_state": "Happy", + "activations": 0, + "angry_activation": false, + "position": "B3" + } + ], + "units": [ + { + "position": "C2", + "unit_type": "Infantry", + "id": 0 + }, + { + "position": "C2", + "unit_type": "Infantry", + "movement_restrictions": [ + "Battle" + ], + "id": 1 + }, + { + "position": "C2", + "unit_type": "Elephant", + "id": 3 + }, + { + "position": "C2", + "unit_type": "Settler", + "id": 4 + }, + { + "position": "C2", + "unit_type": "Settler", + "id": 5 + }, + { + "position": "C2", + "unit_type": "Settler", + "id": 6 + }, + { + "position": "C2", + "unit_type": "Settler", + "id": 7 + } + ], + "civilization": "test0", + "active_leader": null, + "available_leaders": [], + "advances": [ + "Arts", + "Farming", + "Mining", + "State Religion", + "Storage", + "Tactics", + "Voting" + ], + "unlocked_special_advance": [], + "wonders_build": [], + "incident_tokens": 3, + "completed_objectives": [], + "captured_leaders": [], + "event_victory_points": 0.0, + "wonder_cards": [], + "next_unit_id": 8 + }, + { + "name": null, + "id": 1, + "resources": { + "food": 2, + "wood": 7, + "ore": 7, + "ideas": 7, + "gold": 7, + "mood_tokens": 7, + "culture_tokens": 7 + }, + "resource_limit": { + "food": 2, + "wood": 7, + "ore": 7, + "ideas": 7, + "gold": 7, + "mood_tokens": 7, + "culture_tokens": 7 + }, + "cities": [ + { + "city_pieces": {}, + "mood_state": "Happy", + "activations": 0, + "angry_activation": false, + "position": "A3" + }, + { + "city_pieces": { + "fortress": 0 + }, + "mood_state": "Happy", + "activations": 0, + "angry_activation": false, + "position": "A4" + } + ], + "units": [], + "civilization": "test1", + "active_leader": null, + "available_leaders": [], + "advances": [ + "Farming", + "Mining" + ], + "unlocked_special_advance": [], + "wonders_build": [], + "incident_tokens": 3, + "completed_objectives": [], + "captured_leaders": [], + "event_victory_points": 0.0, + "wonder_cards": [], + "next_unit_id": 2 + }, + { + "name": null, + "id": 2, + "resources": { + "food": 2, + "wood": 7, + "ore": 7, + "ideas": 7, + "gold": 7, + "mood_tokens": 7, + "culture_tokens": 7 + }, + "resource_limit": { + "food": 2, + "wood": 7, + "ore": 7, + "ideas": 7, + "gold": 7, + "mood_tokens": 7, + "culture_tokens": 7 + }, + "cities": [ + { + "city_pieces": { + "temple": 1 + }, + "mood_state": "Happy", + "activations": 0, + "angry_activation": false, + "position": "A1" + } + ], + "units": [], + "civilization": "test1", + "active_leader": null, + "available_leaders": [], + "advances": [ + "Farming", + "Mining" + ], + "unlocked_special_advance": [], + "wonders_build": [], + "incident_tokens": 3, + "completed_objectives": [], + "captured_leaders": [], + "event_victory_points": 0.0, + "wonder_cards": [], + "next_unit_id": 2 + }, + { + "name": null, + "id": 3, + "resources": {}, + "resource_limit": { + "food": 2, + "wood": 7, + "ore": 7, + "ideas": 7, + "gold": 7 + }, + "cities": [], + "units": [], + "civilization": "Barbarians", + "active_leader": null, + "available_leaders": [], + "advances": [], + "unlocked_special_advance": [], + "wonders_build": [], + "incident_tokens": 3, + "completed_objectives": [], + "captured_leaders": [], + "event_victory_points": 0.0, + "wonder_cards": [], + "next_unit_id": 0 + } + ], + "map": { + "tiles": [ + [ + "A1", + "Fertile" + ], + [ + "A2", + "Water" + ], + [ + "A3", + "Mountain" + ], + [ + "A4", + "Mountain" + ], + [ + "B1", + "Mountain" + ], + [ + "B2", + "Forest" + ], + [ + "B3", + "Fertile" + ], + [ + "C1", + "Barren" + ], + [ + "C2", + "Forest" + ], + [ + "C3", + "Water" + ], + [ + "D2", + "Water" + ] + ] + }, + "starting_player_index": 0, + "current_player_index": 0, + "action_log": [ + { + "action": { + "Playing": { + "Advance": { + "advance": "Storage", + "payment": { + "gold": 2 + } + } + } + } + }, + { + "action": { + "Response": { + "SelectPlayer": 1 + } + } + } + ], + "action_log_index": 2, + "log": [ + [ + "Player1 paid 2 gold to get the Storage advance", + "Player gained 1 mood token as advance bonus" + ], + [ + "A new game event has been triggered: Reformation", + "Base effect: Barbarians spawn", + "Barbarians cannot spawn a new city", + "Barbarians cannot reinforce", + "Player1 selected Player3 to execute the event", + "Player Player1 has passed the incident to Player3" + ], + [ + "Player2 gained a Temple from Player3 in A1" + ] + ], + "undo_limit": 2, + "actions_left": 2, + "successful_cultural_influence": false, + "round": 1, + "age": 1, + "messages": [ + "The game has started" + ], + "dice_roll_outcomes": [ + 1, + 1, + 10, + 10, + 10, + 10, + 10, + 10, + 10 + ], + "dice_roll_log": [ + 10 + ], + "dropped_players": [], + "wonders_left": [ + "Pyramids" + ], + "wonder_amount_left": 1 +} \ No newline at end of file diff --git a/server/tests/test_games/incidents/scientific_trade.json b/server/tests/test_games/incidents/scientific_trade.json new file mode 100644 index 00000000..b79cdb3a --- /dev/null +++ b/server/tests/test_games/incidents/scientific_trade.json @@ -0,0 +1,258 @@ +{ + "state": [ + "Playing" + ], + "players": [ + { + "name": null, + "id": 0, + "resources": { + "food": 2, + "wood": 7, + "ore": 7, + "ideas": 3, + "gold": 7, + "mood_tokens": 7, + "culture_tokens": 7 + }, + "resource_limit": { + "food": 2, + "wood": 7, + "ore": 7, + "ideas": 7, + "gold": 7, + "mood_tokens": 7, + "culture_tokens": 7 + }, + "cities": [ + { + "city_pieces": {}, + "mood_state": "Happy", + "activations": 0, + "angry_activation": false, + "position": "A1" + } + ], + "units": [ + { + "position": "C2", + "unit_type": "Infantry", + "id": 0 + }, + { + "position": "C2", + "unit_type": "Cavalry", + "id": 1 + }, + { + "position": "C2", + "unit_type": "Leader", + "id": 2 + }, + { + "position": "C2", + "unit_type": "Elephant", + "id": 3 + }, + { + "position": "C2", + "unit_type": "Settler", + "id": 4 + }, + { + "position": "C2", + "unit_type": "Settler", + "id": 5 + }, + { + "position": "C2", + "unit_type": "Settler", + "id": 6 + }, + { + "position": "C2", + "unit_type": "Settler", + "id": 7 + } + ], + "civilization": "test0", + "active_leader": null, + "available_leaders": [], + "advances": [ + "Farming", + "Mining", + "Tactics" + ], + "unlocked_special_advance": [], + "wonders_build": [], + "incident_tokens": 1, + "completed_objectives": [], + "captured_leaders": [], + "event_victory_points": 0.0, + "wonder_cards": [], + "next_unit_id": 8 + }, + { + "name": null, + "id": 1, + "resources": { + "food": 2, + "wood": 7, + "ore": 7, + "ideas": 7, + "gold": 7, + "mood_tokens": 7, + "culture_tokens": 7 + }, + "resource_limit": { + "food": 2, + "wood": 7, + "ore": 7, + "ideas": 7, + "gold": 7, + "mood_tokens": 7, + "culture_tokens": 7 + }, + "cities": [ + { + "city_pieces": { + "academy": 1 + }, + "mood_state": "Angry", + "activations": 0, + "angry_activation": false, + "position": "C1" + }, + { + "city_pieces": {}, + "mood_state": "Neutral", + "activations": 0, + "angry_activation": false, + "position": "C2" + } + ], + "units": [ + { + "position": "C1", + "unit_type": "Infantry", + "id": 0 + }, + { + "position": "C1", + "unit_type": "Infantry", + "id": 1 + } + ], + "civilization": "test1", + "active_leader": null, + "available_leaders": [], + "advances": [ + "Fanaticism", + "Farming", + "Mining", + "Tactics" + ], + "unlocked_special_advance": [], + "wonders_build": [], + "incident_tokens": 3, + "completed_objectives": [], + "captured_leaders": [], + "event_victory_points": 0.0, + "wonder_cards": [], + "next_unit_id": 2 + }, + { + "name": null, + "id": 2, + "resources": {}, + "resource_limit": { + "food": 2, + "wood": 7, + "ore": 7, + "ideas": 7, + "gold": 7 + }, + "cities": [], + "units": [], + "civilization": "Pirates", + "active_leader": null, + "available_leaders": [], + "advances": [], + "unlocked_special_advance": [], + "wonders_build": [], + "incident_tokens": 3, + "completed_objectives": [], + "captured_leaders": [], + "event_victory_points": 0.0, + "wonder_cards": [], + "next_unit_id": 0 + } + ], + "map": { + "tiles": [ + [ + "A1", + "Fertile" + ], + [ + "A3", + { + "Exhausted": "Forest" + } + ], + [ + "B1", + "Mountain" + ], + [ + "B2", + "Forest" + ], + [ + "B3", + "Fertile" + ], + [ + "C1", + "Barren" + ], + [ + "C2", + "Forest" + ] + ] + }, + "starting_player_index": 0, + "current_player_index": 0, + "action_log": [], + "action_log_index": 0, + "log": [], + "undo_limit": 0, + "actions_left": 3, + "successful_cultural_influence": false, + "round": 2, + "age": 1, + "messages": [ + "The game has started" + ], + "dice_roll_outcomes": [ + 1, + 1, + 1, + 1, + 1, + 11, + 11, + 11, + 11 + ], + "dice_roll_log": [], + "dropped_players": [], + "wonders_left": [ + "Pyramids" + ], + "wonder_amount_left": 1, + "incidents_left": [ + 45 + ] +} \ No newline at end of file diff --git a/server/tests/test_games/incidents/scientific_trade.outcome.json b/server/tests/test_games/incidents/scientific_trade.outcome.json new file mode 100644 index 00000000..7f9f6665 --- /dev/null +++ b/server/tests/test_games/incidents/scientific_trade.outcome.json @@ -0,0 +1,282 @@ +{ + "state": [ + "Playing" + ], + "players": [ + { + "name": null, + "id": 0, + "resources": { + "food": 2, + "wood": 7, + "ore": 7, + "ideas": 5, + "gold": 5, + "mood_tokens": 8, + "culture_tokens": 7 + }, + "resource_limit": { + "food": 7, + "wood": 7, + "ore": 7, + "ideas": 7, + "gold": 7, + "mood_tokens": 7, + "culture_tokens": 7 + }, + "cities": [ + { + "city_pieces": {}, + "mood_state": "Happy", + "activations": 0, + "angry_activation": false, + "position": "A1" + } + ], + "units": [ + { + "position": "C2", + "unit_type": "Infantry", + "id": 0 + }, + { + "position": "C2", + "unit_type": "Cavalry", + "id": 1 + }, + { + "position": "C2", + "unit_type": "Leader", + "id": 2 + }, + { + "position": "C2", + "unit_type": "Elephant", + "id": 3 + }, + { + "position": "C2", + "unit_type": "Settler", + "id": 4 + }, + { + "position": "C2", + "unit_type": "Settler", + "id": 5 + }, + { + "position": "C2", + "unit_type": "Settler", + "id": 6 + }, + { + "position": "C2", + "unit_type": "Settler", + "id": 7 + } + ], + "civilization": "test0", + "active_leader": null, + "available_leaders": [], + "advances": [ + "Farming", + "Mining", + "Storage", + "Tactics" + ], + "unlocked_special_advance": [], + "wonders_build": [], + "incident_tokens": 3, + "completed_objectives": [], + "captured_leaders": [], + "event_victory_points": 0.0, + "wonder_cards": [], + "next_unit_id": 8 + }, + { + "name": null, + "id": 1, + "resources": { + "food": 2, + "wood": 7, + "ore": 7, + "ideas": 7, + "gold": 7, + "mood_tokens": 7, + "culture_tokens": 7 + }, + "resource_limit": { + "food": 2, + "wood": 7, + "ore": 7, + "ideas": 7, + "gold": 7, + "mood_tokens": 7, + "culture_tokens": 7 + }, + "cities": [ + { + "city_pieces": { + "academy": 1 + }, + "mood_state": "Angry", + "activations": 0, + "angry_activation": false, + "position": "C1" + }, + { + "city_pieces": {}, + "mood_state": "Neutral", + "activations": 0, + "angry_activation": false, + "position": "C2" + } + ], + "units": [ + { + "position": "C1", + "unit_type": "Infantry", + "id": 0 + }, + { + "position": "C1", + "unit_type": "Infantry", + "id": 1 + } + ], + "civilization": "test1", + "active_leader": null, + "available_leaders": [], + "advances": [ + "Fanaticism", + "Farming", + "Mining", + "Tactics" + ], + "unlocked_special_advance": [], + "wonders_build": [], + "incident_tokens": 3, + "completed_objectives": [], + "captured_leaders": [], + "event_victory_points": 0.0, + "wonder_cards": [], + "next_unit_id": 2 + }, + { + "name": null, + "id": 2, + "resources": {}, + "resource_limit": { + "food": 2, + "wood": 7, + "ore": 7, + "ideas": 7, + "gold": 7 + }, + "cities": [], + "units": [], + "civilization": "Pirates", + "active_leader": null, + "available_leaders": [], + "advances": [], + "unlocked_special_advance": [], + "wonders_build": [], + "incident_tokens": 3, + "completed_objectives": [], + "captured_leaders": [], + "event_victory_points": 0.0, + "wonder_cards": [], + "next_unit_id": 0 + } + ], + "map": { + "tiles": [ + [ + "A1", + "Fertile" + ], + [ + "A3", + { + "Exhausted": "Forest" + } + ], + [ + "B1", + "Mountain" + ], + [ + "B2", + "Forest" + ], + [ + "B3", + "Fertile" + ], + [ + "C1", + "Barren" + ], + [ + "C2", + "Forest" + ] + ] + }, + "starting_player_index": 0, + "current_player_index": 0, + "action_log": [ + { + "action": { + "Playing": { + "Advance": { + "advance": "Storage", + "payment": { + "gold": 2 + } + } + } + } + } + ], + "action_log_index": 1, + "log": [ + [ + "Player1 paid 2 gold to get the Storage advance", + "Player gained 1 mood token as advance bonus" + ], + [ + "A new game event has been triggered: Scientific Trade", + "Base effect: Pirates spawn", + "No valid positions for Pirate Ship", + "Player1 gained 2 ideas", + "Player2 gained 1 idea", + "Player2 could not store 1 idea" + ] + ], + "undo_limit": 1, + "actions_left": 2, + "successful_cultural_influence": false, + "round": 2, + "age": 1, + "messages": [ + "The game has started" + ], + "dice_roll_outcomes": [ + 1, + 1, + 1, + 1, + 1, + 11, + 11, + 11, + 11 + ], + "dice_roll_log": [], + "dropped_players": [], + "wonders_left": [ + "Pyramids" + ], + "wonder_amount_left": 1 +} \ No newline at end of file