diff --git a/client/Cargo.lock b/client/Cargo.lock index 258429cf..40b95ee3 100644 --- a/client/Cargo.lock +++ b/client/Cargo.lock @@ -766,9 +766,9 @@ checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "serde" -version = "1.0.218" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8dfc9d19bdbf6d17e22319da49161d5d0108e4188e8b680aef6299eed22df60" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" dependencies = [ "serde_derive", ] @@ -786,9 +786,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.218" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f09503e191f4e797cb8aac08e9a4a4695c5edf6a2e70e376d961ddd5c969f82b" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", diff --git a/client/src/action_buttons.rs b/client/src/action_buttons.rs index 8580b637..2aa2ca45 100644 --- a/client/src/action_buttons.rs +++ b/client/src/action_buttons.rs @@ -11,8 +11,8 @@ use crate::payment_ui::Payment; use crate::render_context::RenderContext; use server::action::Action; use server::city::City; -use server::content::advances_culture::{sports_options, theaters_options}; -use server::content::advances_economy::tax_options; +use server::content::advances::culture::{sports_options, theaters_options}; +use server::content::advances::economy::tax_options; use server::content::custom_actions::{CustomAction, CustomActionType}; use server::game::GameState; use server::playing_actions::{PlayingAction, PlayingActionType}; diff --git a/client/src/city_ui.rs b/client/src/city_ui.rs index e0a5f99e..43bfc110 100644 --- a/client/src/city_ui.rs +++ b/client/src/city_ui.rs @@ -255,7 +255,7 @@ fn structure_selected( draw_circle_lines(center.x, center.y, size, 3., h.highlight_type.color()); if is_mouse_button_pressed(MouseButton::Left) && is_in_circle(rc.mouse_pos(), center, size) { let ActiveDialog::StructuresRequest(r) = &rc.state.active_dialog else { - panic!("invalid state"); + panic!("Expected StructuresRequest"); }; Some(StateUpdate::OpenDialog(ActiveDialog::StructuresRequest( r.clone().toggle((position, h.structure.clone())), diff --git a/client/src/client_state.rs b/client/src/client_state.rs index 34a1318d..a5d19544 100644 --- a/client/src/client_state.rs +++ b/client/src/client_state.rs @@ -547,7 +547,7 @@ impl State { rotation: r.block.position.rotation, }) } else { - panic!("invalid state"); + panic!("ExploreResolution expected"); } } }; diff --git a/client/src/payment_ui.rs b/client/src/payment_ui.rs index 9569abfd..383a9103 100644 --- a/client/src/payment_ui.rs +++ b/client/src/payment_ui.rs @@ -60,7 +60,7 @@ impl Payment { #[must_use] pub fn new_gain(options: &PaymentOptions, name: &str) -> Payment { - let a = options.default.resource_amount(); + let a = options.default.amount(); let mut available = ResourcePile::empty(); for r in options.possible_resource_types() { available += ResourcePile::of(r, a); diff --git a/server/src/ability_initializer.rs b/server/src/ability_initializer.rs index b0a53324..41d58eac 100644 --- a/server/src/ability_initializer.rs +++ b/server/src/ability_initializer.rs @@ -328,7 +328,7 @@ pub(crate) trait AbilityInitializerSetup: Sized { return; } } - panic!("Invalid state"); + panic!("Expected payment response"); }, ) } @@ -393,7 +393,7 @@ pub(crate) trait AbilityInitializerSetup: Sized { return; } } - panic!("Invalid state"); + panic!("Expected resource reward response"); }, ) } @@ -424,7 +424,7 @@ pub(crate) trait AbilityInitializerSetup: Sized { return; } } - panic!("Invalid state"); + panic!("Boolean request expected"); }, ) } @@ -450,7 +450,7 @@ pub(crate) trait AbilityInitializerSetup: Sized { return (request.choices.clone(), reward); } } - panic!("Invalid state"); + panic!("Advance request expected"); }, request, gain_reward, @@ -478,7 +478,7 @@ pub(crate) trait AbilityInitializerSetup: Sized { return (request.choices.clone(), reward, request.needed.clone()); } } - panic!("Invalid state"); + panic!("Position request expected"); }, request, gain_reward, @@ -506,7 +506,7 @@ pub(crate) trait AbilityInitializerSetup: Sized { return (request.choices.clone(), reward); } } - panic!("Invalid state"); + panic!("Player request expected"); }, request, gain_reward, @@ -534,7 +534,7 @@ pub(crate) trait AbilityInitializerSetup: Sized { return (request.choices.clone(), reward); } } - panic!("Invalid state"); + panic!("Unit type request expected"); }, request, gain_reward, @@ -566,7 +566,7 @@ pub(crate) trait AbilityInitializerSetup: Sized { ); } } - panic!("Invalid state"); + panic!("Units request expected"); }, request, move |game, c| { @@ -598,7 +598,7 @@ pub(crate) trait AbilityInitializerSetup: Sized { return (request.choices.clone(), choices, request.needed.clone()); } } - panic!("Invalid state"); + panic!("Structures request expected"); }, request, structures_selected, diff --git a/server/src/content.rs b/server/src/content.rs index 965477ab..c84b89eb 100644 --- a/server/src/content.rs +++ b/server/src/content.rs @@ -1,27 +1,7 @@ pub mod advances; -mod advances_agriculture; -mod advances_autocracy; -mod advances_construction; -pub mod advances_culture; -mod advances_democracy; -pub mod advances_economy; -mod advances_education; -mod advances_science; -mod advances_seafearing; -mod advances_spirituality; -mod advances_theocracy; -mod advances_warfare; pub mod builtin; pub mod civilizations; pub mod custom_actions; pub mod custom_phase_actions; -mod incidects_civil_war; pub mod incidents; -mod incidents_5; -mod incidents_earthquake; -mod incidents_famine; -mod incidents_good_year; -mod incidents_trade; -mod incidents_trojan; -pub mod trade_routes; pub mod wonders; diff --git a/server/src/content/advances.rs b/server/src/content/advances.rs index 79453a8b..dcc8edc7 100644 --- a/server/src/content/advances.rs +++ b/server/src/content/advances.rs @@ -1,17 +1,31 @@ +mod agriculture; +mod autocracy; +mod construction; +pub mod culture; +mod democracy; +pub mod economy; +mod education; +mod science; +mod seafearing; +mod spirituality; +mod theocracy; +pub mod trade_routes; +mod warfare; + use crate::advance::Advance; use crate::advance::AdvanceBuilder; -use crate::content::advances_agriculture::agriculture; -use crate::content::advances_autocracy::autocracy; -use crate::content::advances_construction::construction; -use crate::content::advances_culture::culture; -use crate::content::advances_democracy::democracy; -use crate::content::advances_economy::economy; -use crate::content::advances_education::education; -use crate::content::advances_science::science; -use crate::content::advances_seafearing::seafaring; -use crate::content::advances_spirituality::spirituality; -use crate::content::advances_theocracy::theocracy; -use crate::content::advances_warfare::warfare; +use crate::content::advances::agriculture::agriculture; +use crate::content::advances::autocracy::autocracy; +use crate::content::advances::construction::construction; +use crate::content::advances::culture::culture; +use crate::content::advances::democracy::democracy; +use crate::content::advances::economy::economy; +use crate::content::advances::education::education; +use crate::content::advances::science::science; +use crate::content::advances::seafearing::seafaring; +use crate::content::advances::spirituality::spirituality; +use crate::content::advances::theocracy::theocracy; +use crate::content::advances::warfare::warfare; use std::vec; //names of advances that need special handling diff --git a/server/src/content/advances_agriculture.rs b/server/src/content/advances/agriculture.rs similarity index 100% rename from server/src/content/advances_agriculture.rs rename to server/src/content/advances/agriculture.rs diff --git a/server/src/content/advances_autocracy.rs b/server/src/content/advances/autocracy.rs similarity index 100% rename from server/src/content/advances_autocracy.rs rename to server/src/content/advances/autocracy.rs diff --git a/server/src/content/advances_construction.rs b/server/src/content/advances/construction.rs similarity index 100% rename from server/src/content/advances_construction.rs rename to server/src/content/advances/construction.rs diff --git a/server/src/content/advances_culture.rs b/server/src/content/advances/culture.rs similarity index 100% rename from server/src/content/advances_culture.rs rename to server/src/content/advances/culture.rs diff --git a/server/src/content/advances_democracy.rs b/server/src/content/advances/democracy.rs similarity index 100% rename from server/src/content/advances_democracy.rs rename to server/src/content/advances/democracy.rs diff --git a/server/src/content/advances_economy.rs b/server/src/content/advances/economy.rs similarity index 97% rename from server/src/content/advances_economy.rs rename to server/src/content/advances/economy.rs index b520e2b7..6d25dc57 100644 --- a/server/src/content/advances_economy.rs +++ b/server/src/content/advances/economy.rs @@ -2,10 +2,10 @@ use crate::ability_initializer::AbilityInitializerSetup; use crate::advance::Bonus::{CultureToken, MoodToken}; use crate::advance::{Advance, AdvanceBuilder}; use crate::city_pieces::Building::Market; +use crate::content::advances::trade_routes::{trade_route_log, trade_route_reward, TradeRoute}; use crate::content::advances::{advance_group_builder, AdvanceGroup, CURRENCY}; use crate::content::custom_actions::CustomActionType::Taxes; use crate::content::custom_phase_actions::ResourceRewardRequest; -use crate::content::trade_routes::{trade_route_log, trade_route_reward, TradeRoute}; use crate::game::Game; use crate::payment::PaymentOptions; use crate::player::Player; diff --git a/server/src/content/advances_education.rs b/server/src/content/advances/education.rs similarity index 100% rename from server/src/content/advances_education.rs rename to server/src/content/advances/education.rs diff --git a/server/src/content/advances_science.rs b/server/src/content/advances/science.rs similarity index 100% rename from server/src/content/advances_science.rs rename to server/src/content/advances/science.rs diff --git a/server/src/content/advances_seafearing.rs b/server/src/content/advances/seafearing.rs similarity index 100% rename from server/src/content/advances_seafearing.rs rename to server/src/content/advances/seafearing.rs diff --git a/server/src/content/advances_spirituality.rs b/server/src/content/advances/spirituality.rs similarity index 100% rename from server/src/content/advances_spirituality.rs rename to server/src/content/advances/spirituality.rs diff --git a/server/src/content/advances_theocracy.rs b/server/src/content/advances/theocracy.rs similarity index 100% rename from server/src/content/advances_theocracy.rs rename to server/src/content/advances/theocracy.rs diff --git a/server/src/content/trade_routes.rs b/server/src/content/advances/trade_routes.rs similarity index 100% rename from server/src/content/trade_routes.rs rename to server/src/content/advances/trade_routes.rs diff --git a/server/src/content/advances_warfare.rs b/server/src/content/advances/warfare.rs similarity index 100% rename from server/src/content/advances_warfare.rs rename to server/src/content/advances/warfare.rs diff --git a/server/src/content/builtin.rs b/server/src/content/builtin.rs index 340dc3bc..032cbe38 100644 --- a/server/src/content/builtin.rs +++ b/server/src/content/builtin.rs @@ -4,8 +4,8 @@ use crate::barbarians::barbarians_bonus; use crate::combat_listeners::{ choose_carried_units_casualties, choose_fighter_casualties, offer_retreat, place_settler, }; -use crate::content::incidents_famine::pestilence_permanent_effect; -use crate::content::incidents_trojan::{ +use crate::content::incidents::famine::pestilence_permanent_effect; +use crate::content::incidents::trojan::{ activate_trojan_horse, anarchy_advance, decide_trojan_horse, solar_eclipse_end_combat, }; use crate::cultural_influence::cultural_influence_resolution; diff --git a/server/src/content/custom_actions.rs b/server/src/content/custom_actions.rs index b643bf4a..e98f3c4e 100644 --- a/server/src/content/custom_actions.rs +++ b/server/src/content/custom_actions.rs @@ -2,8 +2,8 @@ use serde::{Deserialize, Serialize}; use crate::action::Action; use crate::collect::collect; -use crate::content::advances_culture::{execute_sports, execute_theaters}; -use crate::content::advances_economy::collect_taxes; +use crate::content::advances::culture::{execute_sports, execute_theaters}; +use crate::content::advances::economy::collect_taxes; use crate::content::wonders::construct_wonder; use crate::cultural_influence::influence_culture_attempt; use crate::log::{ @@ -128,7 +128,7 @@ impl CustomAction { format!("{} using Free Economy", format_collect_log_item(player, player_name, c)), CustomAction::Sports { city_position, payment } => format!("{player_name} paid {payment} to increase the happiness in {} using Sports", - format_city_happiness_increase(player, *city_position, payment.resource_amount())), + format_city_happiness_increase(player, *city_position, payment.amount())), CustomAction::Taxes(r) => format!("{player_name} paid 1 mood token to collect {r} using Taxes"), CustomAction::Theaters(r) => diff --git a/server/src/content/custom_phase_actions.rs b/server/src/content/custom_phase_actions.rs index 43c661a0..273002c7 100644 --- a/server/src/content/custom_phase_actions.rs +++ b/server/src/content/custom_phase_actions.rs @@ -141,6 +141,9 @@ pub struct CurrentEventPlayer { #[serde(default)] #[serde(skip_serializing_if = "u8::is_zero")] pub sacrifice: u8, + #[serde(default)] + #[serde(skip_serializing_if = "u8::is_zero")] + pub myths_payment: u8, } impl CurrentEventPlayer { @@ -153,6 +156,7 @@ impl CurrentEventPlayer { payment: ResourcePile::empty(), must_reduce_mood: vec![], sacrifice: 0, + myths_payment: 0, } } } @@ -190,17 +194,21 @@ pub struct CurrentEventState { #[serde(default)] #[serde(skip_serializing_if = "Option::is_none")] pub selected_player: Option, + #[serde(default)] + #[serde(skip_serializing_if = "Option::is_none")] + pub selected_position: Option, } impl CurrentEventState { #[must_use] pub fn new(current_player: usize, event_type: CurrentEventType) -> Self { Self { + event_type, players_used: vec![], player: CurrentEventPlayer::new(current_player), barbarians: None, selected_player: None, - event_type, + selected_position: None, } } } diff --git a/server/src/content/incidents.rs b/server/src/content/incidents.rs index 5ad91979..0c03092d 100644 --- a/server/src/content/incidents.rs +++ b/server/src/content/incidents.rs @@ -1,12 +1,18 @@ -use crate::content::incidects_civil_war::civil_wars; -use crate::content::incidents_5::successful_year; -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, population_booms, -}; -use crate::content::incidents_trade::trades; -use crate::content::incidents_trojan::trojan_incidents; +mod civil_war; +mod earthquake; +pub(crate) mod famine; +mod good_year; +mod pandemics; +mod trade; +pub(crate) mod trojan; + +use crate::content::incidents::civil_war::civil_war_incidents; +use crate::content::incidents::earthquake::earthquake_incidents; +use crate::content::incidents::famine::pestilence_incidents; +use crate::content::incidents::good_year::good_years_incidents; +use crate::content::incidents::pandemics::pandemics_incidents; +use crate::content::incidents::trade::trade_incidents; +use crate::content::incidents::trojan::trojan_incidents; use crate::incident::Incident; use itertools::Itertools; use std::vec; @@ -15,25 +21,19 @@ use std::vec; pub(crate) fn get_all() -> Vec { let all = vec![ // 1+ - pestilence(), - epidemics(), - famines(), + pestilence_incidents(), // 9+ - good_years(), - awesome_years(), - fantastic_years(), - // 27+ - population_booms(), + good_years_incidents(), // 29+ - earthquakes(), + earthquake_incidents(), // 34+ - civil_wars(), + civil_war_incidents(), // 41+ trojan_incidents(), // 45+ - trades(), - // 51+ - successful_year(), + trade_incidents(), + // 49+ + pandemics_incidents(), // 18+ // great persons ] diff --git a/server/src/content/incidects_civil_war.rs b/server/src/content/incidents/civil_war.rs similarity index 53% rename from server/src/content/incidects_civil_war.rs rename to server/src/content/incidents/civil_war.rs index 0c6aefab..51328f15 100644 --- a/server/src/content/incidects_civil_war.rs +++ b/server/src/content/incidents/civil_war.rs @@ -1,9 +1,12 @@ use crate::city::MoodState; use crate::content::custom_phase_actions::{new_position_request, PaymentRequest, UnitsRequest}; -use crate::content::incidents_famine::{decrease_mod_and_log, decrease_mood_incident_city}; -use crate::content::incidents_good_year::select_player_to_gain_settler; +use crate::content::incidents::famine::kill_incident_units; +use crate::content::incidents::good_year::select_player_to_gain_settler; use crate::game::Game; -use crate::incident::{Incident, IncidentBaseEffect, IncidentBuilder, PermanentIncidentEffect}; +use crate::incident::{ + decrease_mod_and_log, Incident, IncidentBaseEffect, IncidentBuilder, MoodModifier, + PermanentIncidentEffect, +}; use crate::payment::{PaymentConversion, PaymentConversionType, PaymentOptions}; use crate::player::Player; use crate::player_events::IncidentTarget; @@ -16,7 +19,7 @@ use crate::unit::UnitType; use crate::wonder::draw_wonder_from_pile; use itertools::Itertools; -pub(crate) fn civil_wars() -> Vec { +pub(crate) fn civil_war_incidents() -> Vec { vec![ migration(34), migration(35), @@ -29,23 +32,23 @@ pub(crate) fn civil_wars() -> Vec { } fn migration(id: u8) -> Incident { - let mut b = Incident::builder( + let b = Incident::builder( id, "Migration", - "Select a player to gain 1 settler in one of their cities. Decrease the mood in one of your cities.", + "Select a player to gain 1 settler in one of their cities. \ + Decrease the mood in one of your cities.", IncidentBaseEffect::GoldDeposits, ); - b = select_player_to_gain_settler(b); - b = b.add_myths_payment(IncidentTarget::ActivePlayer, |_game, p| { - u32::from(!non_angry_cites(p).is_empty()) - }); - decrease_mood_incident_city(b, IncidentTarget::ActivePlayer, 0, |game, player_index| { - (non_angry_cites(game.get_player(player_index)), 1) - }) - .build() + select_player_to_gain_settler(b) + .add_decrease_mood( + IncidentTarget::ActivePlayer, + MoodModifier::Decrease, + |p, _game| (non_angry_cites(p), 1), + ) + .build() } -fn non_angry_cites(p: &Player) -> Vec { +pub(crate) fn non_angry_cites(p: &Player) -> Vec { p.cities .iter() .filter(|c| !matches!(c.mood_state, MoodState::Angry)) @@ -54,25 +57,26 @@ fn non_angry_cites(p: &Player) -> Vec { } fn civil_war(id: u8) -> Incident { - let mut b = Incident::builder( + Incident::builder( id, "Civil War", - "Select a non-Happy city with an Infantry: kill the Infantry and decrease the mood. If no such city exists, select a city to decrease the mood.", + "Select a non-Happy city with an Infantry: \ + kill the Infantry and decrease the mood. If no such city exists, \ + select a city to decrease the mood.", IncidentBaseEffect::None, - ); - b = b.add_myths_payment(IncidentTarget::ActivePlayer, |_game, p| { - u32::from(!non_angry_cites(p).is_empty()) - }); - b = decrease_mood_incident_city(b, IncidentTarget::ActivePlayer, 1, |game, player_index| { - if !game.current_event_player().payment.is_empty() { - return (vec![], 0); - } - if non_happy_cites_with_infantry(game.get_player(player_index)).is_empty() { - return (non_angry_cites(game.get_player(player_index)), 1); - } - (vec![], 0) - }); - b = b.add_incident_position_request( + ) + .add_decrease_mood( + IncidentTarget::ActivePlayer, + MoodModifier::Decrease, + |p, _game| { + if non_happy_cites_with_infantry(p).is_empty() { + (non_angry_cites(p), 1) + } else { + (vec![], 0) + } + }, + ) + .add_incident_position_request( IncidentTarget::ActivePlayer, 0, |game, player_index, _incident| { @@ -94,7 +98,7 @@ fn civil_war(id: u8) -> Incident { let position = s.choice[0]; let mood = game.get_any_city(position).mood_state.clone(); if game.current_event_player().payment.is_empty() && !matches!(mood, MoodState::Angry) { - decrease_mod_and_log(game, s); + decrease_mod_and_log(game, s, MoodModifier::Decrease); } let unit = game .get_player(s.player_index) @@ -111,8 +115,8 @@ fn civil_war(id: u8) -> Incident { )); game.kill_unit(unit, s.player_index, None); }, - ); - b.build() + ) + .build() } fn non_happy_cites_with_infantry(p: &Player) -> Vec { @@ -132,7 +136,9 @@ fn revolution() -> Incident { let mut b = Incident::builder( 38, "Revolution", - "You may kill one of your Army units each to avoid the following steps: Step 1: Loose one action (from your next turn if in Status phase). Step 2: Change your government for free if possible.", + "You may kill one of your Army units each to avoid the following steps: \ + Step 1: Loose one action (from your next turn if in Status phase). \ + Step 2: Change your government for free if possible.", IncidentBaseEffect::GoldDeposits, ); b = kill_unit_for_revolution( @@ -192,13 +198,10 @@ fn kill_unit_for_revolution( )) }, |game, s| { - if s.choice.is_empty() { - game.add_info_log_item(&format!("{} did not kill an Army unit", s.player_name)); - return; + kill_incident_units(game, s); + if !s.choice.is_empty() { + game.current_event_mut().player.sacrifice = 1; } - game.add_info_log_item(&format!("{} killed an Army unit", s.player_name)); - game.kill_unit(s.choice[0], s.player_index, None); - game.current_event_mut().player.sacrifice = 1; }, ) } @@ -221,64 +224,92 @@ fn loose_action(game: &mut Game, player: usize) { #[allow(clippy::float_cmp)] fn uprising() -> Incident { - Incident::builder(39, "Uprising", "Pay 1-4 mood or culture tokens if possible. Each token is worth half a point at the end of the game.", IncidentBaseEffect::None) - .add_incident_payment_request( - IncidentTarget::ActivePlayer, - 0, - |game, player_index, _incident| { - let player = game.get_player(player_index); - let mut cost = PaymentOptions::tokens(4); - cost.conversions.push(PaymentConversion::new( - vec![ResourcePile::mood_tokens(1), ResourcePile::culture_tokens(1)], - ResourcePile::empty(), - PaymentConversionType::MayOverpay(3), - )); - player.can_afford(&cost).then_some( - vec![PaymentRequest::new(cost, "Pay 1-4 mood or culture tokens", false)]) - }, - |game, s| { - let player = game.get_player_mut(s.player_index); - let pile = &s.choice[0]; - let v = pile.resource_amount() as f32 / 2_f32; - player.event_victory_points += v; - game.add_info_log_item(&format!("{} paid {} to gain {} victory point{}", s.player_name, pile, v, - if v == 1.0 { "" } else { "s" })); - }, - ) - .build() + Incident::builder( + 39, + "Uprising", + "Pay 1-4 mood or culture tokens if possible. \ + Each token is worth half a point at the end of the game.", + IncidentBaseEffect::None, + ) + .add_incident_payment_request( + IncidentTarget::ActivePlayer, + 0, + |game, player_index, _incident| { + let player = game.get_player(player_index); + let mut cost = PaymentOptions::tokens(4); + cost.conversions.push(PaymentConversion::new( + vec![ + ResourcePile::mood_tokens(1), + ResourcePile::culture_tokens(1), + ], + ResourcePile::empty(), + PaymentConversionType::MayOverpay(3), + )); + player.can_afford(&cost).then_some(vec![PaymentRequest::new( + cost, + "Pay 1-4 mood or culture tokens", + false, + )]) + }, + |game, s| { + let player = game.get_player_mut(s.player_index); + let pile = &s.choice[0]; + let v = pile.amount() as f32 / 2_f32; + player.event_victory_points += v; + game.add_info_log_item(&format!( + "{} paid {} to gain {} victory point{}", + s.player_name, + pile, + v, + if v == 1.0 { "" } else { "s" } + )); + }, + ) + .build() } fn envoy() -> Incident { - Incident::builder(40, - "Envoy", - "Gain 1 idea and 1 culture token. Select another player to gain 1 culture token. Draw the top card from the wonder deck. This card can be taken by anyone instead of drawing from the wonder pile.", - IncidentBaseEffect::BarbariansMove) - .add_simple_incident_listener( - IncidentTarget::ActivePlayer, - 1, - |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)); + Incident::builder( + 40, + "Envoy", + "Gain 1 idea and 1 culture token. \ + Select another player to gain 1 culture token. \ + Draw the top card from the wonder deck. \ + This card can be taken by anyone instead of drawing from the wonder pile.", + IncidentBaseEffect::BarbariansMove, + ) + .add_simple_incident_listener( + IncidentTarget::ActivePlayer, + 1, + |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)); - let wonder_from_pile = draw_wonder_from_pile(game); - if let Some(wonder) = wonder_from_pile { - game.add_info_log_item(&format!("{} is now available to be taken by anyone", - wonder.name)); - game.permanent_incident_effects.push( - PermanentIncidentEffect::PublicWonderCard(wonder.name)); - } - }, - ) - .add_incident_player_request( - "Select a player to gain 1 culture token", - |_p, _| true, - 0, - |game, s| { - let p = s.choice; - game.add_info_log_item(&format!("{} was selected to gain 1 culture token.", - game.player_name(p))); - game.get_player_mut(p).gain_resources(ResourcePile::culture_tokens(1)); - }) - .build() + let wonder_from_pile = draw_wonder_from_pile(game); + if let Some(wonder) = wonder_from_pile { + game.add_info_log_item(&format!( + "{} is now available to be taken by anyone", + wonder.name + )); + game.permanent_incident_effects + .push(PermanentIncidentEffect::PublicWonderCard(wonder.name)); + } + }, + ) + .add_incident_player_request( + "Select a player to gain 1 culture token", + |_p, _| true, + 0, + |game, s| { + let p = s.choice; + game.add_info_log_item(&format!( + "{} was selected to gain 1 culture token.", + game.player_name(p) + )); + game.get_player_mut(p) + .gain_resources(ResourcePile::culture_tokens(1)); + }, + ) + .build() } diff --git a/server/src/content/incidents_earthquake.rs b/server/src/content/incidents/earthquake.rs similarity index 57% rename from server/src/content/incidents_earthquake.rs rename to server/src/content/incidents/earthquake.rs index 5ead9c0c..73a1cfe2 100644 --- a/server/src/content/incidents_earthquake.rs +++ b/server/src/content/incidents/earthquake.rs @@ -6,15 +6,14 @@ use crate::content::custom_phase_actions::{ is_selected_structures_valid, new_position_request, SelectedStructure, Structure, StructuresRequest, }; -use crate::content::incidents_famine::decrease_mood_incident_city; use crate::content::wonders::get_wonder; use crate::game::Game; -use crate::incident::{Incident, IncidentBaseEffect, IncidentBuilder}; +use crate::incident::{Incident, IncidentBaseEffect, MoodModifier}; use crate::player_events::{IncidentInfo, IncidentTarget}; use crate::position::Position; use itertools::Itertools; -pub(crate) fn earthquakes() -> Vec { +pub(crate) fn earthquake_incidents() -> Vec { vec![ volcano(), earthquake(30, "Earthquake", IncidentTarget::ActivePlayer), @@ -28,71 +27,88 @@ fn volcano() -> Incident { Incident::builder( 29, "Vulcan", - "If you have at least 4 cities: Select one of your cities. Kill all units in the city. Remove all structures (center, buildings, wonders) from the game permanently. Wonder effects are lost (exception: Pyramids). The city center and buildings are worth 2 points each (according to the last owner), wonders as usual.", - IncidentBaseEffect::None) - .add_incident_position_request( - IncidentTarget::ActivePlayer, - 0, - |game, player_index, _incident| { - let p = game.get_player(player_index); - let cities = p - .cities - .iter() - .map(|c| c.position) - .collect_vec(); - (cities.len() >= 4) - .then_some(new_position_request( - cities, - 1..=1, - "Select a city to be destroyed", - )) - }, - |game, s| { - let pos = s.choice[0]; - let player_index = s.player_index; - game.add_info_log_item(&format!( - "{} selected city {} to be destroyed", - s.player_name, pos - )); - let city = game.get_player(player_index).get_city(pos); - let buildings = city.pieces.buildings(None); - let wonders = city.pieces.wonders.iter().map(|w| w.name.clone()).collect_vec(); - for b in buildings { - destroy_building(game, b, pos); - } - for wonder in wonders { - destroy_wonder(game, pos, &wonder); - } - destroy_city_center(game, pos); - }, - ).build() + "If you have at least 4 cities: \ + Select one of your cities. \ + Kill all units in the city. \ + Remove all structures (center, buildings, wonders) from the game permanently. \ + Wonder effects are lost (exception: Pyramids). \ + The city center and buildings are worth 2 points each (according to the last owner), \ + wonders as usual.", + IncidentBaseEffect::None, + ) + .add_incident_position_request( + IncidentTarget::ActivePlayer, + 0, + |game, player_index, _incident| { + let p = game.get_player(player_index); + let cities = p.cities.iter().map(|c| c.position).collect_vec(); + (cities.len() >= 4).then_some(new_position_request( + cities, + 1..=1, + "Select a city to be destroyed", + )) + }, + |game, s| { + let pos = s.choice[0]; + let player_index = s.player_index; + game.add_info_log_item(&format!( + "{} selected city {} to be destroyed", + s.player_name, pos + )); + let city = game.get_player(player_index).get_city(pos); + let buildings = city.pieces.buildings(None); + let wonders = city + .pieces + .wonders + .iter() + .map(|w| w.name.clone()) + .collect_vec(); + for b in buildings { + destroy_building(game, b, pos); + } + for wonder in wonders { + destroy_wonder(game, pos, &wonder); + } + destroy_city_center(game, pos); + }, + ) + .build() } fn earthquake(id: u8, name: &str, target: IncidentTarget) -> Incident { - let mut b = Incident::builder( + Incident::builder( id, name, - "If you have at least 3 cities: Select 1-3 structures (center, buildings, wonders) in your cities and remove them from the game permanently. Wonder effects are lost (exception: Pyramids). The mood of all affected cities is reduced. The city center and buildings are worth 2 points each (according to the last owner), wonders as usual.", IncidentBaseEffect::None) - .add_incident_structures_request( - target, - 11, - |game, player_index, _incident| { - let p = game.get_player(player_index); - let cities = &p.cities; - (cities.len() >= 3) - .then_some(structures_request(cities)) - }, - |game, s| { - apply_earthquake(game, s); - }, - ) - .add_myths_payment(target, move |game, _p| { - game.current_event_player().must_reduce_mood.len() as u32 - }); - b = earthquake_mood_city(b, 0); - b = earthquake_mood_city(b, 1); - b = earthquake_mood_city(b, 2); - b.build() + "If you have at least 3 cities: \ + Select 1-3 structures (center, buildings, wonders) in your cities \ + and remove them from the game permanently. \ + Wonder effects are lost (exception: Pyramids). \ + The mood of all affected cities is reduced. \ + The city center and buildings are worth 2 points each \ + (according to the last owner), wonders as usual.", + IncidentBaseEffect::None, + ) + .add_incident_structures_request( + target, + 11, + |game, player_index, _incident| { + let p = game.get_player(player_index); + let cities = &p.cities; + (cities.len() >= 3).then_some(structures_request(cities)) + }, + |game, s| { + apply_earthquake(game, s); + }, + ) + .add_decrease_mood( + IncidentTarget::AllPlayers, + MoodModifier::Decrease, + move |_p, game| { + let c = &game.current_event_player().must_reduce_mood; + (c.clone(), c.len() as u8) + }, + ) + .build() } fn apply_earthquake(game: &mut Game, s: &SelectedChoice, IncidentInfo>) { @@ -215,34 +231,16 @@ fn destroy_wonder(game: &mut Game, position: Position, name: &str) { )); } -fn earthquake_mood_city(b: IncidentBuilder, i: usize) -> IncidentBuilder { - decrease_mood_incident_city( - b, - IncidentTarget::AllPlayers, - i as i32, - move |game, _player_index| { - let p = game.current_event_player(); - if p.payment.resource_amount() as usize + i >= p.must_reduce_mood.len() { - return (vec![], 0); - } - - (game.current_event_player().must_reduce_mood.clone(), 1) - }, - ) -} - fn flood(id: u8, name: &str, target: IncidentTarget) -> Incident { - let b = Incident::builder( + Incident::builder( id, name, - "Select one of your cities that is adjacent to water. Decrease the mood in that city.", + "Select one of your cities that is adjacent to water. \ + Decrease the mood in that city.", IncidentBaseEffect::None, ) - .add_myths_payment(target, move |game, p| { - u32::from(!non_angry_shore_cites(game, p.index).is_empty()) - }); - decrease_mood_incident_city(b, target, 0, |game, player_index| { - (non_angry_shore_cites(game, player_index), 1) + .add_decrease_mood(target, MoodModifier::Decrease, |p, game| { + (non_angry_shore_cites(game, p.index), 1) }) .build() } diff --git a/server/src/content/incidents/famine.rs b/server/src/content/incidents/famine.rs new file mode 100644 index 00000000..c866c889 --- /dev/null +++ b/server/src/content/incidents/famine.rs @@ -0,0 +1,243 @@ +use crate::ability_initializer::{AbilityInitializerSetup, SelectedChoice}; +use crate::city::{City, MoodState}; +use crate::content::builtin::Builtin; +use crate::content::custom_phase_actions::UnitsRequest; +use crate::game::Game; +use crate::incident::{Incident, IncidentBaseEffect, MoodModifier, PermanentIncidentEffect}; +use crate::player::Player; +use crate::player_events::{IncidentInfo, IncidentTarget}; +use crate::playing_actions::PlayingActionType; +use crate::position::Position; +use crate::resource_pile::ResourcePile; +use itertools::Itertools; +use std::vec; + +pub(crate) fn pestilence_incidents() -> Vec { + let mut r = vec![pestilence(), epidemics()]; + r.extend(famines()); + r +} + +fn pestilence() -> Incident { + Incident::builder( + 1, + "Pestilence", + "Every player with 2 or more cities: \ + Choose 1 (or 2 if you have Roads, Navigation, or Trade Routes) cities \ + and decrease the mood by 1 in each of them. \ + You must choose cities where this is possible. \ + You cannot construct buildings or wonders until you research Sanitation.", + IncidentBaseEffect::None, + ) + .set_protection_advance("Sanitation") + .add_decrease_mood( + IncidentTarget::AllPlayers, + MoodModifier::Decrease, + move |p, game| { + if !pestilence_applies(p) { + return (vec![], 0); + } + + let needed = if additional_sanitation_damage(p) { + 2 + } else { + 1 + } - game.current_event_player().payment.amount() as u8; + + ( + p.cities + .iter() + .filter(|c| !matches!(c.mood_state, MoodState::Angry)) + .map(|c| c.position) + .collect_vec(), + needed, + ) + }, + ) + .add_simple_incident_listener(IncidentTarget::ActivePlayer, 0, |game, _, _, _| { + game.permanent_incident_effects + .push(PermanentIncidentEffect::Pestilence); + }) + .build() +} + +fn pestilence_applies(player: &Player) -> bool { + player.cities.len() >= 2 +} + +pub(crate) fn additional_sanitation_damage(p: &Player) -> bool { + p.has_advance("Roads") || p.has_advance("Navigation") || p.has_advance("Trade Routes") +} + +pub(crate) fn pestilence_permanent_effect() -> Builtin { + Builtin::builder( + "Pestilence", + "You cannot construct buildings or wonders until you research Sanitation.", + ) + .add_player_event_listener( + |event| &mut event.is_playing_action_available, + 1, + |available, game, i| { + let player = game.get_player(i.player); + if game + .permanent_incident_effects + .contains(&PermanentIncidentEffect::Pestilence) + && matches!(i.action_type, PlayingActionType::Construct) + && !player.has_advance("Sanitation") + { + *available = false; + } + }, + ) + .build() +} + +fn epidemics() -> Incident { + Incident::builder( + 2, + "Epidemics", + "Every player with 2 or more units: \ + Choose 1 (or 2 if you have Roads, Navigation, or Trade Routes) units and kill them.", + IncidentBaseEffect::None, + ) + .set_protection_advance("Sanitation") + .add_incident_units_request( + IncidentTarget::AllPlayers, + 0, + |game, player_index, _incident| { + let p = game.get_player(player_index); + let units = p.units.iter().map(|u| u.id).collect_vec(); + let needed = if additional_sanitation_damage(p) { + 2 + } else { + 1 + }; + if units.len() <= 2 { + None + } else { + Some(UnitsRequest::new( + player_index, + units, + needed..=needed, + "Select units to kill", + )) + } + }, + |game, s| { + kill_incident_units(game, s); + }, + ) + .build() +} + +pub(crate) fn kill_incident_units(game: &mut Game, s: &SelectedChoice, IncidentInfo>) { + if s.choice.is_empty() { + game.add_info_log_item(&format!("{} declined to kill units", s.player_name)); + return; + } + + let p = game.get_player(s.player_index); + game.add_info_log_item(&format!( + "{} killed units: {}", + p.get_name(), + s.choice + .iter() + .map(|u| { + let unit = p.get_unit(*u); + format!("{:?} at {}", unit.unit_type, unit.position) + }) + .join(", ") + )); + for u in &s.choice { + game.kill_unit(*u, s.player_index, None); + } +} + +fn famines() -> Vec { + vec![ + common_famine(3, false), + common_famine(4, false), + common_famine(5, false), + common_famine(6, true), + common_famine(7, true), + common_famine(8, true), + ] +} + +fn common_famine(id: u8, severe: bool) -> Incident { + let (name, amount) = if severe { + ("Severe famine", 2) + } else { + ("Famine", 1) + }; + + let description = &format!( + "You must pay up to {amount} food (gold is not possible). If you cannot pay the full amount, make 1 city Angry." + ); + famine( + id, + name, + description, + IncidentTarget::ActivePlayer, + IncidentBaseEffect::BarbariansMove, + move |_, _| amount, + |_| true, + |_, _| true, + ) +} + +pub(crate) fn famine( + id: u8, + name: &str, + description: &str, + target: IncidentTarget, + incident_base_effect: IncidentBaseEffect, + amount: impl Fn(&Player, &Game) -> u8 + Clone + 'static, + player_pred: impl Fn(&Player) -> bool + Clone + 'static, + city_pred: impl Fn(&City, &Game) -> bool + Clone + 'static, +) -> Incident { + let player_pred2 = player_pred.clone(); + let city_pred2 = city_pred.clone(); + Incident::builder(id, name, description, incident_base_effect) + .set_protection_advance("Irrigation") + .add_simple_incident_listener(target, 11, move |game, player_index, player_name, _| { + // we lose the food regardless of the outcome + let p = game.get_player(player_index); + if !player_pred.clone()(p) { + return; + } + + let needed = amount(p, game); + let lost = needed.min(p.resources.food as u8) as u32; + + if lost == needed as u32 { + // only avoid anger if full amount is paid + game.current_event_mut().player.payment = ResourcePile::food(lost); + } + + game.get_player_mut(player_index) + .lose_resources(ResourcePile::food(lost)); + + game.add_info_log_item(&format!("{player_name} lost {lost} food to Famine",)); + }) + .add_decrease_mood(target, MoodModifier::MakeAngry, move |p, game| { + if player_pred2(p) && game.current_event_player().payment.is_empty() { + (famine_targets(p, game, city_pred2.clone()), 1) + } else { + (vec![], 0) + } + }) + .build() +} + +fn famine_targets( + p: &Player, + game: &Game, + city_pred: impl Fn(&City, &Game) -> bool, +) -> Vec { + p.cities + .iter() + .filter(|c| !matches!(c.mood_state, MoodState::Angry) && city_pred(c, game)) + .map(|c| c.position) + .collect_vec() +} diff --git a/server/src/content/incidents_good_year.rs b/server/src/content/incidents/good_year.rs similarity index 90% rename from server/src/content/incidents_good_year.rs rename to server/src/content/incidents/good_year.rs index ae3c0925..51c34391 100644 --- a/server/src/content/incidents_good_year.rs +++ b/server/src/content/incidents/good_year.rs @@ -12,7 +12,15 @@ enum GoodYearType { Distribute, } -pub(crate) fn good_years() -> Vec { +pub(crate) fn good_years_incidents() -> Vec { + let mut r = good_years(); + r.extend(awesome_years()); + r.extend(fantastic_years()); + r.extend(population_booms()); + r +} + +fn good_years() -> Vec { vec![ good_year( Incident::builder( @@ -47,7 +55,7 @@ pub(crate) fn good_years() -> Vec { ] } -pub(crate) fn awesome_years() -> Vec { +fn awesome_years() -> Vec { vec![ good_year( Incident::builder( @@ -82,7 +90,7 @@ pub(crate) fn awesome_years() -> Vec { ] } -pub(crate) fn fantastic_years() -> Vec { +fn fantastic_years() -> Vec { vec![ good_year( Incident::builder( @@ -160,7 +168,7 @@ fn good_year(mut builder: IncidentBuilder, amount: u32, good_year_type: &GoodYea builder.build() } -pub(crate) fn population_booms() -> Vec { +fn population_booms() -> Vec { vec![ population_boom(27, IncidentBaseEffect::BarbariansSpawn), population_boom(28, IncidentBaseEffect::BarbariansMove), @@ -168,14 +176,20 @@ pub(crate) fn population_booms() -> Vec { } fn population_boom(id: u8, effect: IncidentBaseEffect) -> Incident { - let mut b = Incident::builder(id, "Population Boom", "-", effect); + let mut b = Incident::builder( + id, + "Population Boom", + "Gain 1 settler in one of your cities. \ + Select another player to gain 1 settler on one of their cities.", + effect, + ); 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", + "Select another player to gain 1 settler on one of their cities", |p, _| p.available_units().settlers > 0 && !p.cities.is_empty(), 12, |game, c| { diff --git a/server/src/content/incidents/pandemics.rs b/server/src/content/incidents/pandemics.rs new file mode 100644 index 00000000..6ee0a552 --- /dev/null +++ b/server/src/content/incidents/pandemics.rs @@ -0,0 +1,284 @@ +use crate::city::City; +use crate::content::custom_phase_actions::{ + PaymentRequest, PositionRequest, ResourceRewardRequest, UnitsRequest, +}; +use crate::content::incidents::famine::{ + additional_sanitation_damage, famine, kill_incident_units, +}; +use crate::game::Game; +use crate::incident::{Incident, IncidentBaseEffect, MoodModifier}; +use crate::map::{Map, Terrain}; +use crate::payment::PaymentOptions; +use crate::player::Player; +use crate::player_events::IncidentTarget; +use crate::position::Position; +use crate::resource::ResourceType; +use itertools::Itertools; + +pub(crate) fn pandemics_incidents() -> Vec { + vec![ + pandemics(), + black_death(), + vermin(), + draught(), + fire(), + successful_year(), + ] +} + +fn pandemics() -> Incident { + // todo can also discard any cards to pay the cost + Incident::builder( + 49, + "Pandemics", + "Every player loses an amount resources, tokens, cards, \ + or units equal to the half of the number of their cities (rounded down).", + IncidentBaseEffect::BarbariansMove, + ) + .set_protection_advance("Sanitation") + .add_incident_units_request( + IncidentTarget::AllPlayers, + 1, + |game, p, _i| { + let player = game.get_player(p); + let needed = pandemics_cost(player); + let choices = player.units.iter().map(|u| u.id).collect_vec(); + let min = needed.saturating_sub(player.resources.amount() as u8); + let max = needed.min(choices.len() as u8); + Some(UnitsRequest::new( + p, + choices, + min..=max, + "Select units to lose", + )) + }, + |game, s| { + kill_incident_units(game, s); + game.current_event_mut().player.sacrifice = s.choice.len() as u8; + }, + ) + .add_incident_payment_request( + IncidentTarget::AllPlayers, + 0, + |game, p, _i| { + let player = game.get_player(p); + let resources = player.resources.amount() as u8; + let needed = + (pandemics_cost(player) - game.current_event_mut().player.sacrifice).min(resources); + Some(vec![PaymentRequest::new( + PaymentOptions::sum(needed as u32, &ResourceType::all()), + "Select resources to lose", + false, + )]) + }, + |game, s| { + game.add_info_log_item(&format!("{} lost {}", s.player_name, s.choice[0])); + }, + ) + .build() +} + +fn pandemics_cost(player: &Player) -> u8 { + (player.cities.len() / 2) as u8 +} + +fn black_death() -> Incident { + Incident::builder( + 50, + "Black Death", + "Every player with at least 4 units: \ + Lose 1 unit for every 4 units they have (rounded down). \ + If you have Roads, Navigation, or Trade Routes, you lose 1 additional unit. \ + Gain 1 victory point for every unit lost.", + IncidentBaseEffect::None, + ) + .add_incident_units_request( + IncidentTarget::AllPlayers, + 0, + |game, p, _i| { + let player = game.get_player(p); + let units = player.units.iter().map(|u| u.id).collect_vec(); + if units.len() < 4 { + return None; + }; + + let mut needed = (units.len() / 4) as u8; + if additional_sanitation_damage(player) { + needed += 1; + } + + Some(UnitsRequest::new( + p, + units, + needed..=needed, + "Select units to lose", + )) + }, + |game, s| { + kill_incident_units(game, s); + let vp = s.choice.len() as f32; + game.add_info_log_item(&format!("{} gained {} victory points", s.player_name, vp)); + game.get_player_mut(s.player_index).event_victory_points += vp; + }, + ) + .build() +} + +fn vermin() -> Incident { + famine(51, + "Famine: Vermin", + "Every player with Storage: Pay 1 food (gold not allowed). If you cannot pay, make 1 city Angry.", + IncidentTarget::AllPlayers, + IncidentBaseEffect::None, + |_, _| 1, + |p| p.has_advance("Storage"), + |_, _| true, + ) +} + +fn draught() -> Incident { + famine(52, + "Famine: Draught", + "Pay 1 food for every city on or adjacent to Barren Land (up to 3 food, gold not allowed). If you cannot pay the full amount, make 1 of those cities Angry.", + IncidentTarget::ActivePlayer, + IncidentBaseEffect::None, + |p, game| p.cities.iter().filter(|c| on_or_adjacent_to_barren(c, game)).count().min(3) as u8, + |_| true, + on_or_adjacent_to_barren, + ) +} + +fn on_or_adjacent_to_barren(c: &City, game: &Game) -> bool { + game.map.get(c.position) == Some(&Terrain::Barren) + || c.position + .neighbors() + .iter() + .any(|p| game.map.get(*p) == Some(&Terrain::Barren)) +} + +fn fire() -> Incident { + Incident::builder( + 53, + "Fire", + "Select one of your cities that is placed on a Forest. \ + Decrease the mood in that city, and all cities adjacent that are part of the same forest, \ + regardless of who owns them. If you have no cities on a Forest, loose 1 wood.", + IncidentBaseEffect::GoldDeposits, + ) + .add_incident_position_request( + IncidentTarget::ActivePlayer, + 11, + |game, p, _i| { + let player = game.get_player(p); + let cities = player + .cities + .iter() + .filter(|c| game.map.get(c.position) == Some(&Terrain::Forest)) + .map(|c| c.position) + .collect_vec(); + let name = game.player_name(p); + if cities.is_empty() { + if player.resources.wood > 0 { + game.add_info_log_item(&format!("{name} lost 1 wood")); + return None; + } + game.add_info_log_item(&format!( + "{name} has no cities on a Forest and no wood to lose" + )); + return None; + } + Some(PositionRequest::new( + cities, + 1..=1, + "Select a city to set on fire", + )) + }, + |game, s| { + game.current_event_mut().selected_position = Some(s.choice[0]); + }, + ) + .add_decrease_mood( + IncidentTarget::AllPlayers, + MoodModifier::Decrease, + |p, game| { + let b = burning_cities(p, game); + let a = b.len() as u8; + (b, a) + }, + ) + .build() +} + +fn burning_cities(p: &Player, game: &Game) -> Vec { + if let Some(pos) = game.current_event().selected_position { + let mut fire = vec![]; + spread_fire(pos, &game.map, &mut fire); + p.cities + .iter() + .filter(|c| fire.contains(&c.position)) + .map(|c| c.position) + .collect_vec() + } else { + vec![] + } +} + +fn spread_fire(p: Position, map: &Map, fire: &mut Vec) { + if fire.contains(&p) { + return; + } + if map.get(p) == Some(&Terrain::Forest) { + fire.push(p); + for n in p.neighbors() { + spread_fire(n, map, fire); + } + } +} + +pub(crate) fn successful_year() -> Incident { + Incident::builder( + 54, + "A successful year", + "All players with the fewest cities gains 1 food for every city \ + they have less than the player with the most cities. \ + If everyone has the same number of cities, all players gain 1 food.", + IncidentBaseEffect::PiratesSpawnAndRaid, + ) + .add_incident_resource_request( + IncidentTarget::AllPlayers, + 0, + |game, player_index, _incident| { + let player_to_city_num = game + .players + .iter() + .map(|p| p.cities.len()) + .collect::>(); + + let min_cities = player_to_city_num.iter().min().unwrap_or(&0); + let max_cities = player_to_city_num.iter().max().unwrap_or(&0); + if min_cities == max_cities { + return Some(ResourceRewardRequest::new( + PaymentOptions::sum(1, &[ResourceType::Food]), + "-".to_string(), + )); + } + + let cities = game.players[player_index].cities.len(); + if cities == *min_cities { + Some(ResourceRewardRequest::new( + PaymentOptions::sum((max_cities - min_cities) as u32, &[ResourceType::Food]), + "-".to_string(), + )) + } else { + None + } + }, + |_game, s| { + vec![format!( + "{} gained {} from A successful year", + s.player_name, s.choice + )] + }, + ) + .build() +} diff --git a/server/src/content/incidents/trade.rs b/server/src/content/incidents/trade.rs new file mode 100644 index 00000000..81b8f1d0 --- /dev/null +++ b/server/src/content/incidents/trade.rs @@ -0,0 +1,228 @@ +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 trade_incidents() -> 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 similarity index 90% rename from server/src/content/incidents_trojan.rs rename to server/src/content/incidents/trojan.rs index 665c9582..40047c8d 100644 --- a/server/src/content/incidents_trojan.rs +++ b/server/src/content/incidents/trojan.rs @@ -105,14 +105,15 @@ fn solar_eclipse() -> Incident { Incident::builder( 41, "Solar Eclipse", - "The next land battle will end after the first round (retreat if not finished). The winner gains 1 victory point (defender if draw).", + "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, _, _, _| { - game.permanent_incident_effects - .push(PermanentIncidentEffect::SolarEclipse); - }) - .build() + .add_simple_incident_listener(IncidentTarget::ActivePlayer, 0, |game, _, _, _| { + game.permanent_incident_effects + .push(PermanentIncidentEffect::SolarEclipse); + }) + .build() } pub(crate) fn solar_eclipse_end_combat() -> Builtin { @@ -170,10 +171,18 @@ fn anarchy() -> Incident { Incident::builder( 44, "Anarchy", - "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.", + "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()); @@ -181,7 +190,8 @@ fn anarchy() -> Incident { p.event_victory_points += lost as f32; if lost > 0 { game.add_info_log_item(&format!( - "{player_name} lost {lost} government advances due to Anarchy - adding {lost} victory points", + "{player_name} lost {lost} government advances due to Anarchy -\ + adding {lost} victory points", )); } @@ -190,8 +200,9 @@ fn anarchy() -> Incident { player: player_index, advances_lost: lost, })); - }) - .build() + }, + ) + .build() } pub(crate) fn anarchy_advance() -> Builtin { diff --git a/server/src/content/incidents_5.rs b/server/src/content/incidents_5.rs deleted file mode 100644 index 6730f80f..00000000 --- a/server/src/content/incidents_5.rs +++ /dev/null @@ -1,51 +0,0 @@ -use crate::content::custom_phase_actions::ResourceRewardRequest; -use crate::incident::{Incident, IncidentBaseEffect}; -use crate::payment::PaymentOptions; -use crate::player_events::IncidentTarget; -use crate::resource::ResourceType; - -pub(crate) fn successful_year() -> Vec { - vec![Incident::builder( - 54, - "A successful year", - "-", - IncidentBaseEffect::PiratesSpawnAndRaid, - ) - .add_incident_resource_request( - IncidentTarget::AllPlayers, - 0, - |game, player_index, _incident| { - let player_to_city_num = game - .players - .iter() - .map(|p| p.cities.len()) - .collect::>(); - - let min_cities = player_to_city_num.iter().min().unwrap_or(&0); - let max_cities = player_to_city_num.iter().max().unwrap_or(&0); - if min_cities == max_cities { - return Some(ResourceRewardRequest::new( - PaymentOptions::sum(1, &[ResourceType::Food]), - "-".to_string(), - )); - } - - let cities = game.players[player_index].cities.len(); - if cities == *min_cities { - Some(ResourceRewardRequest::new( - PaymentOptions::sum((max_cities - min_cities) as u32, &[ResourceType::Food]), - "-".to_string(), - )) - } else { - None - } - }, - |_game, s| { - vec![format!( - "{} gained {} from A successful year", - s.player_name, s.choice - )] - }, - ) - .build()] -} diff --git a/server/src/content/incidents_famine.rs b/server/src/content/incidents_famine.rs deleted file mode 100644 index 0f1a096d..00000000 --- a/server/src/content/incidents_famine.rs +++ /dev/null @@ -1,269 +0,0 @@ -use crate::ability_initializer::{AbilityInitializerSetup, SelectedChoice}; -use crate::city::MoodState; -use crate::content::builtin::Builtin; -use crate::content::custom_phase_actions::{new_position_request, UnitsRequest}; -use crate::game::Game; -use crate::incident::{Incident, IncidentBaseEffect, IncidentBuilder, PermanentIncidentEffect}; -use crate::player::Player; -use crate::player_events::{IncidentInfo, IncidentTarget}; -use crate::playing_actions::PlayingActionType; -use crate::position::Position; -use crate::resource_pile::ResourcePile; -use itertools::Itertools; -use std::vec; - -pub(crate) fn pestilence() -> Vec { - let mut builder = Incident::builder( - 1, - "Pestilence", - "Every player with 2 or more cities: Choose 1 (or 2 if you have Roads, Navigation, or Trade Routes) cities and decrease the mood by 1 in each of them. You must choose cities where this is possible. You cannot construct buildings or wonders until you research Sanitation.", - IncidentBaseEffect::None) - .set_protection_advance("Sanitation"); - builder = builder.add_myths_payment(IncidentTarget::AllPlayers, |_game, p| { - if pestilence_applies(p) { - if additional_sanitation_damage(p) { - 2 - } else { - 1 - } - } else { - 0 - } - }); - builder = pestilence_city(builder, 1); - builder = - builder.add_simple_incident_listener(IncidentTarget::ActivePlayer, 0, |game, _, _, _| { - game.permanent_incident_effects - .push(PermanentIncidentEffect::Pestilence); - }); - vec![builder.build()] -} - -fn pestilence_applies(player: &Player) -> bool { - player.cities.len() >= 2 -} - -fn additional_sanitation_damage(p: &Player) -> bool { - p.has_advance("Roads") || p.has_advance("Navigation") || p.has_advance("Trade Routes") -} - -pub(crate) fn pestilence_permanent_effect() -> Builtin { - Builtin::builder( - "Pestilence", - "You cannot construct buildings or wonders until you research Sanitation.", - ) - .add_player_event_listener( - |event| &mut event.is_playing_action_available, - 1, - |available, game, i| { - let player = game.get_player(i.player); - if game - .permanent_incident_effects - .contains(&PermanentIncidentEffect::Pestilence) - && matches!(i.action_type, PlayingActionType::Construct) - && !player.has_advance("Sanitation") - { - *available = false; - } - }, - ) - .build() -} - -fn pestilence_city(b: IncidentBuilder, priority: i32) -> IncidentBuilder { - decrease_mood_incident_city( - b, - IncidentTarget::AllPlayers, - priority, - move |game, player_index| { - let p = game.get_player(player_index); - if !pestilence_applies(p) { - return (vec![], 0); - } - - let needed = if additional_sanitation_damage(p) { - 2 - } else { - 1 - } - game.current_event_player().payment.resource_amount() as u8; - - ( - p.cities - .iter() - .filter(|c| !matches!(c.mood_state, MoodState::Angry)) - .map(|c| c.position) - .collect_vec(), - needed, - ) - }, - ) -} - -pub(crate) fn decrease_mood_incident_city( - b: IncidentBuilder, - target: IncidentTarget, - priority: i32, - cities: impl Fn(&Game, usize) -> (Vec, u8) + 'static + Clone, -) -> IncidentBuilder { - b.add_incident_position_request( - target, - priority, - move |game, player_index, _incident| { - let (cities, needed) = cities(game, player_index); - Some(new_position_request( - cities, - needed..=needed, - "Select a city to decrease the mood", - )) - }, - |game, s| { - decrease_mod_and_log(game, s); - }, - ) -} - -pub(crate) fn decrease_mod_and_log( - game: &mut Game, - s: &SelectedChoice, IncidentInfo>, -) { - let pos = s.choice[0]; - game.add_info_log_item(&format!( - "{} decreased the mood in city {}", - s.player_name, pos - )); - game.get_player_mut(s.player_index) - .get_city_mut(pos) - .decrease_mood_state(); -} - -pub(crate) fn epidemics() -> Vec { - vec![Incident::builder( - 2, - "Epidemics", - "Every player with 2 or more units: Choose 1 (or 2 if you have Roads, Navigation, or Trade Routes) units and kill them.", - IncidentBaseEffect::None) - .set_protection_advance("Sanitation") - .add_incident_units_request(IncidentTarget::AllPlayers, 0, |game, player_index, _incident| { - let p = game - .get_player(player_index); - let units = p - .units - .iter() - .map(|u| u.id) - .collect_vec(); - let needed = if additional_sanitation_damage(p) - { - 2 - } else { - 1 - }; - if units.len() <= 2 { - None - } else { - Some(UnitsRequest::new( - player_index, - units, - needed..=needed, - "Select units to kill", - )) - } - }, - |game, s| { - let p = game.get_player(s.player_index); - game.add_info_log_item(&format!( - "{} killed units: {}", - p.get_name(), - s.choice.iter().map(|u| { - let unit = p - .get_unit(*u); - format!("{:?} at {}", unit.unit_type, unit.position) - }).join(", ") - )); - for u in &s.choice { - game.kill_unit(*u, s.player_index, None); - } - }, - ).build()] -} - -pub(crate) fn famines() -> Vec { - vec![ - famine(3, false), - famine(4, false), - famine(5, false), - famine(6, true), - famine(7, true), - famine(8, true), - ] -} - -pub(crate) fn famine(id: u8, severe: bool) -> Incident { - let (name, amount) = if severe { - ("Severe famine", 2) - } else { - ("Famine", 1) - }; - - Incident::builder( - id, - name, - &format!( - "You must pay up to {amount} food (gold is not possible). If you cannot pay the full amount, make 1 city Angry." - ), - IncidentBaseEffect::BarbariansMove, - ) - .set_protection_advance("Irrigation") - .add_myths_payment(IncidentTarget::ActivePlayer, move |_game, p| { - u32::from(famine_applies(p, amount)) - }) - .add_incident_position_request( - IncidentTarget::ActivePlayer, - 0, - move |game, player_index, _incident| { - let p = game.get_player(player_index); - let applies = famine_applies(p, amount); - let lost = amount.min(p.resources.food as i32) as u32; - - // we lose the food regardless of the outcome - game.get_player_mut(player_index) - .lose_resources(ResourcePile::food(lost)); - - game.add_info_log_item(&format!( - "{} lost {} food to Famine", - game.player_name(player_index), - lost - )); - - if applies && game.current_event_player().payment.is_empty() { - Some(new_position_request( - famine_targets(game.get_player(player_index)), - 1..=1, - "Select a city to make Angry", - )) - } else { - None - } - }, - |game, s| { - let p = game.get_player_mut(s.player_index); - let pos = s.choice[0]; - p.get_city_mut(pos) - .mood_state = MoodState::Angry; - let name = p.get_name(); - game.add_info_log_item(&format!("{name} made city {pos} Angry")); - }, - ) - .build() -} - -fn famine_applies(p: &Player, amount: i32) -> bool { - p.resources.food < amount as u32 && !famine_targets(p).is_empty() -} - -fn famine_targets(p: &Player) -> Vec { - p.cities - .iter() - .filter(|c| !matches!(c.mood_state, MoodState::Angry)) - .map(|c| c.position) - .collect_vec() -} diff --git a/server/src/content/incidents_trade.rs b/server/src/content/incidents_trade.rs deleted file mode 100644 index e78996e7..00000000 --- a/server/src/content/incidents_trade.rs +++ /dev/null @@ -1,207 +0,0 @@ -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/incident.rs b/server/src/incident.rs index 02902c60..3f38745b 100644 --- a/server/src/incident.rs +++ b/server/src/incident.rs @@ -1,6 +1,7 @@ use crate::ability_initializer::{AbilityInitializerBuilder, AbilityListeners}; use crate::ability_initializer::{AbilityInitializerSetup, SelectedChoice}; use crate::barbarians::{barbarians_move, barbarians_spawn, no_units_present}; +use crate::city::MoodState; use crate::content::custom_phase_actions::{ new_position_request, CurrentEventType, PaymentRequest, PlayerRequest, PositionRequest, ResourceRewardRequest, SelectedStructure, StructuresRequest, UnitTypeRequest, UnitsRequest, @@ -135,6 +136,12 @@ impl IncidentFilter { } } +#[derive(Clone, Copy)] +pub(crate) enum MoodModifier { + Decrease, + MakeAngry, +} + pub struct IncidentBuilder { id: u8, pub name: String, @@ -393,15 +400,25 @@ impl IncidentBuilder { ) } - pub(crate) fn add_myths_payment( + pub(crate) fn add_decrease_mood( self, - role: IncidentTarget, - amount: impl Fn(&Game, &Player) -> u32 + 'static + Clone, + target: IncidentTarget, + mood_modifier: MoodModifier, + cities: impl Fn(&Player, &Game) -> (Vec, u8) + 'static + Clone, ) -> Self { - let name = self.name.clone(); + let cities2 = cities.clone(); + self.add_myths_payment(target, mood_modifier, move |g, p| cities(p, g).1 as u32) + .decrease_mood(target, mood_modifier, cities2) + } + fn add_myths_payment( + self, + target: IncidentTarget, + mood_modifier: MoodModifier, + amount: impl Fn(&Game, &Player) -> u32 + 'static + Clone, + ) -> Self { self.add_incident_payment_request( - role, + target, 10, move |game, player_index, _incident| { let p = game.get_player(player_index); @@ -417,9 +434,14 @@ impl IncidentBuilder { PaymentConversionType::MayOverpay(needed), )); + let action = match mood_modifier { + MoodModifier::Decrease => "reducing the mood", + MoodModifier::MakeAngry => "making it Angry", + }; + Some(vec![PaymentRequest::new( options, - "You may pay 1 mood token for each city to avoid reducing its mood", + &format!("You may pay 1 mood token for each city to avoid {action}"), false, )]) } else { @@ -428,8 +450,41 @@ impl IncidentBuilder { }, move |game, s| { let pile = &s.choice[0]; - game.add_info_log_item(&format!("{} paid {} for {}", s.player_name, pile, name)); - game.current_event_mut().player.payment = pile.clone(); + game.current_event_mut().player.myths_payment = pile.amount() as u8; + game.add_info_log_item(&format!( + "{} paid {pile} to avoid the mood change using Myths", + s.player_name + )); + }, + ) + } + + fn decrease_mood( + self, + target: IncidentTarget, + mood_modifier: MoodModifier, + cities: impl Fn(&Player, &Game) -> (Vec, u8) + 'static + Clone, + ) -> Self { + self.add_incident_position_request( + target, + 9, + move |game, p, _incident| { + let (cities, mut needed) = cities(game.get_player(p), game); + needed -= game.current_event_player().myths_payment; + + let action = match mood_modifier { + MoodModifier::Decrease => "decrease the mood", + MoodModifier::MakeAngry => "make Angry", + }; + + Some(new_position_request( + cities, + needed..=needed, + &format!("Select a city to {action}"), + )) + }, + move |game, s| { + decrease_mod_and_log(game, s, mood_modifier); }, ) } @@ -605,3 +660,36 @@ fn gold_deposits(b: IncidentBuilder) -> IncidentBuilder { }, ) } + +pub(crate) fn decrease_mod_and_log( + game: &mut Game, + s: &SelectedChoice, IncidentInfo>, + mood_modifier: MoodModifier, +) { + for &pos in &s.choice { + let name = &s.player_name; + match mood_modifier { + MoodModifier::Decrease => { + game.get_player_mut(s.player_index) + .get_city_mut(pos) + .decrease_mood_state(); + let mood_state = &game.get_player(s.player_index).get_city(pos).mood_state; + if s.actively_selected { + game.add_info_log_item(&format!( + "{name} selected to decrease the mood in city {pos} to {mood_state:?}", + )); + } else { + game.add_info_log_item(&format!( + "{name} decreased the mood in city {pos} to {mood_state:?}", + )); + } + } + MoodModifier::MakeAngry => { + game.add_info_log_item(&format!("{name} made city {pos} Angry")); + game.get_player_mut(s.player_index) + .get_city_mut(pos) + .mood_state = MoodState::Angry; + } + } + } +} diff --git a/server/src/pirates.rs b/server/src/pirates.rs index fd1e9bbd..d4e571ac 100644 --- a/server/src/pirates.rs +++ b/server/src/pirates.rs @@ -92,7 +92,7 @@ pub(crate) fn pirates_spawn_and_raid(mut builder: IncidentBuilder) -> IncidentBu return None; } - if player.resources.resource_amount() > 0 { + if player.resources.amount() > 0 { game.add_info_log_item(&format!( "{} must pay 1 resource or token to bribe the pirates", player.get_name() diff --git a/server/src/resource_pile.rs b/server/src/resource_pile.rs index fa261a52..ed8488c8 100644 --- a/server/src/resource_pile.rs +++ b/server/src/resource_pile.rs @@ -188,7 +188,7 @@ impl ResourcePile { } #[must_use] - pub fn resource_amount(&self) -> u32 { + pub fn amount(&self) -> u32 { self.food + self.wood + self.ore @@ -200,7 +200,7 @@ impl ResourcePile { #[must_use] pub fn is_empty(&self) -> bool { - self.resource_amount() == 0 + self.amount() == 0 } #[must_use] diff --git a/server/tests/advances_tests.rs b/server/tests/advances_tests.rs index 4a6ed752..65089c90 100644 --- a/server/tests/advances_tests.rs +++ b/server/tests/advances_tests.rs @@ -1,13 +1,13 @@ use crate::common::{illegal_action_test, influence_action, JsonTest, TestAction}; use server::action::{execute_action, Action}; use server::city_pieces::Building::{Fortress, Temple}; +use server::content::advances::trade_routes::find_trade_routes; use server::content::custom_actions::CustomAction; use server::content::custom_actions::CustomAction::{ AbsolutePower, ArtsInfluenceCultureAttempt, CivilRights, ConstructWonder, ForcedLabor, Sports, Taxes, Theaters, VotingIncreaseHappiness, }; use server::content::custom_phase_actions::CurrentEventResponse; -use server::content::trade_routes::find_trade_routes; use server::events::EventOrigin; use server::game::Game; use server::movement::move_units_destinations; diff --git a/server/tests/incident_tests.rs b/server/tests/incident_tests.rs index 232ec53f..a8e08723 100644 --- a/server/tests/incident_tests.rs +++ b/server/tests/incident_tests.rs @@ -150,6 +150,12 @@ fn test_pestilence() { payment: ResourcePile::gold(2), }), ), + TestAction::not_undoable( + 0, + Action::Response(CurrentEventResponse::Payment(vec![ + ResourcePile::mood_tokens(1), + ])), + ), TestAction::not_undoable( 0, Action::Response(CurrentEventResponse::SelectPositions(vec![ @@ -577,3 +583,107 @@ fn test_reformation() { ], ); } + +const PANDEMICS: JsonTest = JsonTest::child("incidents", "pandemics"); + +#[test] +fn test_pandemics() { + PANDEMICS.test( + "pandemics", + vec![ + TestAction::not_undoable( + 0, + Action::Playing(Advance { + advance: String::from("Storage"), + payment: ResourcePile::gold(2), + }), + ), + TestAction::not_undoable( + 0, + Action::Response(CurrentEventResponse::SelectUnits(vec![0])), + ), + TestAction::not_undoable( + 0, + Action::Response(CurrentEventResponse::Payment(vec![ + ResourcePile::culture_tokens(1), + ])), + ), + TestAction::not_undoable( + 1, + Action::Response(CurrentEventResponse::Payment(vec![ + ResourcePile::culture_tokens(1), + ])), + ), + ], + ); +} + +#[test] +fn test_black_death() { + PANDEMICS.test( + "black_death", + vec![ + TestAction::not_undoable( + 0, + Action::Playing(Advance { + advance: String::from("Storage"), + payment: ResourcePile::gold(2), + }), + ), + TestAction::not_undoable( + 0, + Action::Response(CurrentEventResponse::SelectUnits(vec![0])), + ), + ], + ); +} + +#[test] +fn test_vermin() { + PANDEMICS.test( + "vermin", + vec![TestAction::not_undoable( + 0, + Action::Playing(Advance { + advance: String::from("Storage"), + payment: ResourcePile::gold(2), + }), + )], + ); +} + +#[test] +fn test_drought() { + PANDEMICS.test( + "drought", + vec![TestAction::not_undoable( + 0, + Action::Playing(Advance { + advance: String::from("Storage"), + payment: ResourcePile::gold(2), + }), + )], + ); +} + +#[test] +fn test_fire() { + PANDEMICS.test( + "fire", + vec![ + TestAction::not_undoable( + 0, + Action::Playing(Advance { + advance: String::from("Storage"), + payment: ResourcePile::gold(2), + }), + ), + TestAction::not_undoable( + 0, + Action::Response(CurrentEventResponse::SelectPositions(vec![ + Position::from_offset("B2"), + ])), + ), + ], + ); +} diff --git a/server/tests/test_games/incidents/civil_war/civil_war.outcome.json b/server/tests/test_games/incidents/civil_war/civil_war.outcome.json index 0dddfbc2..81ce1159 100644 --- a/server/tests/test_games/incidents/civil_war/civil_war.outcome.json +++ b/server/tests/test_games/incidents/civil_war/civil_war.outcome.json @@ -279,7 +279,7 @@ ], [ "A new game event has been triggered: Civil War", - "Player1 decreased the mood in city C2", + "Player1 decreased the mood in city C2 to Angry", "Player1 killed an Infantry in C2" ] ], diff --git a/server/tests/test_games/incidents/civil_war/migration.outcome.json b/server/tests/test_games/incidents/civil_war/migration.outcome.json index cb9b4157..15aaf685 100644 --- a/server/tests/test_games/incidents/civil_war/migration.outcome.json +++ b/server/tests/test_games/incidents/civil_war/migration.outcome.json @@ -255,7 +255,7 @@ "A new game event has been triggered: Migration", "Player1 gained 2 gold from a Gold Mine", "Player2 was selected to gain 1 settler.", - "Player1 decreased the mood in city C2", + "Player1 decreased the mood in city C2 to Neutral", "Player2 gained 1 settler in A4" ] ], diff --git a/server/tests/test_games/incidents/civil_war/revolution.outcome1.json b/server/tests/test_games/incidents/civil_war/revolution.outcome1.json index 9acd9353..8ac970d9 100644 --- a/server/tests/test_games/incidents/civil_war/revolution.outcome1.json +++ b/server/tests/test_games/incidents/civil_war/revolution.outcome1.json @@ -1,5 +1,5 @@ { -"state": "Playing", + "state": "Playing", "current_events": [ { "event_type": { @@ -322,7 +322,7 @@ "Player1 gained 2 gold from a Gold Mine" ], [ - "Player1 killed an Army unit" + "Player1 killed units: Elephant at C2" ] ], "undo_limit": 2, @@ -353,4 +353,4 @@ "incidents_left": [ 38 ] -} +} \ No newline at end of file diff --git a/server/tests/test_games/incidents/civil_war/revolution.outcome2.json b/server/tests/test_games/incidents/civil_war/revolution.outcome2.json index 8a23b7e9..03a71a41 100644 --- a/server/tests/test_games/incidents/civil_war/revolution.outcome2.json +++ b/server/tests/test_games/incidents/civil_war/revolution.outcome2.json @@ -1,5 +1,5 @@ { -"state": "Playing", + "state": "Playing", "current_events": [ { "event_type": { @@ -321,10 +321,10 @@ "Player1 gained 2 gold from a Gold Mine" ], [ - "Player1 killed an Army unit" + "Player1 killed units: Elephant at C2" ], [ - "Player1 did not kill an Army unit" + "Player1 declined to kill units" ] ], "undo_limit": 3, @@ -355,4 +355,4 @@ "incidents_left": [ 38 ] -} +} \ No newline at end of file diff --git a/server/tests/test_games/incidents/civil_war/revolution.outcome3.json b/server/tests/test_games/incidents/civil_war/revolution.outcome3.json index 35b14f84..b2b54bf1 100644 --- a/server/tests/test_games/incidents/civil_war/revolution.outcome3.json +++ b/server/tests/test_games/incidents/civil_war/revolution.outcome3.json @@ -1,5 +1,5 @@ { -"state": "Playing", + "state": "Playing", "players": [ { "name": null, @@ -310,10 +310,10 @@ "Player1 gained 2 gold from a Gold Mine" ], [ - "Player1 killed an Army unit" + "Player1 killed units: Elephant at C2" ], [ - "Player1 did not kill an Army unit" + "Player1 declined to kill units" ], [ "Player1 changed their government from Democracy to Theocracy", @@ -347,4 +347,4 @@ "dropped_players": [], "wonders_left": [], "wonder_amount_left": 1 -} +} \ No newline at end of file diff --git a/server/tests/test_games/incidents/earthquake/earthquake.outcome1.json b/server/tests/test_games/incidents/earthquake/earthquake.outcome1.json index 41af32f0..58bbc542 100644 --- a/server/tests/test_games/incidents/earthquake/earthquake.outcome1.json +++ b/server/tests/test_games/incidents/earthquake/earthquake.outcome1.json @@ -1,5 +1,5 @@ { -"state": "Playing", + "state": "Playing", "current_events": [ { "event_type": { @@ -32,7 +32,7 @@ } ] }, - "name": "You may pay 1 mood token for each city to avoid reducing its mood", + "name": "You may pay 1 mood token for each city to avoid reducing the mood", "optional": false } ] @@ -384,4 +384,4 @@ "incidents_left": [ 31 ] -} +} \ No newline at end of file diff --git a/server/tests/test_games/incidents/earthquake/earthquake.outcome2.json b/server/tests/test_games/incidents/earthquake/earthquake.outcome2.json index a71f7a2c..ceb6a53f 100644 --- a/server/tests/test_games/incidents/earthquake/earthquake.outcome2.json +++ b/server/tests/test_games/incidents/earthquake/earthquake.outcome2.json @@ -1,5 +1,5 @@ { -"state": "Playing", + "state": "Playing", "current_events": [ { "event_type": { @@ -376,7 +376,7 @@ "Player1 gained 2 points for the city center at B2" ], [ - "Player1 paid 1 mood token for Heavy Earthquake" + "Player1 paid 1 mood token to avoid the mood change using Myths" ] ], "undo_limit": 3, @@ -407,4 +407,4 @@ "incidents_left": [ 31 ] -} +} \ No newline at end of file diff --git a/server/tests/test_games/incidents/earthquake/earthquake.outcome3.json b/server/tests/test_games/incidents/earthquake/earthquake.outcome3.json index a3fe34ad..f4781ef3 100644 --- a/server/tests/test_games/incidents/earthquake/earthquake.outcome3.json +++ b/server/tests/test_games/incidents/earthquake/earthquake.outcome3.json @@ -330,7 +330,7 @@ "Player1 gained 2 points for the city center at B2" ], [ - "Player1 paid 1 mood token for Heavy Earthquake" + "Player1 paid 1 mood token to avoid the mood change using Myths" ], [ "Player1 gained 2 points for the Fortress at A1", diff --git a/server/tests/test_games/incidents/earthquake/flood.outcome.json b/server/tests/test_games/incidents/earthquake/flood.outcome.json index bd3e20cd..a2bf29f9 100644 --- a/server/tests/test_games/incidents/earthquake/flood.outcome.json +++ b/server/tests/test_games/incidents/earthquake/flood.outcome.json @@ -1,5 +1,5 @@ { -"state": "Playing", + "state": "Playing", "current_events": [ { "event_type": { @@ -8,9 +8,9 @@ } }, "player": 0, - "last_priority_used": 0, + "last_priority_used": 9, "handler": { - "priority": 0, + "priority": 9, "request": { "SelectPositions": { "choices": [ @@ -345,4 +345,4 @@ "incidents_left": [ 33 ] -} +} \ No newline at end of file diff --git a/server/tests/test_games/incidents/earthquake/flood.outcome1.json b/server/tests/test_games/incidents/earthquake/flood.outcome1.json index 6c0800c3..b85a03fb 100644 --- a/server/tests/test_games/incidents/earthquake/flood.outcome1.json +++ b/server/tests/test_games/incidents/earthquake/flood.outcome1.json @@ -1,5 +1,5 @@ { -"state": "Playing", + "state": "Playing", "current_events": [ { "event_type": { @@ -11,9 +11,9 @@ 0 ], "player": 1, - "last_priority_used": 0, + "last_priority_used": 9, "handler": { - "priority": 0, + "priority": 9, "request": { "SelectPositions": { "choices": [ @@ -328,7 +328,7 @@ "A new game event has been triggered: Heavy Flood" ], [ - "Player1 decreased the mood in city C2" + "Player1 selected to decrease the mood in city C2 to Neutral" ] ], "undo_limit": 2, @@ -359,4 +359,4 @@ "incidents_left": [ 33 ] -} +} \ No newline at end of file diff --git a/server/tests/test_games/incidents/earthquake/flood.outcome2.json b/server/tests/test_games/incidents/earthquake/flood.outcome2.json index 82373595..ed6c2830 100644 --- a/server/tests/test_games/incidents/earthquake/flood.outcome2.json +++ b/server/tests/test_games/incidents/earthquake/flood.outcome2.json @@ -304,10 +304,10 @@ "A new game event has been triggered: Heavy Flood" ], [ - "Player1 decreased the mood in city C2" + "Player1 selected to decrease the mood in city C2 to Neutral" ], [ - "Player2 decreased the mood in city A1" + "Player2 selected to decrease the mood in city A1 to Neutral" ] ], "undo_limit": 3, diff --git a/server/tests/test_games/incidents/famine/pestilence.json b/server/tests/test_games/incidents/famine/pestilence.json index b07de214..52b66809 100644 --- a/server/tests/test_games/incidents/famine/pestilence.json +++ b/server/tests/test_games/incidents/famine/pestilence.json @@ -30,6 +30,13 @@ "angry_activation": false, "position": "A1" }, + { + "city_pieces": {}, + "mood_state": "Happy", + "activations": 0, + "angry_activation": false, + "position": "A2" + }, { "city_pieces": {}, "mood_state": "Happy", @@ -86,6 +93,8 @@ "advances": [ "Farming", "Mining", + "Myths", + "Roads", "Tactics" ], "unlocked_special_advance": [], @@ -198,7 +207,7 @@ ], [ "A2", - "Water" + "Fertile" ], [ "A3", diff --git a/server/tests/test_games/incidents/famine/pestilence.outcome.json b/server/tests/test_games/incidents/famine/pestilence.outcome.json index 0357abcb..2c3f6cb8 100644 --- a/server/tests/test_games/incidents/famine/pestilence.outcome.json +++ b/server/tests/test_games/incidents/famine/pestilence.outcome.json @@ -1,5 +1,5 @@ { -"state": "Playing", + "state": "Playing", "current_events": [ { "event_type": { @@ -8,21 +8,34 @@ } }, "player": 0, - "last_priority_used": 1, + "last_priority_used": 10, "handler": { - "priority": 1, + "priority": 10, "request": { - "SelectPositions": { - "choices": [ - "A1", - "C2" - ], - "needed": { - "start": 1, - "end": 1 - }, - "description": "Select a city to decrease the mood" - } + "Payment": [ + { + "cost": { + "default": { + "mood_tokens": 2 + }, + "conversions": [ + { + "from": [ + { + "mood_tokens": 1 + } + ], + "to": {}, + "type": { + "MayOverpay": 2 + } + } + ] + }, + "name": "You may pay 1 mood token for each city to avoid reducing the mood", + "optional": false + } + ] }, "origin": { "Incident": 1 @@ -60,6 +73,13 @@ "angry_activation": false, "position": "A1" }, + { + "city_pieces": {}, + "mood_state": "Happy", + "activations": 0, + "angry_activation": false, + "position": "A2" + }, { "city_pieces": {}, "mood_state": "Happy", @@ -116,6 +136,8 @@ "advances": [ "Farming", "Mining", + "Myths", + "Roads", "Storage", "Tactics" ], @@ -229,7 +251,7 @@ ], [ "A2", - "Water" + "Fertile" ], [ "A3", @@ -321,4 +343,4 @@ "incidents_left": [ 1 ] -} +} \ No newline at end of file diff --git a/server/tests/test_games/incidents/famine/pestilence.outcome1.json b/server/tests/test_games/incidents/famine/pestilence.outcome1.json index 5c7adf5f..23af272b 100644 --- a/server/tests/test_games/incidents/famine/pestilence.outcome1.json +++ b/server/tests/test_games/incidents/famine/pestilence.outcome1.json @@ -1,5 +1,5 @@ { -"state": "Playing", + "state": "Playing", "current_events": [ { "event_type": { @@ -7,43 +7,29 @@ "active_player": 0 } }, - "players_used": [ - 0 - ], - "player": 1, - "last_priority_used": 10, + "player": 0, + "last_priority_used": 9, "handler": { - "priority": 10, + "priority": 9, "request": { - "Payment": [ - { - "cost": { - "default": { - "mood_tokens": 1 - }, - "conversions": [ - { - "from": [ - { - "mood_tokens": 1 - } - ], - "to": {}, - "type": { - "MayOverpay": 1 - } - } - ] - }, - "name": "You may pay 1 mood token for each city to avoid reducing its mood", - "optional": false - } - ] + "SelectPositions": { + "choices": [ + "A1", + "A2", + "C2" + ], + "needed": { + "start": 1, + "end": 1 + }, + "description": "Select a city to decrease the mood" + } }, "origin": { "Incident": 1 } - } + }, + "myths_payment": 1 } ], "players": [ @@ -56,7 +42,7 @@ "ore": 6, "ideas": 5, "gold": 5, - "mood_tokens": 8, + "mood_tokens": 7, "culture_tokens": 7 }, "resource_limit": { @@ -71,11 +57,18 @@ "cities": [ { "city_pieces": {}, - "mood_state": "Neutral", + "mood_state": "Happy", "activations": 0, "angry_activation": false, "position": "A1" }, + { + "city_pieces": {}, + "mood_state": "Happy", + "activations": 0, + "angry_activation": false, + "position": "A2" + }, { "city_pieces": {}, "mood_state": "Happy", @@ -132,6 +125,8 @@ "advances": [ "Farming", "Mining", + "Myths", + "Roads", "Storage", "Tactics" ], @@ -245,7 +240,7 @@ ], [ "A2", - "Water" + "Fertile" ], [ "A3", @@ -299,8 +294,10 @@ { "action": { "Response": { - "SelectPositions": [ - "A1" + "Payment": [ + { + "mood_tokens": 1 + } ] } } @@ -316,7 +313,7 @@ "A new game event has been triggered: Pestilence" ], [ - "Player1 decreased the mood in city A1" + "Player1 paid 1 mood token to avoid the mood change using Myths" ] ], "undo_limit": 2, @@ -348,8 +345,5 @@ "wonder_amount_left": 1, "incidents_left": [ 1 - ], - "permanent_incident_effects": [ - "Pestilence" ] -} +} \ No newline at end of file diff --git a/server/tests/test_games/incidents/famine/pestilence.outcome2.json b/server/tests/test_games/incidents/famine/pestilence.outcome2.json index 2caeacc6..290f559b 100644 --- a/server/tests/test_games/incidents/famine/pestilence.outcome2.json +++ b/server/tests/test_games/incidents/famine/pestilence.outcome2.json @@ -1,5 +1,51 @@ { "state": "Playing", + "current_events": [ + { + "event_type": { + "Incident": { + "active_player": 0 + } + }, + "players_used": [ + 0 + ], + "player": 1, + "last_priority_used": 10, + "handler": { + "priority": 10, + "request": { + "Payment": [ + { + "cost": { + "default": { + "mood_tokens": 1 + }, + "conversions": [ + { + "from": [ + { + "mood_tokens": 1 + } + ], + "to": {}, + "type": { + "MayOverpay": 1 + } + } + ] + }, + "name": "You may pay 1 mood token for each city to avoid reducing the mood", + "optional": false + } + ] + }, + "origin": { + "Incident": 1 + } + } + } + ], "players": [ { "name": null, @@ -10,7 +56,7 @@ "ore": 6, "ideas": 5, "gold": 5, - "mood_tokens": 8, + "mood_tokens": 7, "culture_tokens": 7 }, "resource_limit": { @@ -30,6 +76,13 @@ "angry_activation": false, "position": "A1" }, + { + "city_pieces": {}, + "mood_state": "Happy", + "activations": 0, + "angry_activation": false, + "position": "A2" + }, { "city_pieces": {}, "mood_state": "Happy", @@ -86,6 +139,8 @@ "advances": [ "Farming", "Mining", + "Myths", + "Roads", "Storage", "Tactics" ], @@ -107,7 +162,7 @@ "ore": 7, "ideas": 7, "gold": 7, - "mood_tokens": 6, + "mood_tokens": 7, "culture_tokens": 7 }, "resource_limit": { @@ -199,7 +254,7 @@ ], [ "A2", - "Water" + "Fertile" ], [ "A3", @@ -253,8 +308,10 @@ { "action": { "Response": { - "SelectPositions": [ - "A1" + "Payment": [ + { + "mood_tokens": 1 + } ] } } @@ -262,10 +319,8 @@ { "action": { "Response": { - "Payment": [ - { - "mood_tokens": 1 - } + "SelectPositions": [ + "A1" ] } } @@ -281,10 +336,10 @@ "A new game event has been triggered: Pestilence" ], [ - "Player1 decreased the mood in city A1" + "Player1 paid 1 mood token to avoid the mood change using Myths" ], [ - "Player2 paid 1 mood token for Pestilence" + "Player1 selected to decrease the mood in city A1 to Neutral" ] ], "undo_limit": 3, @@ -314,6 +369,9 @@ "Pyramids" ], "wonder_amount_left": 1, + "incidents_left": [ + 1 + ], "permanent_incident_effects": [ "Pestilence" ] diff --git a/server/tests/test_games/incidents/famine/pestilence.outcome3.json b/server/tests/test_games/incidents/famine/pestilence.outcome3.json new file mode 100644 index 00000000..f3a83b9f --- /dev/null +++ b/server/tests/test_games/incidents/famine/pestilence.outcome3.json @@ -0,0 +1,343 @@ +{ + "state": "Playing", + "players": [ + { + "name": null, + "id": 0, + "resources": { + "food": 1, + "wood": 6, + "ore": 6, + "ideas": 5, + "gold": 5, + "mood_tokens": 7, + "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": "Neutral", + "activations": 0, + "angry_activation": false, + "position": "A1" + }, + { + "city_pieces": {}, + "mood_state": "Happy", + "activations": 0, + "angry_activation": false, + "position": "A2" + }, + { + "city_pieces": {}, + "mood_state": "Happy", + "activations": 0, + "angry_activation": false, + "position": "C2" + } + ], + "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", + "Myths", + "Roads", + "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": 6, + "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": "C1" + }, + { + "city_pieces": {}, + "mood_state": "Happy", + "activations": 0, + "angry_activation": false, + "position": "B3" + } + ], + "units": [ + { + "position": "C1", + "unit_type": "Infantry", + "id": 0 + }, + { + "position": "C1", + "unit_type": "Infantry", + "id": 1 + } + ], + "civilization": "test1", + "active_leader": null, + "available_leaders": [], + "advances": [ + "Farming", + "Mining", + "Myths" + ], + "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": "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", + "Fertile" + ], + [ + "A3", + "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": { + "Payment": [ + { + "mood_tokens": 1 + } + ] + } + } + }, + { + "action": { + "Response": { + "SelectPositions": [ + "A1" + ] + } + } + }, + { + "action": { + "Response": { + "Payment": [ + { + "mood_tokens": 1 + } + ] + } + } + } + ], + "action_log_index": 4, + "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: Pestilence" + ], + [ + "Player1 paid 1 mood token to avoid the mood change using Myths" + ], + [ + "Player1 selected to decrease the mood in city A1 to Neutral" + ], + [ + "Player2 paid 1 mood token to avoid the mood change using Myths" + ] + ], + "undo_limit": 4, + "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, + "permanent_incident_effects": [ + "Pestilence" + ] +} \ No newline at end of file diff --git a/server/tests/test_games/incidents/pandemics/black_death.json b/server/tests/test_games/incidents/pandemics/black_death.json new file mode 100644 index 00000000..f57c395a --- /dev/null +++ b/server/tests/test_games/incidents/pandemics/black_death.json @@ -0,0 +1,293 @@ +{ + "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, + "temple": 1 + }, + "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": { + "fortress": 0 + }, + "mood_state": "Happy", + "activations": 0, + "angry_activation": false, + "position": "A1" + }, + { + "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": {}, + "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": [ + 50 + ] +} \ No newline at end of file diff --git a/server/tests/test_games/incidents/pandemics/black_death.outcome.json b/server/tests/test_games/incidents/pandemics/black_death.outcome.json new file mode 100644 index 00000000..82d01b21 --- /dev/null +++ b/server/tests/test_games/incidents/pandemics/black_death.outcome.json @@ -0,0 +1,351 @@ +{ + "state": "Playing", + "current_events": [ + { + "event_type": { + "Incident": { + "active_player": 0 + } + }, + "player": 0, + "last_priority_used": 0, + "handler": { + "priority": 0, + "request": { + "SelectUnits": { + "player": 0, + "choices": [ + 0, + 1, + 3, + 4, + 5, + 6, + 7 + ], + "needed": { + "start": 1, + "end": 1 + }, + "description": "Select units to lose" + } + }, + "origin": { + "Incident": 50 + } + } + } + ], + "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, + "temple": 1 + }, + "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": { + "fortress": 0 + }, + "mood_state": "Happy", + "activations": 0, + "angry_activation": false, + "position": "A1" + }, + { + "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": {}, + "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: Black Death" + ] + ], + "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": [ + 50 + ] +} \ No newline at end of file diff --git a/server/tests/test_games/incidents/pandemics/black_death.outcome1.json b/server/tests/test_games/incidents/pandemics/black_death.outcome1.json new file mode 100644 index 00000000..79e7a270 --- /dev/null +++ b/server/tests/test_games/incidents/pandemics/black_death.outcome1.json @@ -0,0 +1,320 @@ +{ + "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, + "temple": 1 + }, + "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", + "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": 1.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": { + "fortress": 0 + }, + "mood_state": "Happy", + "activations": 0, + "angry_activation": false, + "position": "A1" + }, + { + "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": {}, + "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": { + "SelectUnits": [ + 0 + ] + } + } + } + ], + "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: Black Death" + ], + [ + "Player1 killed units: Infantry at C2", + "Player1 gained 1 victory points" + ] + ], + "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/pandemics/drought.json b/server/tests/test_games/incidents/pandemics/drought.json new file mode 100644 index 00000000..737e6324 --- /dev/null +++ b/server/tests/test_games/incidents/pandemics/drought.json @@ -0,0 +1,277 @@ +{ + "state": "Playing", + "players": [ + { + "name": null, + "id": 0, + "resources": { + "food": 3, + "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, + "temple": 1 + }, + "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": { + "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": { + "fortress": 0 + }, + "mood_state": "Happy", + "activations": 0, + "angry_activation": false, + "position": "A1" + } + ], + "units": [], + "civilization": "test1", + "active_leader": null, + "available_leaders": [], + "advances": [ + "Farming", + "Mining", + "Storage" + ], + "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": "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": [ + 52 + ] +} \ No newline at end of file diff --git a/server/tests/test_games/incidents/pandemics/drought.outcome.json b/server/tests/test_games/incidents/pandemics/drought.outcome.json new file mode 100644 index 00000000..3d7bed07 --- /dev/null +++ b/server/tests/test_games/incidents/pandemics/drought.outcome.json @@ -0,0 +1,297 @@ +{ + "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, + "temple": 1 + }, + "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": { + "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": { + "fortress": 0 + }, + "mood_state": "Happy", + "activations": 0, + "angry_activation": false, + "position": "A1" + } + ], + "units": [], + "civilization": "test1", + "active_leader": null, + "available_leaders": [], + "advances": [ + "Farming", + "Mining", + "Storage" + ], + "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": "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: Famine: Draught", + "Player1 lost 2 food to Famine" + ] + ], + "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 +} \ No newline at end of file diff --git a/server/tests/test_games/incidents/pandemics/fire.json b/server/tests/test_games/incidents/pandemics/fire.json new file mode 100644 index 00000000..b0fcd250 --- /dev/null +++ b/server/tests/test_games/incidents/pandemics/fire.json @@ -0,0 +1,277 @@ +{ + "state": "Playing", + "players": [ + { + "name": null, + "id": 0, + "resources": { + "food": 3, + "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, + "temple": 1 + }, + "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": { + "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": { + "fortress": 0 + }, + "mood_state": "Happy", + "activations": 0, + "angry_activation": false, + "position": "A1" + } + ], + "units": [], + "civilization": "test1", + "active_leader": null, + "available_leaders": [], + "advances": [ + "Farming", + "Mining", + "Storage" + ], + "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": "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", + "Forest" + ], + [ + "A2", + "Forest" + ], + [ + "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": [ + 53 + ] +} \ No newline at end of file diff --git a/server/tests/test_games/incidents/pandemics/fire.outcome.json b/server/tests/test_games/incidents/pandemics/fire.outcome.json new file mode 100644 index 00000000..f5d8a345 --- /dev/null +++ b/server/tests/test_games/incidents/pandemics/fire.outcome.json @@ -0,0 +1,330 @@ +{ + "state": "Playing", + "current_events": [ + { + "event_type": { + "Incident": { + "active_player": 0 + } + }, + "player": 0, + "last_priority_used": 11, + "handler": { + "priority": 11, + "request": { + "SelectPositions": { + "choices": [ + "C2", + "B2" + ], + "needed": { + "start": 1, + "end": 1 + }, + "description": "Select a city to set on fire" + } + }, + "origin": { + "Incident": 53 + } + } + } + ], + "players": [ + { + "name": null, + "id": 0, + "resources": { + "food": 3, + "wood": 6, + "ore": 6, + "ideas": 5, + "gold": 7, + "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, + "temple": 1 + }, + "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": { + "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": { + "fortress": 0 + }, + "mood_state": "Happy", + "activations": 0, + "angry_activation": false, + "position": "A1" + } + ], + "units": [], + "civilization": "test1", + "active_leader": null, + "available_leaders": [], + "advances": [ + "Farming", + "Mining", + "Storage" + ], + "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": "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", + "Forest" + ], + [ + "A2", + "Forest" + ], + [ + "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: Fire", + "Player1 gained 2 gold from a Gold Mine" + ] + ], + "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": [ + 53 + ] +} \ No newline at end of file diff --git a/server/tests/test_games/incidents/pandemics/fire.outcome1.json b/server/tests/test_games/incidents/pandemics/fire.outcome1.json new file mode 100644 index 00000000..7527d999 --- /dev/null +++ b/server/tests/test_games/incidents/pandemics/fire.outcome1.json @@ -0,0 +1,311 @@ +{ + "state": "Playing", + "players": [ + { + "name": null, + "id": 0, + "resources": { + "food": 3, + "wood": 6, + "ore": 6, + "ideas": 5, + "gold": 7, + "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, + "temple": 1 + }, + "mood_state": "Angry", + "activations": 0, + "angry_activation": false, + "position": "C2" + }, + { + "city_pieces": {}, + "mood_state": "Happy", + "activations": 0, + "angry_activation": false, + "position": "C1" + }, + { + "city_pieces": {}, + "mood_state": "Neutral", + "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": { + "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": { + "fortress": 0 + }, + "mood_state": "Neutral", + "activations": 0, + "angry_activation": false, + "position": "A1" + } + ], + "units": [], + "civilization": "test1", + "active_leader": null, + "available_leaders": [], + "advances": [ + "Farming", + "Mining", + "Storage" + ], + "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": "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", + "Forest" + ], + [ + "A2", + "Forest" + ], + [ + "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": { + "SelectPositions": [ + "B2" + ] + } + } + } + ], + "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: Fire", + "Player1 gained 2 gold from a Gold Mine" + ], + [ + "Player1 decreased the mood in city B2 to Neutral", + "Player1 decreased the mood in city C2 to Angry", + "Player2 decreased the mood in city A1 to Neutral" + ] + ], + "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/pandemics/pandemics.json b/server/tests/test_games/incidents/pandemics/pandemics.json new file mode 100644 index 00000000..3523e914 --- /dev/null +++ b/server/tests/test_games/incidents/pandemics/pandemics.json @@ -0,0 +1,293 @@ +{ + "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, + "temple": 1 + }, + "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": { + "fortress": 0 + }, + "mood_state": "Happy", + "activations": 0, + "angry_activation": false, + "position": "A1" + }, + { + "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": {}, + "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": [ + 49 + ] +} \ No newline at end of file diff --git a/server/tests/test_games/incidents/pandemics/pandemics.outcome.json b/server/tests/test_games/incidents/pandemics/pandemics.outcome.json new file mode 100644 index 00000000..ed269721 --- /dev/null +++ b/server/tests/test_games/incidents/pandemics/pandemics.outcome.json @@ -0,0 +1,357 @@ +{ + "state": "Playing", + "current_events": [ + { + "event_type": { + "Incident": { + "active_player": 0 + } + }, + "player": 0, + "last_priority_used": 1, + "handler": { + "priority": 1, + "request": { + "SelectUnits": { + "player": 0, + "choices": [ + 0, + 1, + 3, + 4, + 5, + 6, + 7 + ], + "needed": { + "start": 0, + "end": 2 + }, + "description": "Select units to lose" + } + }, + "origin": { + "Incident": 49 + } + }, + "barbarians": { + "move_units": false + } + } + ], + "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, + "temple": 1 + }, + "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": { + "fortress": 0 + }, + "mood_state": "Happy", + "activations": 0, + "angry_activation": false, + "position": "A1" + }, + { + "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": {}, + "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: Pandemics", + "Base effect: Barbarians move", + "Barbarians cannot move - will try to spawn a new city instead", + "Barbarians cannot spawn a new city" + ] + ], + "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": [ + 49 + ] +} \ No newline at end of file diff --git a/server/tests/test_games/incidents/pandemics/pandemics.outcome1.json b/server/tests/test_games/incidents/pandemics/pandemics.outcome1.json new file mode 100644 index 00000000..b4317fc9 --- /dev/null +++ b/server/tests/test_games/incidents/pandemics/pandemics.outcome1.json @@ -0,0 +1,427 @@ +{ + "state": "Playing", + "current_events": [ + { + "event_type": { + "Incident": { + "active_player": 0 + } + }, + "player": 0, + "last_priority_used": 0, + "handler": { + "priority": 0, + "request": { + "Payment": [ + { + "cost": { + "default": { + "food": 1 + }, + "conversions": [ + { + "from": [ + { + "food": 1 + } + ], + "to": { + "wood": 1 + }, + "type": "Unlimited" + }, + { + "from": [ + { + "wood": 1 + } + ], + "to": { + "ore": 1 + }, + "type": "Unlimited" + }, + { + "from": [ + { + "ore": 1 + } + ], + "to": { + "ideas": 1 + }, + "type": "Unlimited" + }, + { + "from": [ + { + "ideas": 1 + } + ], + "to": { + "gold": 1 + }, + "type": "Unlimited" + }, + { + "from": [ + { + "gold": 1 + } + ], + "to": { + "mood_tokens": 1 + }, + "type": "Unlimited" + }, + { + "from": [ + { + "mood_tokens": 1 + } + ], + "to": { + "culture_tokens": 1 + }, + "type": "Unlimited" + } + ] + }, + "name": "Select resources to lose", + "optional": false + } + ] + }, + "origin": { + "Incident": 49 + } + }, + "sacrifice": 1, + "barbarians": { + "move_units": false + } + } + ], + "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, + "temple": 1 + }, + "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", + "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": { + "fortress": 0 + }, + "mood_state": "Happy", + "activations": 0, + "angry_activation": false, + "position": "A1" + }, + { + "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": {}, + "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": { + "SelectUnits": [ + 0 + ] + } + } + } + ], + "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: Pandemics", + "Base effect: Barbarians move", + "Barbarians cannot move - will try to spawn a new city instead", + "Barbarians cannot spawn a new city" + ], + [ + "Player1 killed units: Infantry at C2" + ] + ], + "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, + "incidents_left": [ + 49 + ] +} \ No newline at end of file diff --git a/server/tests/test_games/incidents/pandemics/pandemics.outcome2.json b/server/tests/test_games/incidents/pandemics/pandemics.outcome2.json new file mode 100644 index 00000000..3c55a311 --- /dev/null +++ b/server/tests/test_games/incidents/pandemics/pandemics.outcome2.json @@ -0,0 +1,443 @@ +{ + "state": "Playing", + "current_events": [ + { + "event_type": { + "Incident": { + "active_player": 0 + } + }, + "players_used": [ + 0 + ], + "player": 1, + "last_priority_used": 0, + "handler": { + "priority": 0, + "request": { + "Payment": [ + { + "cost": { + "default": { + "food": 1 + }, + "conversions": [ + { + "from": [ + { + "food": 1 + } + ], + "to": { + "wood": 1 + }, + "type": "Unlimited" + }, + { + "from": [ + { + "wood": 1 + } + ], + "to": { + "ore": 1 + }, + "type": "Unlimited" + }, + { + "from": [ + { + "ore": 1 + } + ], + "to": { + "ideas": 1 + }, + "type": "Unlimited" + }, + { + "from": [ + { + "ideas": 1 + } + ], + "to": { + "gold": 1 + }, + "type": "Unlimited" + }, + { + "from": [ + { + "gold": 1 + } + ], + "to": { + "mood_tokens": 1 + }, + "type": "Unlimited" + }, + { + "from": [ + { + "mood_tokens": 1 + } + ], + "to": { + "culture_tokens": 1 + }, + "type": "Unlimited" + } + ] + }, + "name": "Select resources to lose", + "optional": false + } + ] + }, + "origin": { + "Incident": 49 + } + }, + "barbarians": { + "move_units": false + } + } + ], + "players": [ + { + "name": null, + "id": 0, + "resources": { + "food": 1, + "wood": 6, + "ore": 6, + "ideas": 5, + "gold": 5, + "mood_tokens": 8, + "culture_tokens": 6 + }, + "resource_limit": { + "food": 7, + "wood": 7, + "ore": 7, + "ideas": 7, + "gold": 7, + "mood_tokens": 7, + "culture_tokens": 7 + }, + "cities": [ + { + "city_pieces": { + "fortress": 0, + "temple": 1 + }, + "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", + "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": { + "fortress": 0 + }, + "mood_state": "Happy", + "activations": 0, + "angry_activation": false, + "position": "A1" + }, + { + "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": {}, + "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": { + "SelectUnits": [ + 0 + ] + } + } + }, + { + "action": { + "Response": { + "Payment": [ + { + "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: Pandemics", + "Base effect: Barbarians move", + "Barbarians cannot move - will try to spawn a new city instead", + "Barbarians cannot spawn a new city" + ], + [ + "Player1 killed units: Infantry at C2" + ], + [ + "Player1 lost 1 culture token" + ] + ], + "undo_limit": 3, + "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": [ + 49 + ] +} \ No newline at end of file diff --git a/server/tests/test_games/incidents/pandemics/pandemics.outcome3.json b/server/tests/test_games/incidents/pandemics/pandemics.outcome3.json new file mode 100644 index 00000000..083b58b2 --- /dev/null +++ b/server/tests/test_games/incidents/pandemics/pandemics.outcome3.json @@ -0,0 +1,350 @@ +{ + "state": "Playing", + "players": [ + { + "name": null, + "id": 0, + "resources": { + "food": 1, + "wood": 6, + "ore": 6, + "ideas": 5, + "gold": 5, + "mood_tokens": 8, + "culture_tokens": 6 + }, + "resource_limit": { + "food": 7, + "wood": 7, + "ore": 7, + "ideas": 7, + "gold": 7, + "mood_tokens": 7, + "culture_tokens": 7 + }, + "cities": [ + { + "city_pieces": { + "fortress": 0, + "temple": 1 + }, + "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", + "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": 6 + }, + "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": "Happy", + "activations": 0, + "angry_activation": false, + "position": "A1" + }, + { + "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": {}, + "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": { + "SelectUnits": [ + 0 + ] + } + } + }, + { + "action": { + "Response": { + "Payment": [ + { + "culture_tokens": 1 + } + ] + } + } + }, + { + "action": { + "Response": { + "Payment": [ + { + "culture_tokens": 1 + } + ] + } + } + } + ], + "action_log_index": 4, + "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: Pandemics", + "Base effect: Barbarians move", + "Barbarians cannot move - will try to spawn a new city instead", + "Barbarians cannot spawn a new city" + ], + [ + "Player1 killed units: Infantry at C2" + ], + [ + "Player1 lost 1 culture token" + ], + [ + "Player2 lost 1 culture token" + ] + ], + "undo_limit": 4, + "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/pandemics/vermin.json b/server/tests/test_games/incidents/pandemics/vermin.json new file mode 100644 index 00000000..b86f8d3d --- /dev/null +++ b/server/tests/test_games/incidents/pandemics/vermin.json @@ -0,0 +1,277 @@ +{ + "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, + "temple": 1 + }, + "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": { + "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": { + "fortress": 0 + }, + "mood_state": "Happy", + "activations": 0, + "angry_activation": false, + "position": "A1" + } + ], + "units": [], + "civilization": "test1", + "active_leader": null, + "available_leaders": [], + "advances": [ + "Farming", + "Mining", + "Storage" + ], + "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": "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": [ + 51 + ] +} \ No newline at end of file diff --git a/server/tests/test_games/incidents/pandemics/vermin.outcome.json b/server/tests/test_games/incidents/pandemics/vermin.outcome.json new file mode 100644 index 00000000..c8200635 --- /dev/null +++ b/server/tests/test_games/incidents/pandemics/vermin.outcome.json @@ -0,0 +1,298 @@ +{ + "state": "Playing", + "players": [ + { + "name": null, + "id": 0, + "resources": { + "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, + "temple": 1 + }, + "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": { + "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": { + "fortress": 0 + }, + "mood_state": "Angry", + "activations": 0, + "angry_activation": false, + "position": "A1" + } + ], + "units": [], + "civilization": "test1", + "active_leader": null, + "available_leaders": [], + "advances": [ + "Farming", + "Mining", + "Storage" + ], + "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": "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: Famine: Vermin", + "Player1 lost 1 food to Famine", + "Player2 lost 0 food to Famine", + "Player2 made city A1 Angry" + ] + ], + "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 +} \ No newline at end of file