From f1ec81331282f4b3bc706bd4265071a1d2c2b759 Mon Sep 17 00:00:00 2001 From: Edoardo Lolletti Date: Sat, 10 Feb 2024 11:33:42 +0100 Subject: [PATCH] Update handling for EFFECT_MUST_ATTACK_MONSTER Properly implement 58c488de9e23986c1009879e78ffc5422022a994: * If only one effect is providing EFFECT_MUST_ATTACK_MONSTER the attack selection proceeds as normal, with the turn player choosing the attack targets * If multiple effects are providing EFFECT_MUST_ATTACK_MONSTER, the opponent choose the attack target like it did prior to the other change, if then the EFFECT_MUST_ATTACK_MONSTER effect applied on the selected monster was also applying to other monsters, the turn player will then choose among those monsters a valid attack target --- field.cpp | 7 +++++-- field.h | 2 +- processor.cpp | 45 ++++++++++++++++++++++++++++++++++++++++++--- processor_unit.h | 2 ++ 4 files changed, 50 insertions(+), 6 deletions(-) diff --git a/field.cpp b/field.cpp index 34eef556..dbe7f366 100644 --- a/field.cpp +++ b/field.cpp @@ -2381,7 +2381,7 @@ int32_t field::effect_replace_check(uint32_t code, const tevent& e) { } return FALSE; } -int32_t field::get_attack_target(card* pcard, card_vector* v, bool chain_attack, bool select_target) { +int32_t field::get_attack_target(card* pcard, card_vector* v, bool chain_attack, bool select_target, std::multimap* must_attack_map) { pcard->direct_attackable = 0; uint8_t p = pcard->current.controler; card_vector auto_attack, only_attack, must_attack, attack_tg; @@ -2391,8 +2391,11 @@ int32_t field::get_attack_target(card* pcard, card_vector* v, bool chain_attack, auto_attack.push_back(atarget); if(pcard->is_affected_by_effect(EFFECT_ONLY_ATTACK_MONSTER, atarget)) only_attack.push_back(atarget); - if(pcard->is_affected_by_effect(EFFECT_MUST_ATTACK_MONSTER, atarget)) + if(auto* peffect = pcard->is_affected_by_effect(EFFECT_MUST_ATTACK_MONSTER, atarget); peffect) { must_attack.push_back(atarget); + if(must_attack_map) + must_attack_map->emplace(peffect, atarget); + } } } card_vector* pv = nullptr; diff --git a/field.h b/field.h index c6c68138..635ea65d 100644 --- a/field.h +++ b/field.h @@ -515,7 +515,7 @@ class field { uint32_t get_field_counter(uint8_t playerid, uint8_t self, uint8_t oppo, uint16_t countertype); int32_t effect_replace_check(uint32_t code, const tevent& e); - int32_t get_attack_target(card* pcard, card_vector* v, bool chain_attack = false, bool select_target = true); + int32_t get_attack_target(card* pcard, card_vector* v, bool chain_attack = false, bool select_target = true, std::multimap* must_attack_map = nullptr); bool confirm_attack_target(); void attack_all_target_check(); int32_t check_tribute(card* pcard, int32_t min, int32_t max, group* mg, uint8_t toplayer, uint32_t zone = 0x1f, uint32_t releasable = 0xff00ff, uint32_t pos = 0x1); diff --git a/processor.cpp b/processor.cpp index 44d40a4a..b1e4d3b6 100644 --- a/processor.cpp +++ b/processor.cpp @@ -1973,7 +1973,8 @@ bool field::process(Processors::BattleCommand& arg) { core.attack_player = FALSE; core.select_cards.clear(); return_cards.clear(); - get_attack_target(core.attacker, &core.select_cards, core.chain_attack); + arg.must_attack_map.clear(); + auto atype = get_attack_target(core.attacker, &core.select_cards, core.chain_attack, true, &arg.must_attack_map); // direct attack if(core.attacker->direct_attackable) { if(core.select_cards.size() == 0) { @@ -1993,7 +1994,15 @@ bool field::process(Processors::BattleCommand& arg) { arg.step = 6; return FALSE; } - if(is_player_affected_by_effect(infos.turn_player, EFFECT_PATRICIAN_OF_DARKNESS)) { + auto differentMustAttackMonsterEffects = [&] { + size_t count = 0; + const auto& must = arg.must_attack_map; + for(auto it = must.begin(), end = must.end(); it != end; it = must.upper_bound(it->first)) + ++count; + return count; + }(); + + if((atype == 3 && differentMustAttackMonsterEffects != 1) || is_player_affected_by_effect(infos.turn_player, EFFECT_PATRICIAN_OF_DARKNESS)) { if(core.select_cards.size() == 1) return_cards.list.push_back(core.select_cards.front()); else { @@ -2005,13 +2014,17 @@ bool field::process(Processors::BattleCommand& arg) { message->write(1 - infos.turn_player); message->write(549); emplace_process(1 - infos.turn_player, false, 1, 1); + if(atype == 3 && arg.must_attack_map.size() != differentMustAttackMonsterEffects) { + arg.step = 15; + return FALSE; + } } } else { auto message = pduel->new_message(MSG_HINT); message->write(HINT_SELECTMSG); message->write(infos.turn_player); message->write(549); - emplace_process(infos.turn_player, static_cast(core.attack_cancelable), 1, 1); + emplace_process(infos.turn_player, core.attack_cancelable, 1, 1); } arg.step = 5; return FALSE; @@ -2210,6 +2223,32 @@ bool field::process(Processors::BattleCommand& arg) { arg.step = -1; return FALSE; } + // EFFECT_MUST_ATTACK_MONSTER where an effect affects more than 1 monster + case 16: { + auto selected_card = return_cards.list.front(); + const auto range = arg.must_attack_map.equal_range( + std::find_if(arg.must_attack_map.begin(), arg.must_attack_map.end(), + [selected_card](const auto& must_pair) { + return must_pair.second == selected_card; + })->first); + if(std::distance(range.first, range.second) < 2) { + core.attack_target = selected_card; + core.pre_field[1] = core.attack_target->fieldid_r; + arg.step = 5; + return FALSE; + } + core.select_cards.clear(); + std::transform(range.first, range.second, std::back_inserter(core.select_cards), [](const auto& pair) { + return pair.second; + }); + auto message = pduel->new_message(MSG_HINT); + message->write(HINT_SELECTMSG); + message->write(infos.turn_player); + message->write(549); + emplace_process(infos.turn_player, core.attack_cancelable, 1, 1); + arg.step = 5; + return FALSE; + } case 19: { infos.phase = PHASE_DAMAGE; core.chain_attack = false; diff --git a/processor_unit.h b/processor_unit.h index f7575a82..2042049b 100644 --- a/processor_unit.h +++ b/processor_unit.h @@ -7,6 +7,7 @@ #define PROCESSOR_UNIT_H_ #include +#include //std::multimap #include //std::unique_ptr #include //std::false_type, std::true_type #include @@ -235,6 +236,7 @@ struct BattleCommand : public Process { effect* damage_change_effect; group* cards_destroyed_by_battle; card* reason_card; + std::multimap must_attack_map; BattleCommand(uint16_t step_, group* cards_destroyed_by_battle_ = nullptr) : Process(step_), phase_to_change_to(0), is_replaying_attack(false), attack_announce_failed(false), repeat_battle_phase(false), second_battle_phase_is_optional(false),