use forge_foundation::{CardTypeLine, ColorSet, ManaCost, ZoneType};
use super::EffectContext;
use crate::card::Card;
use crate::event::RunParams;
use crate::ids::{CardId, PlayerId};
use crate::spellability::SpellAbility;
use crate::trigger::TriggerType;
const DUNGEONS: &[(&str, &[&str])] = &[
(
"Dungeon of the Mad Mage",
&[
"Yawning Portal",
"Dungeon Level",
"Goblin Bazaar",
"Twisted Caverns",
"Lost Level",
"Runestone Caverns",
"Mad Wizard's Lair",
],
),
(
"Lost Mine of Phandelver",
&[
"Cave Entrance",
"Goblin Lair",
"Mine Tunnels",
"Temple of Dumathoin",
],
),
(
"Tomb of Annihilation",
&[
"Trapped Entry",
"Veils of Fear",
"Sandfall Cell",
"Cradle of the Death God",
],
),
(
"Undercity",
&[
"Secret Entrance",
"Forge",
"Lost Well",
"Throne of the Dead Three",
],
),
];
#[manabrew_engine_macros::spell_effect(VentureEffect)]
fn resolve(ctx: &mut EffectContext, sa: &crate::spellability::SpellAbility) {
let controller = sa.activating_player;
let players = if let Some(def) = sa.defined_player() {
super::resolve_defined_players(def, controller, ctx.game)
} else if let Some(target_player) = sa.target_chosen.target_player {
vec![target_player]
} else {
vec![controller]
};
for pid in players {
if ctx.game.player(pid).has_lost {
continue;
}
venture_into_dungeon(ctx, sa, pid);
}
}
fn venture_into_dungeon(ctx: &mut EffectContext, sa: &SpellAbility, player: PlayerId) {
let current_dungeon = find_current_dungeon(ctx, player);
let dungeon_id = match current_dungeon {
Some((id, room, dungeon_name)) => {
let rooms = get_dungeon_rooms(&dungeon_name);
if let Some(rooms) = rooms {
if !room.is_empty() && rooms.last().is_some_and(|last| *last == room) {
complete_dungeon(ctx, player, id);
create_dungeon(ctx, sa, player)
} else {
id
}
} else {
id
}
}
None => {
create_dungeon(ctx, sa, player)
}
};
let dungeon_name = ctx
.game
.card(dungeon_id)
.svars
.get("DungeonName")
.cloned()
.unwrap_or_default();
let current_room = ctx
.game
.card(dungeon_id)
.svars
.get("CurrentRoom")
.cloned()
.unwrap_or_default();
let rooms = get_dungeon_rooms(&dungeon_name);
let next_room = if let Some(rooms) = rooms {
if current_room.is_empty() {
rooms.first().map(|s| s.to_string())
} else {
let idx = rooms.iter().position(|r| *r == current_room);
match idx {
Some(i) if i + 1 < rooms.len() => Some(rooms[i + 1].to_string()),
_ => None, }
}
} else {
None
};
if let Some(next) = next_room {
ctx.game
.card_mut(dungeon_id)
.svars
.insert("CurrentRoom".to_string(), next.clone());
ctx.trigger_handler.run_trigger(
TriggerType::RoomEntered,
RunParams {
card: Some(dungeon_id),
player: Some(player),
room_name: Some(next),
..Default::default()
},
false,
);
}
}
fn find_current_dungeon(ctx: &EffectContext, player: PlayerId) -> Option<(CardId, String, String)> {
ctx.game
.cards
.iter()
.find(|c| {
c.zone == ZoneType::Command && c.owner == player && c.svars.contains_key("DungeonName")
})
.map(|c| {
let room = c.svars.get("CurrentRoom").cloned().unwrap_or_default();
let name = c.svars.get("DungeonName").cloned().unwrap_or_default();
(c.id, room, name)
})
}
fn create_dungeon(ctx: &mut EffectContext, sa: &SpellAbility, player: PlayerId) -> CardId {
let dungeon_name = sa.ir.dungeon_text.clone().unwrap_or_else(|| {
let idx = ctx.rng.next_int(DUNGEONS.len() as i32) as usize % DUNGEONS.len();
DUNGEONS[idx].0.to_string()
});
let mut card = Card::new(
CardId(0),
dungeon_name.clone(),
player,
CardTypeLine::parse("Dungeon"),
ManaCost::parse(""),
ColorSet::COLORLESS,
None,
None,
vec![],
vec![],
);
card.set_controller(player);
card.set_s_var("DungeonName", dungeon_name);
card.set_s_var("CurrentRoom", String::new());
let id = ctx.game.create_card(card);
ctx.move_card(id, ZoneType::Command, player);
ctx.trigger_handler.register_active_trigger(ctx.game, id);
id
}
fn complete_dungeon(ctx: &mut EffectContext, player: PlayerId, dungeon_id: CardId) {
let old_zone = ctx.game.card(dungeon_id).zone;
ctx.move_card(dungeon_id, ZoneType::Exile, player);
super::emit_zone_trigger(ctx.trigger_handler, dungeon_id, old_zone, ZoneType::Exile);
ctx.trigger_handler.run_trigger(
TriggerType::DungeonCompleted,
RunParams {
card: Some(dungeon_id),
player: Some(player),
..Default::default()
},
false,
);
}
fn get_dungeon_rooms(name: &str) -> Option<&'static [&'static str]> {
DUNGEONS
.iter()
.find(|(n, _)| *n == name)
.map(|(_, rooms)| *rooms)
}