From 07954a5ccd81f0b7edddc8d6c76658d29fb25bda Mon Sep 17 00:00:00 2001 From: Sam Maselli Date: Tue, 26 Nov 2024 12:31:17 -0500 Subject: [PATCH] reeducator defense check --- client/src/resources/lang/en_us.json | 14 +++++++------- server/src/game/role/apostle.rs | 2 +- server/src/game/role/detective.rs | 2 +- server/src/game/role/doctor.rs | 1 - server/src/game/role/gossip.rs | 2 +- server/src/game/role/kidnapper.rs | 2 +- server/src/game/role/philosopher.rs | 2 +- server/src/game/role/reeducator.rs | 14 +++++++++++--- server/src/game/role/scarecrow.rs | 2 +- server/src/game/role/warper.rs | 2 +- server/src/game/role/witch.rs | 2 +- server/src/game/win_condition.rs | 6 +++--- 12 files changed, 29 insertions(+), 22 deletions(-) diff --git a/client/src/resources/lang/en_us.json b/client/src/resources/lang/en_us.json index 7369f2c30..b2cb52c94 100644 --- a/client/src/resources/lang/en_us.json +++ b/client/src/resources/lang/en_us.json @@ -1109,7 +1109,7 @@ "wiki.article.standard.insider.title": "Insider", "wiki.article.standard.insider.title:var.0": "Insiders", "wiki.article.standard.insider.title:var.1": "Member", - "wiki.article.standard.insider.text": "An insider is a player who knows the roles of all the other insiders in their group. Insiders also get to talk to each other at night. For example, A typical Godfather is a Syndicate Insider because the godfather has the roles of all syndicate insiders revealed and can talk to them at night. Marionettes are Puppeteer Insiders, but Detectives are not Town Insiders because they don't have a night chat or have all town insiders revealed to them.\n Another name for insider is \"member\".\nSome examples of insider groups are:\n - Syndicate\n - Cult\n - Puppeteer", + "wiki.article.standard.insider.text": "An insider group is a group of players who have the following properties\n - They have the role of all other living insiders in their group revealed to them\n - They have a night chat group with all other living insiders\n\n For example, A typical Godfather is a Syndicate Insider, they have the roles of all syndicate insiders revealed and can talk to them at night.\n Marionettes are Puppeteer Insiders\n Detectives are not often Town Insiders because they don't have a night chat or have all town insiders revealed to them.\n Another name for insider is \"member\". A player is a syndicate insider or syndicate member if they are in the syndicate insider group\nSome examples of insider groups are:\n - Syndicate\n - Cult\n - Puppeteer", "wiki.article.standard.conclusion.title": "Conclusion", "wiki.article.standard.conclusion.title:var.0": "Conclusions", "wiki.article.standard.conclusion.text": "The state of the game when it ends, and who won. Some players win conditions will depend on the final conclusion.\n Some Conclusion Possibilities Are:\n - Town\n - Syndicate\n - Cult\n - Fiends\n - Draw\n\n A player is a town loyalist if their win condition is the following: I win if and only if town is the games conclusion.\n A jesters default win condition does not depend on the games conclusion.", @@ -1331,7 +1331,7 @@ "wiki.article.role.detective.guide": "- At night, select a player to find if they're friends or enemies with the town\n- If a player registers as suspicious, they are enemies with town. You most likely want them voted out.\n- Auras can mess with your results\n - Godfathers have innocent aura", "wiki.article.role.detective.abilities":"* Visit a player to learn if they're friends or enemies with the town", "wiki.article.role.detective.attributes":"* A suspicious aura makes the player with the aura register as suspicious to you\n* An innocent aura makes the player with the aura register as innocent to you", - "wiki.article.role.detective.extra": "Jesters and most neutral role are enemies with town.", + "wiki.article.role.detective.extra": "Most neutral roles like jester are usually friends with town.", "wiki.article.role.lookout.reminder": "At night, select a player to find out who visits them.", "wiki.article.role.lookout.guide": "- At night, select a player to find out who visits them\n\n_For Example_\n- If you select @1 on the night they die, and @2 visited them then it's likely @2 killed them.\n\nYou should tell the town the information you get daily.", @@ -1388,16 +1388,16 @@ "wiki.article.role.tallyClerk.extra":"", "wiki.article.role.doctor.reminder": "At night, select a player to protect from attacks. You can protect yourself once per game.", - "wiki.article.role.doctor.guide": "- At night, select a player to protect\n - Whoever you protect doesn't die if they're attacked\n - You and the player you protected are both told if they are attacked\n- You can select yourself to protect yourself once per game\n- If your target is attacked by a protection-piercing attack they still die\n- You can't select anyone night 1\n\nYou should try to protect people you think are townies who will be attacked.", - "wiki.article.role.doctor.abilities":"* Visit a player to protect for the night\n* You can't select a player night 1", - "wiki.article.role.doctor.attributes":"* If you visit yourself, you lose the ability to select yourself", + "wiki.article.role.doctor.guide": "- At night, select a player to protect\n - Whoever you protect doesn't die if they're attacked\n - You and the player you protected are both told if they are attacked\n- You can select yourself to protect yourself once per game\n- If your target is attacked by a protection-piercing attack they still die\n\nYou should try to protect people you think are townies who will be attacked.", + "wiki.article.role.doctor.abilities":"* Visit a player to protect for the night", + "wiki.article.role.doctor.attributes":"* If you visit yourself, you use the self protect charge, and you can't select yourself again", "wiki.article.role.doctor.extra":"* You are told if your target is attacked\n* Your target is told they are protected only if they get attacked", "wiki.article.role.bodyguard.reminder": "At night, select a player to protect. If they are attacked, you get attacked instead, and you attack the attacker back.", "wiki.article.role.bodyguard.guide": "- Select a player to protect each night\n - If the player you protect is attacked, you are attacked instead, and you attack the attacker back\n - You can't select anyone night 1\n- You can target yourself once per game\n\n- You should try to protect people you think are town loyalists who will be attacked.", "wiki.article.role.bodyguard.abilities":"* Visit another player\n * When any players visit your target with an attacking visit, they are redirected into visiting and attacking you\n * If you redirect an attackers visit into yourself, you attack all attackers\n* Visit yourself\n * You protect yourself tonight\n * You don't redirect any attacks\n * You lose the ability to select yourself", "wiki.article.role.bodyguard.attributes":"* You have an indirect armor-piercing attack", - "wiki.article.role.bodyguard.extra":"* You will be told if your target is attacked\n* Your target is told that they were protected if they were attacked\n* If your target is attacked by multiple visits, you will attack back for each visit", + "wiki.article.role.bodyguard.extra":"* You are told if your target is attacked\n* Your target is told that they were protected if they were attacked\n* If your target is attacked by multiple visits, you will attack back for each visit", "wiki.article.role.cop.reminder": "At night, select a player to protect. You attack a random visitor of the player you protect.", "wiki.article.role.cop.guide": "- At night, select a player to protect\n - You attack one random other player who visits your target\n- You can't use your ability night 1\n\n You should try to protect people you think are townies who will be attacked. You could easily accidentally kill town loyalists, so you should try to make sure no other town loyalists visit the player you protect.", @@ -1592,7 +1592,7 @@ "wiki.article.role.reeducator.reminder": "At night, choose a fellow insider to become a syndicate support role that you choose. Once per game, convert a player to join the syndicate.", "wiki.article.role.reeducator.guide": "- At night, either select a player and syndicate support role\n - If you select a syndicate insider\n - Their role switches to the role you choose\n - If you select someone who isn't a syndicate insider\n - Their role switches to the role you choose\n - They become a syndicate insider\n - They become a syndicate loyalist\n - You can only convert somebody once, if you visit a non syndicate insider again, your ability does nothing\n- If the reeducator is randomly generated, a non reeducator syndicate insider has their role switched to a random town role before the game starts, they never know of this", "wiki.article.role.reeducator.abilities": "- At night, either select a player and syndicate support role\n - If you select a syndicate insider\n - Their role switches to the role you choose\n - If you select someone who isn't a syndicate insider\n - Their role switches to the role you choose\n - They become a syndicate insider\n - They become a syndicate loyalist\n - You can only convert somebody once, if you visit a non syndicate insider again, your ability does nothing\n- If the reeducator is randomly generated, a non reeducator syndicate insider has their role switched to a random town role before the game starts, they never know of this", - "wiki.article.role.reeducator.attributes": "- After you convert somebody, you can't select non-insiders anymore", + "wiki.article.role.reeducator.attributes": "- After you convert somebody, you can't select non-insiders anymore\n- Converting a non-syndicate insider to join the syndicate does a defense check, it has an attack power of basic\n- If the convert is blocked by defense, the charge is not consumed\n- You can't convert a non syndicate insider on night 1", "wiki.article.role.reeducator.extra": "- Players roles switch after night ends, so their ability still happens for tonight\n- You can't select roles that aren't enabled", "wiki.article.role.cupid.guide": "- At night, select 2 players to love link\n* The players are told they are love linked, if one of them dies, the other dies of a broken heart", diff --git a/server/src/game/role/apostle.rs b/server/src/game/role/apostle.rs index c7cfa5be6..a3e1a718f 100644 --- a/server/src/game/role/apostle.rs +++ b/server/src/game/role/apostle.rs @@ -52,7 +52,7 @@ impl RoleStateImpl for Apostle { target_ref.set_night_convert_role_to(game, Some(Role::Zealot.default_state())); InsiderGroupID::Cult.add_player_to_revealed_group(game, target_ref); - target_ref.set_win_condition(game, WinCondition::new_single_resolution_state(GameConclusion::Cult)); + target_ref.set_win_condition(game, WinCondition::new_loyalist(GameConclusion::Cult)); Cult::set_ability_used_last_night(game, Some(CultAbility::Convert)); } diff --git a/server/src/game/role/detective.rs b/server/src/game/role/detective.rs index e0786cf22..9b5018213 100644 --- a/server/src/game/role/detective.rs +++ b/server/src/game/role/detective.rs @@ -52,7 +52,7 @@ impl Detective { }else if player_ref.has_innocent_aura(game){ false }else{ - !player_ref.win_condition(game).can_win_when_resolution_state_reached(GameConclusion::Town) + !player_ref.win_condition(game).friends_with_resolution_state(GameConclusion::Town) } } } \ No newline at end of file diff --git a/server/src/game/role/doctor.rs b/server/src/game/role/doctor.rs index 16e5eb01b..5ed8f2f63 100644 --- a/server/src/game/role/doctor.rs +++ b/server/src/game/role/doctor.rs @@ -73,7 +73,6 @@ impl RoleStateImpl for Doctor { } } fn can_select(self, game: &Game, actor_ref: PlayerReference, target_ref: PlayerReference) -> bool { - game.day_number() > 1 && (actor_ref != target_ref || self.self_heals_remaining > 0) && !crate::game::components::detained::Detained::is_detained(game, actor_ref) && actor_ref.selection(game).is_empty() && diff --git a/server/src/game/role/gossip.rs b/server/src/game/role/gossip.rs index 3150bb4a3..eddf30c99 100644 --- a/server/src/game/role/gossip.rs +++ b/server/src/game/role/gossip.rs @@ -59,7 +59,7 @@ impl Gossip { }else if visited_player.has_innocent_aura(game){ false }else{ - !WinCondition::can_win_together(player_ref.win_condition(game), visited_player.win_condition(game)) + !WinCondition::are_friends(player_ref.win_condition(game), visited_player.win_condition(game)) } ) } diff --git a/server/src/game/role/kidnapper.rs b/server/src/game/role/kidnapper.rs index ef24349b5..03f311335 100644 --- a/server/src/game/role/kidnapper.rs +++ b/server/src/game/role/kidnapper.rs @@ -158,7 +158,7 @@ impl RoleStateImpl for Kidnapper { .filter(|p|p.alive(game)) .filter(|p|p.keeps_game_running(game)) .all(|p| - WinCondition::can_win_together(&p.win_condition(game), actor_ref.win_condition(game)) + WinCondition::are_friends(&p.win_condition(game), actor_ref.win_condition(game)) ) { diff --git a/server/src/game/role/philosopher.rs b/server/src/game/role/philosopher.rs index f04bba327..d1758be34 100644 --- a/server/src/game/role/philosopher.rs +++ b/server/src/game/role/philosopher.rs @@ -64,7 +64,7 @@ impl Philosopher{ }else if a.has_innocent_aura(game) || b.has_innocent_aura(game){ false }else{ - !WinCondition::can_win_together(a.win_condition(game), b.win_condition(game)) + !WinCondition::are_friends(a.win_condition(game), b.win_condition(game)) } } } \ No newline at end of file diff --git a/server/src/game/role/reeducator.rs b/server/src/game/role/reeducator.rs index 512360909..f299e9aaf 100644 --- a/server/src/game/role/reeducator.rs +++ b/server/src/game/role/reeducator.rs @@ -3,6 +3,8 @@ use serde::Serialize; use vec1::vec1; use crate::game::ability_input::AbilityInput; +use crate::game::attack_power::AttackPower; +use crate::game::chat::ChatMessageVariant; use crate::game::components::detained::Detained; use crate::game::components::insider_group::InsiderGroupID; use crate::game::game_conclusion::GameConclusion; @@ -72,12 +74,17 @@ impl RoleStateImpl for Reeducator { target_ref.set_night_convert_role_to(game, Some(new_state)); - }else if self.convert_charges_remaining { + }else if self.convert_charges_remaining && game.day_number() > 1{ + + if target_ref.night_defense(game).can_block(AttackPower::Basic) { + actor_ref.push_night_message(game, ChatMessageVariant::YourConvertFailed); + return + } InsiderGroupID::Mafia.add_player_to_revealed_group(game, target_ref); target_ref.set_win_condition( game, - WinCondition::new_single_resolution_state(crate::game::game_conclusion::GameConclusion::Mafia) + WinCondition::new_loyalist(crate::game::game_conclusion::GameConclusion::Mafia) ); target_ref.set_night_convert_role_to(game, Some(new_state)); @@ -97,7 +104,8 @@ impl RoleStateImpl for Reeducator { ( ( !InsiderGroupID::in_same_revealed_group(game, actor_ref, target_ref) && - self.convert_charges_remaining + self.convert_charges_remaining && + game.day_number() > 1 ) || InsiderGroupID::in_same_revealed_group(game, actor_ref, target_ref) ) diff --git a/server/src/game/role/scarecrow.rs b/server/src/game/role/scarecrow.rs index 00241391f..2b582cd2b 100644 --- a/server/src/game/role/scarecrow.rs +++ b/server/src/game/role/scarecrow.rs @@ -59,7 +59,7 @@ impl RoleStateImpl for Scarecrow { .filter(|p|p.alive(game)) .filter(|p|p.keeps_game_running(game)) .all(|p| - WinCondition::can_win_together(&p.win_condition(game), actor_ref.win_condition(game)) + WinCondition::are_friends(&p.win_condition(game), actor_ref.win_condition(game)) ) { diff --git a/server/src/game/role/warper.rs b/server/src/game/role/warper.rs index 978d18ded..403492d9d 100644 --- a/server/src/game/role/warper.rs +++ b/server/src/game/role/warper.rs @@ -60,7 +60,7 @@ impl RoleStateImpl for Warper { .filter(|p|p.alive(game)) .filter(|p|p.keeps_game_running(game)) .all(|p| - WinCondition::can_win_together(&p.win_condition(game), actor_ref.win_condition(game)) + WinCondition::are_friends(&p.win_condition(game), actor_ref.win_condition(game)) ) { diff --git a/server/src/game/role/witch.rs b/server/src/game/role/witch.rs index aa3e02c93..f22dd0bea 100644 --- a/server/src/game/role/witch.rs +++ b/server/src/game/role/witch.rs @@ -59,7 +59,7 @@ impl RoleStateImpl for Witch { .filter(|p|p.alive(game)) .filter(|p|p.keeps_game_running(game)) .all(|p| - WinCondition::can_win_together(&p.win_condition(game), actor_ref.win_condition(game)) + WinCondition::are_friends(&p.win_condition(game), actor_ref.win_condition(game)) ) { diff --git a/server/src/game/win_condition.rs b/server/src/game/win_condition.rs index 16a266fdb..9737c7d3f 100644 --- a/server/src/game/win_condition.rs +++ b/server/src/game/win_condition.rs @@ -19,7 +19,7 @@ impl WinCondition{ WinCondition::RoleStateWon => None, } } - pub fn can_win_together(a: &WinCondition, b: &WinCondition)->bool{ + pub fn are_friends(a: &WinCondition, b: &WinCondition)->bool{ let a_conditions = a.required_resolution_states_for_win(); let b_conditions = b.required_resolution_states_for_win(); @@ -28,7 +28,7 @@ impl WinCondition{ _ => true } } - pub fn can_win_when_resolution_state_reached(&self, resolution_state: GameConclusion)->bool{ + pub fn friends_with_resolution_state(&self, resolution_state: GameConclusion)->bool{ match self{ WinCondition::GameConclusionReached{win_if_any} => win_if_any.contains(&resolution_state), WinCondition::RoleStateWon => true, @@ -41,7 +41,7 @@ impl WinCondition{ } } - pub fn new_single_resolution_state(resolution_state: GameConclusion) -> WinCondition { + pub fn new_loyalist(resolution_state: GameConclusion) -> WinCondition { let mut win_if_any = HashSet::new(); win_if_any.insert(resolution_state); WinCondition::GameConclusionReached { win_if_any }