/* * Copyright (c) 2010-2015, Argon Sun (Fluorohydride) * Copyright (c) 2016-2025, Edoardo Lolletti (edo9300) * * SPDX-License-Identifier: AGPL-3.0-or-later */ #include //std::sort, std::swap, std::find, std::find_if, std::min, std::none_of #include //std::tie #include //std::move #include #include "card.h" #include "duel.h" #include "effect.h" #include "field.h" #include "group.h" #include "interpreter.h" bool chain::chain_operation_sort(const chain& c1, const chain& c2) { return c1.triggering_effect->id < c2.triggering_effect->id; } void chain::set_triggering_state(card* pcard) { triggering_controler = pcard->current.controler; triggering_location = pcard->current.location; if(pcard->current.location & (LOCATION_MZONE | LOCATION_SZONE)) { if(pcard->current.is_location(LOCATION_FZONE)) triggering_location |= LOCATION_FZONE; else if(pcard->current.is_location(LOCATION_PZONE)) triggering_location |= LOCATION_PZONE; else if(pcard->current.is_location(LOCATION_STZONE)) triggering_location |= LOCATION_STZONE; else if(pcard->current.is_location(LOCATION_EMZONE)) triggering_location |= LOCATION_EMZONE; else if(pcard->current.is_location(LOCATION_MMZONE)) triggering_location |= LOCATION_MMZONE; } triggering_sequence = pcard->current.sequence; triggering_position = pcard->current.position; triggering_status = pcard->status; triggering_summon_type = pcard->summon.type & 0xff00ffff; triggering_summon_location = pcard->summon.location; triggering_summon_proc_complete = pcard->is_status(STATUS_PROC_COMPLETE); triggering_state.code = pcard->get_code(); triggering_state.code2 = pcard->get_another_code(); triggering_state.level = pcard->get_level(); triggering_state.rank = pcard->get_rank(); triggering_state.attribute = pcard->get_attribute(); triggering_state.type = pcard->get_type(); triggering_state.race = pcard->get_race(); triggering_state.attack = pcard->get_attack(); triggering_state.defense = pcard->get_defense(); //For the triggering archetypes: auto& setcode = triggering_state.setcodes; setcode.clear(); pcard->get_set_card(setcode); } bool tevent::operator< (const tevent& rhs) const { return std::tie(trigger_card, event_cards, reason_effect, event_code, event_value, reason, event_player, reason_player, global_id) < std::tie(rhs.trigger_card, rhs.event_cards, rhs.reason_effect, rhs.event_code, rhs.event_value, rhs.reason, rhs.event_player, rhs.reason_player, rhs.global_id); } field::field(duel* _pduel, const OCG_DuelOptions& options) :pduel(_pduel), player({ {options.team1, options.team2} }) { core.duel_options = options.flags; nil_event.event_code = 0; nil_event.event_cards = nullptr; nil_event.event_player = PLAYER_NONE; nil_event.event_value = 0; nil_event.reason = 0; nil_event.reason_effect = nullptr; nil_event.reason_player = PLAYER_NONE; } void field::reload_field_info() { auto message = pduel->new_message(MSG_RELOAD_FIELD); message->write(core.duel_options); for(int32_t playerid = 0; playerid < 2; ++playerid) { message->write(player[playerid].lp); for(auto& pcard : player[playerid].list_mzone) { if(pcard) { message->write(1); message->write(pcard->current.position); message->write(pcard->xyz_materials.size()); } else { message->write(0); } } for(auto& pcard : player[playerid].list_szone) { if(pcard) { message->write(1); message->write(pcard->current.position); message->write(pcard->xyz_materials.size()); } else { message->write(0); } } message->write(player[playerid].list_main.size()); message->write(player[playerid].list_hand.size()); message->write(player[playerid].list_grave.size()); message->write(player[playerid].list_remove.size()); message->write(player[playerid].list_extra.size()); message->write(player[playerid].extra_p_count); } message->write(core.current_chain.size()); for(const auto& ch : core.current_chain) { effect* peffect = ch.triggering_effect; message->write(peffect->get_handler()->data.code); message->write(peffect->get_handler()->get_info_location()); message->write(ch.triggering_controler); message->write((uint8_t)ch.triggering_location); message->write(ch.triggering_sequence); message->write(peffect->description); } } // The core of moving cards, and Debug.AddCard() will call this function directly. // check Fusion/S/X monster redirection by the rule, set fieldid_r void field::add_card(uint8_t playerid, card* pcard, uint8_t location, uint8_t sequence, bool pzone) { if (pcard->current.location != 0) return; if (!is_location_useable(playerid, location, sequence)) return; // explicitly allow fusion spell cards to start in the extra if(pcard->is_extra_deck_monster() || (pcard->data.type & TYPE_FUSION) != 0) { if(location & (LOCATION_HAND | LOCATION_DECK)) { location = LOCATION_EXTRA; pcard->sendto_param.position = POS_FACEDOWN_DEFENSE; } } else if(location == LOCATION_EXTRA) { if(!(pcard->data.type & TYPE_PENDULUM) || !(pcard->sendto_param.position & POS_FACEUP)) { location = LOCATION_DECK; pcard->sendto_param.position = POS_FACEDOWN_DEFENSE; } } pcard->current.controler = playerid; pcard->current.location = location; switch (location) { case LOCATION_MZONE: { player[playerid].list_mzone[sequence] = pcard; pcard->current.sequence = sequence; break; } case LOCATION_SZONE: { player[playerid].list_szone[sequence] = pcard; pcard->current.sequence = sequence; break; } case LOCATION_DECK: { if (sequence == 0) { //deck top player[playerid].list_main.push_back(pcard); pcard->current.sequence = static_cast(player[playerid].list_main.size() - 1); } else if (sequence == 1) { //deck bottom player[playerid].list_main.insert(player[playerid].list_main.begin(), pcard); reset_sequence(playerid, LOCATION_DECK); } else { //deck top & shuffle player[playerid].list_main.push_back(pcard); pcard->current.sequence = static_cast(player[playerid].list_main.size() - 1); if(!core.shuffle_check_disabled) core.shuffle_deck_check[playerid] = true; } pcard->sendto_param.position = POS_FACEDOWN; break; } case LOCATION_HAND: { player[playerid].list_hand.push_back(pcard); pcard->current.sequence = static_cast(player[playerid].list_hand.size() - 1); uint32_t pos = pcard->is_affected_by_effect(EFFECT_PUBLIC) ? POS_FACEUP : POS_FACEDOWN; pcard->sendto_param.position = pos; if(!(pcard->current.reason & REASON_DRAW) && !core.shuffle_check_disabled) core.shuffle_hand_check[playerid] = true; break; } case LOCATION_GRAVE: { player[playerid].list_grave.push_back(pcard); pcard->current.sequence = static_cast(player[playerid].list_grave.size() - 1); break; } case LOCATION_REMOVED: { player[playerid].list_remove.push_back(pcard); pcard->current.sequence = static_cast(player[playerid].list_remove.size() - 1); break; } case LOCATION_EXTRA: { if(player[playerid].extra_p_count == 0 || ((pcard->data.type & TYPE_PENDULUM) && (pcard->sendto_param.position & POS_FACEUP))) player[playerid].list_extra.push_back(pcard); else player[playerid].list_extra.insert(player[playerid].list_extra.end() - player[playerid].extra_p_count, pcard); if((pcard->data.type & TYPE_PENDULUM) && (pcard->sendto_param.position & POS_FACEUP)) ++player[playerid].extra_p_count; reset_sequence(playerid, LOCATION_EXTRA); break; } } pcard->current.pzone = pzone; pcard->apply_field_effect(); pcard->fieldid = infos.field_id++; pcard->fieldid_r = pcard->fieldid; pcard->turnid = infos.turn_id; if(check_unique_onfield(pcard, pcard->current.controler, pcard->current.location)) pcard->unique_fieldid = UINT_MAX; if (location == LOCATION_MZONE) player[playerid].used_location |= 1 << sequence; if (location == LOCATION_SZONE) player[playerid].used_location |= 256 << sequence; } void field::remove_card(card* pcard) { if (pcard->current.controler == PLAYER_NONE || pcard->current.location == 0) return; uint8_t playerid = pcard->current.controler; switch (pcard->current.location) { case LOCATION_MZONE: player[playerid].list_mzone[pcard->current.sequence] = nullptr; break; case LOCATION_SZONE: player[playerid].list_szone[pcard->current.sequence] = nullptr; break; case LOCATION_DECK: player[playerid].list_main.erase(player[playerid].list_main.begin() + pcard->current.sequence); reset_sequence(playerid, LOCATION_DECK); if(!core.shuffle_check_disabled) core.shuffle_deck_check[playerid] = true; break; case LOCATION_HAND: player[playerid].list_hand.erase(player[playerid].list_hand.begin() + pcard->current.sequence); reset_sequence(playerid, LOCATION_HAND); break; case LOCATION_GRAVE: player[playerid].list_grave.erase(player[playerid].list_grave.begin() + pcard->current.sequence); reset_sequence(playerid, LOCATION_GRAVE); break; case LOCATION_REMOVED: player[playerid].list_remove.erase(player[playerid].list_remove.begin() + pcard->current.sequence); reset_sequence(playerid, LOCATION_REMOVED); break; case LOCATION_EXTRA: player[playerid].list_extra.erase(player[playerid].list_extra.begin() + pcard->current.sequence); reset_sequence(playerid, LOCATION_EXTRA); if((pcard->data.type & TYPE_PENDULUM) && (pcard->current.position & POS_FACEUP)) --player[playerid].extra_p_count; break; } pcard->cancel_field_effect(); if (pcard->current.location == LOCATION_MZONE) player[playerid].used_location &= ~(1 << pcard->current.sequence); if (pcard->current.location == LOCATION_SZONE) player[playerid].used_location &= ~(256 << pcard->current.sequence); if(core.current_chain.size() > 0) core.just_sent_cards.insert(pcard); pcard->previous.controler = pcard->current.controler; pcard->previous.location = pcard->current.location; pcard->previous.sequence = pcard->current.sequence; pcard->previous.position = pcard->current.position; pcard->previous.pzone = pcard->current.pzone; pcard->current.controler = PLAYER_NONE; pcard->current.location = 0; pcard->current.sequence = 0; } // moving cards: // 1. draw() // 2. discard_deck() // 3. swap_control() // 4. control_adjust() // 5. move_card() // check Fusion/S/X monster redirection by the rule bool field::move_card(uint8_t playerid, card* pcard, uint8_t location, uint8_t sequence, bool pzone) { if (!is_location_useable(playerid, location, sequence)) return false; uint8_t preplayer = pcard->current.controler; uint8_t presequence = pcard->current.sequence; if(pcard->is_extra_deck_monster() && (location & (LOCATION_HAND | LOCATION_DECK))) { location = LOCATION_EXTRA; pcard->sendto_param.position = POS_FACEDOWN_DEFENSE; } if (pcard->current.location) { if (pcard->current.location == location && pcard->current.pzone == pzone) { if (pcard->current.location == LOCATION_DECK) { if(preplayer == playerid) { auto message = pduel->new_message(MSG_MOVE); message->write(pcard->data.code); message->write(pcard->get_info_location()); player[preplayer].list_main.erase(player[preplayer].list_main.begin() + pcard->current.sequence); if (sequence == 0) { //deck top player[playerid].list_main.push_back(pcard); } else if (sequence == 1) { player[playerid].list_main.insert(player[playerid].list_main.begin(), pcard); } else { player[playerid].list_main.push_back(pcard); if(!core.shuffle_check_disabled) core.shuffle_deck_check[playerid] = true; } reset_sequence(playerid, LOCATION_DECK); pcard->previous.controler = preplayer; pcard->current.controler = playerid; message->write(pcard->get_info_location()); message->write(pcard->current.reason); return true; } else remove_card(pcard); } else if(location & LOCATION_ONFIELD) { if (playerid == preplayer && sequence == presequence) return false; if(location == LOCATION_MZONE) { if(sequence >= player[playerid].list_mzone.size() || player[playerid].list_mzone[sequence]) return false; } else { if(sequence >= player[playerid].list_szone.size() || player[playerid].list_szone[sequence]) return false; } duel::duel_message* message = nullptr; if(preplayer == playerid) { message = pduel->new_message(MSG_MOVE); message->write(pcard->data.code); message->write(pcard->get_info_location()); } if(core.current_chain.size() > 0) core.just_sent_cards.insert(pcard); pcard->previous.controler = pcard->current.controler; pcard->previous.location = pcard->current.location; pcard->previous.sequence = pcard->current.sequence; pcard->previous.position = pcard->current.position; pcard->previous.pzone = pcard->current.pzone; if (location == LOCATION_MZONE) { player[preplayer].list_mzone[presequence] = nullptr; player[preplayer].used_location &= ~(1 << presequence); player[playerid].list_mzone[sequence] = pcard; player[playerid].used_location |= 1 << sequence; pcard->current.controler = playerid; pcard->current.sequence = sequence; } else { player[preplayer].list_szone[presequence] = nullptr; player[preplayer].used_location &= ~(256 << presequence); player[playerid].list_szone[sequence] = pcard; player[playerid].used_location |= 256 << sequence; pcard->current.controler = playerid; pcard->current.sequence = sequence; } if(message) { message->write(pcard->get_info_location()); message->write(pcard->current.reason); } else { pcard->fieldid = infos.field_id++; if(check_unique_onfield(pcard, pcard->current.controler, pcard->current.location)) pcard->unique_fieldid = UINT_MAX; } return true; } else if(location == LOCATION_HAND) { if(preplayer == playerid) return false; remove_card(pcard); } else { if(location == LOCATION_GRAVE) { if(pcard->current.sequence == player[pcard->current.controler].list_grave.size() - 1) return false; auto message = pduel->new_message(MSG_MOVE); message->write(pcard->data.code); message->write(pcard->get_info_location()); player[pcard->current.controler].list_grave.erase(player[pcard->current.controler].list_grave.begin() + pcard->current.sequence); player[pcard->current.controler].list_grave.push_back(pcard); reset_sequence(pcard->current.controler, LOCATION_GRAVE); message->write(pcard->get_info_location()); message->write(pcard->current.reason); } else if(location == LOCATION_REMOVED) { if(pcard->current.sequence == player[pcard->current.controler].list_remove.size() - 1) return false; auto message = pduel->new_message(MSG_MOVE); message->write(pcard->data.code); message->write(pcard->get_info_location()); player[pcard->current.controler].list_remove.erase(player[pcard->current.controler].list_remove.begin() + pcard->current.sequence); player[pcard->current.controler].list_remove.push_back(pcard); reset_sequence(pcard->current.controler, LOCATION_REMOVED); message->write(pcard->get_info_location()); message->write(pcard->current.reason); } else { auto message = pduel->new_message(MSG_MOVE); message->write(pcard->data.code); message->write(pcard->get_info_location()); player[pcard->current.controler].list_extra.erase(player[pcard->current.controler].list_extra.begin() + pcard->current.sequence); player[pcard->current.controler].list_extra.push_back(pcard); reset_sequence(pcard->current.controler, LOCATION_EXTRA); message->write(pcard->get_info_location()); message->write(pcard->current.reason); } return true; } } else { if((pcard->data.type & TYPE_PENDULUM) && (location == LOCATION_GRAVE) && pcard->is_capable_send_to_extra(playerid) && (((pcard->current.location == LOCATION_MZONE) && !pcard->is_status(STATUS_SUMMON_DISABLED)) || ((pcard->current.location == LOCATION_SZONE) && !pcard->is_status(STATUS_ACTIVATE_DISABLED)))) { location = LOCATION_EXTRA; pcard->sendto_param.position = POS_FACEUP_DEFENSE; } remove_card(pcard); } } add_card(playerid, pcard, location, sequence, pzone); return true; } void field::swap_card(card* pcard1, card* pcard2, uint8_t new_sequence1, uint8_t new_sequence2) { uint8_t p1 = pcard1->current.controler, p2 = pcard2->current.controler; uint8_t l1 = pcard1->current.location, l2 = pcard2->current.location; uint8_t s1 = pcard1->current.sequence, s2 = pcard2->current.sequence; loc_info info1 = pcard1->get_info_location(), info2 = pcard2->get_info_location(); if(!(l1 & LOCATION_ONFIELD) || !(l2 & LOCATION_ONFIELD)) return; if((new_sequence1 != s1 && !is_location_useable(p1, l1, new_sequence1)) || (new_sequence2 != s2 && !is_location_useable(p2, l2, new_sequence2))) return; if(p1 == p2 && l1 == l2 && (new_sequence1 == s2 || new_sequence2 == s1)) return; if(l1 == l2) { pcard1->previous.controler = p1; pcard1->previous.location = l1; pcard1->previous.sequence = s1; pcard1->previous.position = pcard1->current.position; pcard1->previous.pzone = pcard1->current.pzone; pcard1->current.controler = p2; pcard1->current.location = l2; pcard1->current.sequence = new_sequence2; pcard2->previous.controler = p2; pcard2->previous.location = l2; pcard2->previous.sequence = s2; pcard2->previous.position = pcard2->current.position; pcard2->previous.pzone = pcard2->current.pzone; pcard2->current.controler = p1; pcard2->current.location = l1; pcard2->current.sequence = new_sequence1; if(p1 != p2) { pcard1->fieldid = infos.field_id++; pcard2->fieldid = infos.field_id++; if(check_unique_onfield(pcard1, pcard1->current.controler, pcard1->current.location)) pcard1->unique_fieldid = UINT_MAX; if(check_unique_onfield(pcard2, pcard2->current.controler, pcard2->current.location)) pcard2->unique_fieldid = UINT_MAX; } if(l1 == LOCATION_MZONE) { player[p1].list_mzone[s1] = nullptr; player[p1].used_location &= ~(1 << s1); player[p2].list_mzone[s2] = nullptr; player[p2].used_location &= ~(1 << s2); player[p2].list_mzone[new_sequence2] = pcard1; player[p2].used_location |= 1 << new_sequence2; player[p1].list_mzone[new_sequence1] = pcard2; player[p1].used_location |= 1 << new_sequence1; } else if(l1 == LOCATION_SZONE) { player[p1].list_szone[s1] = nullptr; player[p1].used_location &= ~(256 << s1); player[p2].list_szone[s2] = nullptr; player[p2].used_location &= ~(256 << s2); player[p2].list_szone[new_sequence2] = pcard1; player[p2].used_location |= 256 << new_sequence2; player[p1].list_szone[new_sequence1] = pcard2; player[p1].used_location |= 256 << new_sequence1; } } else { remove_card(pcard1); remove_card(pcard2); add_card(p2, pcard1, l2, new_sequence2); add_card(p1, pcard2, l1, new_sequence1); } if(s1 == new_sequence1 && s2 == new_sequence2) { auto message = pduel->new_message(MSG_SWAP); message->write(pcard1->data.code); message->write(info1); message->write(pcard2->data.code); message->write(info2); } else if(s1 == new_sequence1) { auto message = pduel->new_message(MSG_MOVE); message->write(pcard1->data.code); message->write(info1); message->write(pcard1->get_info_location()); message->write(0); message = pduel->new_message(MSG_MOVE); message->write(pcard2->data.code); message->write(info2); message->write(pcard2->get_info_location()); message->write(0); } else { auto message = pduel->new_message(MSG_MOVE); message->write(pcard2->data.code); message->write(info2); message->write(pcard2->get_info_location()); message->write(0); message = pduel->new_message(MSG_MOVE); message->write(pcard1->data.code); message->write(info1); message->write(pcard1->get_info_location()); message->write(0); } } void field::swap_card(card* pcard1, card* pcard2) { return swap_card(pcard1, pcard2, pcard1->current.sequence, pcard2->current.sequence); } // add EFFECT_SET_CONTROL void field::set_control(card* pcard, uint8_t playerid, uint16_t reset_phase, uint8_t reset_count) { if((core.remove_brainwashing && pcard->is_affected_by_effect(EFFECT_REMOVE_BRAINWASHING)) || std::get(pcard->refresh_control_status()) == playerid) return; effect* peffect = pduel->new_effect(); if(core.reason_effect) peffect->owner = core.reason_effect->get_handler(); else peffect->owner = pcard; peffect->handler = pcard; peffect->type = EFFECT_TYPE_SINGLE; peffect->code = EFFECT_SET_CONTROL; peffect->value = playerid; peffect->flag[0] = EFFECT_FLAG_CANNOT_DISABLE; peffect->reset_flag = RESET_EVENT | 0xc6c0000; if(reset_count) { peffect->reset_flag |= RESET_PHASE | reset_phase; if(!(peffect->reset_flag & (RESET_SELF_TURN | RESET_OPPO_TURN))) peffect->reset_flag |= (RESET_SELF_TURN | RESET_OPPO_TURN); peffect->reset_count = reset_count; } pcard->add_effect(peffect); pcard->current.controler = playerid; } card* field::get_field_card(uint32_t playerid, uint32_t location, uint32_t sequence) { switch(location) { case LOCATION_MZONE: { if(sequence < player[playerid].list_mzone.size()) return player[playerid].list_mzone[sequence]; return nullptr; } case LOCATION_MMZONE: { if(is_flag(DUEL_3_COLUMNS_FIELD)) { if(sequence < 3) return player[playerid].list_mzone[sequence + 1]; } else if(sequence < 5) return player[playerid].list_mzone[sequence]; return nullptr; } case LOCATION_EMZONE: { if(sequence < 2) return player[playerid].list_mzone[sequence + 5]; return nullptr; } case LOCATION_SZONE: { if(sequence < player[playerid].list_szone.size()) return player[playerid].list_szone[sequence]; return nullptr; } case LOCATION_STZONE: { if(is_flag(DUEL_3_COLUMNS_FIELD)) { if(sequence < 3) return player[playerid].list_szone[sequence + 1]; } else if(sequence < 5) return player[playerid].list_szone[sequence]; return nullptr; } case LOCATION_FZONE: { if(sequence == 0) return player[playerid].list_szone[5]; return nullptr; } case LOCATION_PZONE: { if(sequence < 2) { card* pcard = player[playerid].list_szone[get_pzone_index(sequence, playerid)]; return pcard && pcard->current.pzone ? pcard : nullptr; } return nullptr; } case LOCATION_DECK: { if(sequence < player[playerid].list_main.size()) return player[playerid].list_main[sequence]; return nullptr; } case LOCATION_HAND: { if(sequence < player[playerid].list_hand.size()) return player[playerid].list_hand[sequence]; return nullptr; } case LOCATION_GRAVE: { if(sequence < player[playerid].list_grave.size()) return player[playerid].list_grave[sequence]; return nullptr; } case LOCATION_REMOVED: { if(sequence < player[playerid].list_remove.size()) return player[playerid].list_remove[sequence]; return nullptr; } case LOCATION_EXTRA: { if(sequence < player[playerid].list_extra.size()) return player[playerid].list_extra[sequence]; return nullptr; } } return nullptr; } int32_t field::is_field_location_valid(uint32_t location, uint32_t sequence) { if(location == LOCATION_EMZONE) { sequence += 5; location = LOCATION_MZONE; } if(location == LOCATION_MMZONE) { location = LOCATION_MZONE; sequence += 1 * is_flag(DUEL_3_COLUMNS_FIELD); } if(location == LOCATION_STZONE) { location = LOCATION_SZONE; sequence += 1 * is_flag(DUEL_3_COLUMNS_FIELD); } if((location & LOCATION_ONFIELD) == 0) return TRUE; if(location == LOCATION_MZONE) { return sequence < 7; } else if(location == LOCATION_SZONE) { return sequence < 8; } else if(location == LOCATION_FZONE) { return sequence < 2; } else if(location == LOCATION_PZONE) { return sequence < 3; } return TRUE; } // return: the given slot in LOCATION_MZONE or all LOCATION_SZONE is available or not int32_t field::is_location_useable(uint32_t playerid, uint32_t location, uint32_t sequence) { uint32_t flag = player[playerid].disabled_location | player[playerid].used_location; if(location == LOCATION_EMZONE) { sequence += 5; location = LOCATION_MZONE; } if(location == LOCATION_MMZONE) { location = LOCATION_MZONE; sequence += 1 * is_flag(DUEL_3_COLUMNS_FIELD); } if(location == LOCATION_STZONE) { location = LOCATION_SZONE; sequence += 1 * is_flag(DUEL_3_COLUMNS_FIELD); } if (location == LOCATION_MZONE) { if(flag & (0x1u << sequence)) return FALSE; if(sequence >= 5) { uint32_t oppo = player[1 - playerid].disabled_location | player[1 - playerid].used_location; if(oppo & (0x1u << (11 - sequence))) return FALSE; } } else if (location == LOCATION_SZONE) { if(flag & (0x100u << sequence)) return FALSE; } else if (location == LOCATION_FZONE) { if(flag & (0x100u << (5 + sequence))) return FALSE; } else if (location == LOCATION_PZONE) { if(!is_flag(DUEL_PZONE)) return FALSE; if(flag & (0x100u << get_pzone_index(sequence, playerid))) return FALSE; } return TRUE; } // uplayer: request player, PLAYER_NONE means ignoring EFFECT_MAX_MZONE, EFFECT_MAX_SZONE // list: store local flag in list // return: usable count of LOCATION_MZONE or real LOCATION_SZONE of playerid requested by uplayer (may be negative) int32_t field::get_useable_count(card* pcard, uint8_t playerid, uint8_t location, uint8_t uplayer, uint32_t reason, uint32_t zone, uint32_t* list) { if(location == LOCATION_MZONE && pcard && pcard->current.location == LOCATION_EXTRA) return get_useable_count_fromex(pcard, playerid, uplayer, zone, list); else return get_useable_count_other(pcard, playerid, location, uplayer, reason, zone, list); } int32_t field::get_useable_count_fromex(card* pcard, uint8_t playerid, uint8_t uplayer, uint32_t zone, uint32_t* list) { bool use_temp_card = false; if(!pcard) { use_temp_card = true; pcard = temp_card; pcard->current.location = LOCATION_EXTRA; } int useable_count = 0; if(is_flag(DUEL_EMZONE)) useable_count = get_useable_count_fromex_rule4(pcard, playerid, uplayer, zone, list); else useable_count = get_useable_count_other(pcard, playerid, LOCATION_MZONE, uplayer, LOCATION_REASON_TOFIELD, zone, list); if(use_temp_card) pcard->current.location = 0; return useable_count; } int32_t field::get_spsummonable_count(card* pcard, uint8_t playerid, uint32_t zone, uint32_t* list) { if(pcard->current.location == LOCATION_EXTRA) return get_spsummonable_count_fromex(pcard, playerid, playerid, zone, list); else return get_tofield_count(pcard, playerid, LOCATION_MZONE, playerid, LOCATION_REASON_TOFIELD, zone, list); } int32_t field::get_spsummonable_count_fromex(card* pcard, uint8_t playerid, uint8_t uplayer, uint32_t zone, uint32_t* list) { bool use_temp_card = false; if(!pcard) { use_temp_card = true; pcard = temp_card; pcard->current.location = LOCATION_EXTRA; } int spsummonable_count = 0; if(is_flag(DUEL_EMZONE)) spsummonable_count = get_spsummonable_count_fromex_rule4(pcard, playerid, uplayer, zone, list); else spsummonable_count = get_tofield_count(pcard, playerid, LOCATION_MZONE, uplayer, LOCATION_REASON_TOFIELD, zone, list); if(use_temp_card) pcard->current.location = 0; return spsummonable_count; } int32_t field::get_useable_count_other(card* pcard, uint8_t playerid, uint8_t location, uint8_t uplayer, uint32_t reason, uint32_t zone, uint32_t* list) { int32_t count = get_tofield_count(pcard, playerid, location, uplayer, reason, zone, list); int32_t limit; if(location == LOCATION_MZONE) limit = get_mzone_limit(playerid, uplayer, reason); else limit = get_szone_limit(playerid, uplayer, reason); if(count > limit) count = limit; return count; } int32_t field::get_tofield_count(card* pcard, uint8_t playerid, uint8_t location, uint32_t uplayer, uint32_t reason, uint32_t zone, uint32_t* list) { if (location != LOCATION_MZONE && location != LOCATION_SZONE) return 0; uint32_t flag = player[playerid].disabled_location | player[playerid].used_location; if(location == LOCATION_MZONE) { flag |= ~get_forced_zones(pcard, playerid, location, uplayer, reason); flag = (flag | ~zone) & 0x1f; } else flag = ((flag >> 8) | ~zone) & 0x1f; int32_t count = 5 - field_used_count[flag]; if(location == LOCATION_MZONE) flag |= (1u << 5) | (1u << 6); if(list) *list = flag; return count; } int32_t field::get_useable_count_fromex_rule4(card* pcard, uint8_t playerid, uint8_t uplayer, uint32_t zone, uint32_t* list) { int32_t count = get_spsummonable_count_fromex_rule4(pcard, playerid, uplayer, zone, list); int32_t limit = get_mzone_limit(playerid, uplayer, LOCATION_REASON_TOFIELD); if(count > limit) count = limit; return count; } int32_t field::get_spsummonable_count_fromex_rule4(card* pcard, uint8_t playerid, uint8_t uplayer, uint32_t zone, uint32_t* list) { uint32_t flag = player[playerid].disabled_location | player[playerid].used_location; flag |= ~get_forced_zones(pcard, playerid, LOCATION_MZONE, uplayer, LOCATION_REASON_TOFIELD); if(player[playerid].list_mzone[5] && is_location_useable(playerid, LOCATION_MZONE, 6) && check_extra_link(playerid, pcard, 6)) { flag |= 1u << 5; } else if(player[playerid].list_mzone[6] && is_location_useable(playerid, LOCATION_MZONE, 5) && check_extra_link(playerid, pcard, 5)) { flag |= 1u << 6; } else if(player[playerid].list_mzone[5] || player[playerid].list_mzone[6]) { flag |= (1u << 5) | (1u << 6); } else { if(!is_location_useable(playerid, LOCATION_MZONE, 5)) flag |= 1u << 5; if(!is_location_useable(playerid, LOCATION_MZONE, 6)) flag |= 1u << 6; } uint32_t rule_zone = get_rule_zone_fromex(playerid, pcard); flag = flag | ~zone | ~rule_zone; if(list) *list = flag & 0x7f; int32_t count = 5 - field_used_count[flag & 0x1f]; if(~flag & ((1u << 5) | (1u << 6))) ++count; return count; } int32_t field::get_mzone_limit(uint8_t playerid, uint8_t uplayer, uint32_t reason) { uint32_t used_flag = player[playerid].used_location; used_flag = used_flag & 0x1f; int32_t max = 5; int32_t used_count = field_used_count[used_flag]; if(is_flag(DUEL_EMZONE)) { max = 7; if(player[playerid].list_mzone[5]) ++used_count; if(player[playerid].list_mzone[6]) ++used_count; } effect_set eset; if(uplayer < 2) filter_player_effect(playerid, EFFECT_MAX_MZONE, &eset); for(const auto& peff : eset) { pduel->lua->add_param(playerid); pduel->lua->add_param(uplayer); pduel->lua->add_param(reason); int32_t v = peff->get_value(3); if(max > v) max = v; } int32_t limit = max - used_count; return limit; } int32_t field::get_szone_limit(uint8_t playerid, uint8_t uplayer, uint32_t reason) { uint32_t used_flag = player[playerid].used_location; used_flag = (used_flag >> 8) & 0x1f; effect_set eset; if(uplayer < 2) filter_player_effect(playerid, EFFECT_MAX_SZONE, &eset); int32_t max = 5; for(const auto& peff : eset) { pduel->lua->add_param(playerid); pduel->lua->add_param(uplayer); pduel->lua->add_param(reason); int32_t v = peff->get_value(3); if(max > v) max = v; } int32_t limit = max - field_used_count[used_flag]; return limit; } int32_t field::get_forced_zones(card* pcard, uint8_t playerid, uint8_t location, uint32_t uplayer, uint32_t reason) { if(location != LOCATION_MZONE) return 0xff; effect_set eset; uint32_t res = 0xff7fff7f; if(uplayer < 2) filter_player_effect(uplayer, EFFECT_MUST_USE_MZONE, &eset); if(pcard) pcard->filter_effect(EFFECT_MUST_USE_MZONE, &eset); for(const auto& peff : eset) { if(peff->is_flag(EFFECT_FLAG_COUNT_LIMIT) && peff->count_limit == 0) continue; if(peff->operation) { pduel->lua->add_param(peff, true); pduel->lua->add_param(playerid); pduel->lua->add_param(uplayer); pduel->lua->add_param(reason); if(!pduel->lua->check_condition(peff->operation, 4)) continue; } if(peff->is_flag(EFFECT_FLAG_PLAYER_TARGET)) { pduel->lua->add_param(playerid); pduel->lua->add_param(uplayer); pduel->lua->add_param(reason); res &= peff->get_value(3); } else { pduel->lua->add_param(playerid); pduel->lua->add_param(uplayer); pduel->lua->add_param(reason); res &= peff->get_value(pcard, 3); } } if(uplayer == playerid || uplayer > 1) res &= 0xff; else (res >>= 16 )&= 0xff; return res; } uint32_t field::get_rule_zone_fromex(int32_t playerid, card* pcard) { if(is_flag(DUEL_EMZONE)) { if(is_flag(DUEL_FSX_MMZONE) && pcard && pcard->is_position(POS_FACEDOWN) && (pcard->data.type & (get_extra_deck_types() & ~TYPE_LINK))) return 0x7f; else return get_linked_zone(playerid) | (1u << 5) | (1u << 6); } else { return 0x1f; } } uint32_t field::get_linked_zone(int32_t playerid, bool free, bool actually_linked) { uint32_t zones = 0; for(auto& pcard : player[playerid].list_mzone) { if(pcard) zones |= pcard->get_linked_zone(free); } for(auto& pcard : player[playerid].list_szone) { if(pcard) zones |= pcard->get_linked_zone(free); } uint32_t oppzones = 0; for(auto& pcard : player[1 - playerid].list_mzone) { if(pcard) oppzones |= pcard->get_linked_zone(free); } for(auto& pcard : player[1 - playerid].list_szone) { if(pcard) oppzones |= pcard->get_linked_zone(free); } zones |= ((oppzones & 0xffff) << 16) | (oppzones >> 16); if(!actually_linked) { effect_set eset; uint32_t value; filter_field_effect(EFFECT_BECOME_LINKED_ZONE, &eset); for(const auto& peff : eset) { value = peff->get_value(); if(value) { zones |= (value >> (16 * playerid)) & 0x7f; } } } return zones; } void field::get_linked_cards(uint8_t self, uint8_t location1, uint8_t location2, card_set* cset) { get_cards_in_zone(cset, get_linked_zone(self), self, location1); get_cards_in_zone(cset, get_linked_zone(1 - self), 1 - self, location2); } int32_t field::check_extra_link(int32_t playerid, card* pcard, int32_t sequence) { if(!pcard) return FALSE; if(player[playerid].list_mzone[sequence]) return FALSE; uint8_t cur_controler = pcard->current.controler; uint8_t cur_location = pcard->current.location; uint8_t cur_sequence = pcard->current.sequence; uint8_t cur_position = pcard->current.position; player[playerid].list_mzone[sequence] = pcard; pcard->current.controler = playerid; pcard->current.location = LOCATION_MZONE; pcard->current.sequence = sequence; pcard->current.position = POS_FACEUP_ATTACK; int32_t ret = pcard->is_extra_link_state(); player[playerid].list_mzone[sequence] = nullptr; pcard->current.controler = cur_controler; pcard->current.location = cur_location; pcard->current.sequence = cur_sequence; pcard->current.position = cur_position; return ret; } void field::get_cards_in_zone(card_set* cset, uint32_t zone, int32_t playerid, int32_t location) const { if(!(location & LOCATION_ONFIELD)) return; if(location & LOCATION_MZONE) { uint32_t icheck = 0x1; for(auto& pcard : player[playerid].list_mzone) { if(zone & icheck) { if(pcard) cset->insert(pcard); } icheck <<= 1; } } if(location & LOCATION_SZONE) { uint32_t icheck = 0x1 << 8; for(auto& pcard : player[playerid].list_szone) { if(zone & icheck) { if(pcard) cset->insert(pcard); } icheck <<= 1; } } } void field::shuffle(uint8_t playerid, uint8_t location) { if(!(location & (LOCATION_HAND | LOCATION_DECK | LOCATION_EXTRA))) return; card_vector& to_shuffle = (location == LOCATION_HAND) ? player[playerid].list_hand : (location == LOCATION_DECK) ? player[playerid].list_main : player[playerid].list_extra; if(to_shuffle.size() == 0) return; if(location == LOCATION_HAND) { bool shuffle = false; for(auto& pcard : to_shuffle) if(!pcard->is_position(POS_FACEUP)) shuffle = true; if(!shuffle) { core.shuffle_hand_check[playerid] = false; return; } } if(location == LOCATION_HAND || !is_flag(DUEL_PSEUDO_SHUFFLE)) { int32_t upper_bound = static_cast(to_shuffle.size()); if(location == LOCATION_EXTRA) upper_bound -= player[playerid].extra_p_count; if(upper_bound > 1) { for(int32_t i = 0; i < upper_bound - 1; ++i) { auto r = pduel->get_next_integer(i, upper_bound - 1); auto* t = to_shuffle[i]; to_shuffle[i] = to_shuffle[r]; to_shuffle[r] = t; } reset_sequence(playerid, location); } } if(location == LOCATION_HAND || location == LOCATION_EXTRA) { auto message = pduel->new_message((location == LOCATION_HAND) ? MSG_SHUFFLE_HAND : MSG_SHUFFLE_EXTRA); message->write(playerid); message->write(to_shuffle.size()); for(auto& pcard : to_shuffle) message->write(pcard->data.code); if(location == LOCATION_HAND) { core.shuffle_hand_check[playerid] = false; for(auto& pcard : to_shuffle) { for(auto& i : pcard->indexer) { effect* peffect = i.first; if(peffect->is_flag(EFFECT_FLAG_CLIENT_HINT) && !peffect->is_flag(EFFECT_FLAG_PLAYER_TARGET)) { auto _message = pduel->new_message(MSG_CARD_HINT); _message->write(pcard->get_info_location()); _message->write(CHINT_DESC_ADD); _message->write(peffect->description); } } } } } else { auto message = pduel->new_message(MSG_SHUFFLE_DECK); message->write(playerid); core.shuffle_deck_check[playerid] = false; if(core.global_flag & GLOBALFLAG_DECK_REVERSE_CHECK) { card* ptop = to_shuffle.back(); if(core.deck_reversed || (ptop->current.position == POS_FACEUP_DEFENSE)) { message = pduel->new_message(MSG_DECK_TOP); message->write(playerid); message->write(0); message->write(ptop->data.code); message->write(ptop->current.position); } } } } void field::reset_sequence(uint8_t playerid, uint8_t location) { if(location & (LOCATION_ONFIELD)) return; uint32_t i = 0; switch(location) { case LOCATION_DECK: for(auto& pcard : player[playerid].list_main) pcard->current.sequence = i++; break; case LOCATION_HAND: for(auto& pcard : player[playerid].list_hand) pcard->current.sequence = i++; break; case LOCATION_EXTRA: for(auto& pcard : player[playerid].list_extra) pcard->current.sequence = i++; break; case LOCATION_GRAVE: for(auto& pcard : player[playerid].list_grave) pcard->current.sequence = i++; break; case LOCATION_REMOVED: for(auto& pcard : player[playerid].list_remove) pcard->current.sequence = i++; break; } } void field::swap_deck_and_grave(uint8_t playerid) { auto& cur_player = player[playerid]; for(auto& pcard : cur_player.list_grave) { if(core.current_chain.size() > 0) core.just_sent_cards.insert(pcard); pcard->previous.location = LOCATION_GRAVE; pcard->previous.sequence = pcard->current.sequence; pcard->enable_field_effect(false); pcard->cancel_field_effect(); } for(auto& pcard : cur_player.list_main) { if(core.current_chain.size() > 0) core.just_sent_cards.insert(pcard); pcard->previous.location = LOCATION_DECK; pcard->previous.sequence = pcard->current.sequence; pcard->enable_field_effect(false); pcard->cancel_field_effect(); } cur_player.list_grave.swap(cur_player.list_main); auto message = pduel->new_message(MSG_SWAP_GRAVE_DECK); message->write(playerid); card_vector ex; ProgressiveBuffer buff; buff.data.reserve((cur_player.list_main.size() / sizeof(uint8_t)) + 1); int i = 0; for(auto clit = cur_player.list_main.begin(); clit != cur_player.list_main.end(); ++i) { if((*clit)->is_extra_deck_monster()) { buff.bitToggle(i, true); ex.push_back(*clit); clit = cur_player.list_main.erase(clit); } else { buff.bitToggle(i, false); ++clit; } } for(auto& pcard : cur_player.list_grave) { pcard->current.position = POS_FACEUP; pcard->current.location = LOCATION_GRAVE; pcard->current.reason = REASON_EFFECT; pcard->current.reason_effect = core.reason_effect; pcard->current.reason_player = core.reason_player; pcard->apply_field_effect(); pcard->enable_field_effect(true); pcard->reset(RESET_TOGRAVE, RESET_EVENT); } for(auto& pcard : cur_player.list_main) { pcard->current.position = POS_FACEDOWN_DEFENSE; pcard->current.location = LOCATION_DECK; pcard->current.reason = REASON_EFFECT; pcard->current.reason_effect = core.reason_effect; pcard->current.reason_player = core.reason_player; pcard->apply_field_effect(); pcard->enable_field_effect(true); pcard->reset(RESET_TODECK, RESET_EVENT); raise_single_event(pcard, nullptr, EVENT_LEAVE_GRAVE, pduel->game_field->core.reason_effect, 0, pduel->game_field->core.reason_player, 0, 0); } for(auto& pcard : ex) { pcard->current.position = POS_FACEDOWN_DEFENSE; pcard->current.location = LOCATION_EXTRA; pcard->current.reason = REASON_EFFECT; pcard->current.reason_effect = core.reason_effect; pcard->current.reason_player = core.reason_player; pcard->apply_field_effect(); pcard->enable_field_effect(true); pcard->reset(RESET_TODECK, RESET_EVENT); raise_single_event(pcard, nullptr, EVENT_LEAVE_GRAVE, pduel->game_field->core.reason_effect, 0, pduel->game_field->core.reason_player, 0, 0); } message->write(cur_player.list_extra.size() - cur_player.extra_p_count); cur_player.list_extra.insert(cur_player.list_extra.end() - cur_player.extra_p_count, ex.begin(), ex.end()); reset_sequence(playerid, LOCATION_GRAVE); reset_sequence(playerid, LOCATION_EXTRA); message->write(buff.data.size()); message->write(buff.data.data(), buff.data.size()); shuffle(playerid, LOCATION_DECK); if(cur_player.list_main.size() || ex.size()) { card_set cset(ex.begin(), ex.end()); cset.insert(cur_player.list_main.begin(), cur_player.list_main.end()); raise_event(std::move(cset), EVENT_LEAVE_GRAVE, pduel->game_field->core.reason_effect, 0, pduel->game_field->core.reason_player, 0, 0); process_single_event(); process_instant_event(); } } void field::reverse_deck(uint8_t playerid) { auto count = player[playerid].list_main.size(); if(count == 0) return; for(size_t i = 0; i < count / 2; ++i) { card* tmp = player[playerid].list_main[i]; tmp->current.sequence = static_cast(count - 1 - i); player[playerid].list_main[count - 1 - i]->current.sequence = static_cast(i); player[playerid].list_main[i] = player[playerid].list_main[count - 1 - i]; player[playerid].list_main[count - 1 - i] = tmp; } } int field::get_player_count(uint8_t playerid) { return static_cast(player[playerid].extra_lists_main.size() + 1); } void field::tag_swap(uint8_t playerid) { if(player[playerid].extra_lists_main.empty()) return; //main for(auto& pcard : player[playerid].list_main) { pcard->enable_field_effect(false); pcard->cancel_field_effect(); } std::swap(player[playerid].list_main, player[playerid].extra_lists_main[player[playerid].tag_index]); for(auto& pcard : player[playerid].list_main) { pcard->apply_field_effect(); pcard->enable_field_effect(true); } //hand for(auto& pcard : player[playerid].list_hand) { pcard->enable_field_effect(false); pcard->cancel_field_effect(); } std::swap(player[playerid].list_hand, player[playerid].extra_lists_hand[player[playerid].tag_index]); for(auto& pcard : player[playerid].list_hand) { pcard->apply_field_effect(); pcard->enable_field_effect(true); } //extra for(auto& pcard : player[playerid].list_extra) { pcard->enable_field_effect(false); pcard->cancel_field_effect(); } std::swap(player[playerid].list_extra, player[playerid].extra_lists_extra[player[playerid].tag_index]); std::swap(player[playerid].extra_p_count, player[playerid].extra_extra_p_count[player[playerid].tag_index]); for(auto& pcard : player[playerid].list_extra) { pcard->apply_field_effect(); pcard->enable_field_effect(true); } auto message = pduel->new_message(MSG_TAG_SWAP); message->write(playerid); message->write(player[playerid].list_main.size()); message->write(player[playerid].list_extra.size()); message->write(player[playerid].extra_p_count); message->write(player[playerid].list_hand.size()); if(core.deck_reversed && player[playerid].list_main.size()) message->write(player[playerid].list_main.back()->data.code); else message->write(0); for(auto& pcard : player[playerid].list_hand) { message->write(pcard->data.code); message->write(pcard->current.position); } for(auto& pcard : player[playerid].list_extra) { message->write(pcard->data.code); message->write(pcard->current.position); } player[playerid].tag_index = (player[playerid].tag_index + 1) % player[playerid].extra_lists_main.size(); } bool field::relay_check(uint8_t playerid) { if (player[playerid].exchanges >= player[playerid].extra_lists_main.size()) return false; ++player[playerid].exchanges; core.force_turn_end = true; player[playerid].recharge = true; return true; } void field::next_player(uint8_t playerid) { //main for(auto& pcard : player[playerid].list_main) { pcard->enable_field_effect(false); pcard->cancel_field_effect(); } std::swap(player[playerid].list_main, player[playerid].extra_lists_main[player[playerid].exchanges - 1]); for(auto& pcard : player[playerid].list_main) { pcard->apply_field_effect(); pcard->enable_field_effect(true); } //hand for(auto& pcard : player[playerid].list_hand) { pcard->enable_field_effect(false); pcard->cancel_field_effect(); } std::swap(player[playerid].list_hand, player[playerid].extra_lists_hand[player[playerid].exchanges - 1]); for(auto& pcard : player[playerid].list_hand) { pcard->apply_field_effect(); pcard->enable_field_effect(true); } //extra for(auto& pcard : player[playerid].list_extra) { pcard->enable_field_effect(false); pcard->cancel_field_effect(); } std::swap(player[playerid].list_extra, player[playerid].extra_lists_extra[player[playerid].exchanges - 1]); std::swap(player[playerid].extra_p_count, player[playerid].extra_extra_p_count[player[playerid].exchanges - 1]); for(auto& pcard : player[playerid].list_extra) { pcard->apply_field_effect(); pcard->enable_field_effect(true); } auto message = pduel->new_message(MSG_TAG_SWAP); message->write(playerid); message->write(player[playerid].list_main.size()); message->write(player[playerid].list_extra.size()); message->write(player[playerid].extra_p_count); message->write(player[playerid].list_hand.size()); if (core.deck_reversed && player[playerid].list_main.size()) message->write(player[playerid].list_main.back()->data.code); else message->write(0); for(auto& pcard : player[playerid].list_hand) { message->write(pcard->data.code); message->write(pcard->current.position); } for(auto& pcard : player[playerid].list_extra) { message->write(pcard->data.code); message->write(pcard->current.position); } player[playerid].lp = player[playerid].start_lp; message = pduel->new_message(MSG_LPUPDATE); message->write(playerid); message->write(player[playerid].start_lp); player[playerid].recharge = false; } bool field::is_flag(uint64_t flag) const { return (core.duel_options & flag) == flag; } bool field::has_separate_pzone(uint8_t p) const { (void)p; return is_flag(DUEL_SEPARATE_PZONE); } uint32_t field::get_pzone_zones_flag() const { uint32_t flag = 0; if(!has_separate_pzone(0)) flag |= 0xC000u; if(!has_separate_pzone(1)) flag |= 0xC000u << 16; return flag; } uint8_t field::get_pzone_index(uint8_t seq, uint8_t p) const { if(seq > 1) return 0; if(has_separate_pzone(p)) // 6 and 7 return seq + 6; if(is_flag(DUEL_3_COLUMNS_FIELD))// 1 and 3 return seq * 2 + 1; return seq * 4; // 0 and 4 } uint32_t field::get_extra_deck_types() const { if(is_flag(DUEL_EXTRA_DECK_RITUAL)) return TYPE_FUSION | TYPE_SYNCHRO | TYPE_XYZ | TYPE_LINK | TYPE_RITUAL; return TYPE_FUSION | TYPE_SYNCHRO | TYPE_XYZ | TYPE_LINK; } void field::add_effect(effect* peffect, uint8_t owner_player) { if (!peffect->handler) { peffect->flag[0] |= EFFECT_FLAG_FIELD_ONLY; peffect->handler = peffect->owner; peffect->effect_owner = owner_player; peffect->id = peffect->initial_id = infos.field_id++; } peffect->card_type = peffect->owner->data.type; effect_container::iterator it; if (!(peffect->type & EFFECT_TYPE_ACTIONS)) { it = effects.aura_effect.emplace(peffect->code, peffect); if(peffect->code == EFFECT_SELF_TOGRAVE) core.global_flag |= GLOBALFLAG_SELF_TOGRAVE; else if(peffect->code == EFFECT_SPSUMMON_COUNT_LIMIT) effects.spsummon_count_eff.insert(peffect); else if(peffect->code == EFFECT_REMOVE_BRAINWASHING) core.global_flag |= GLOBALFLAG_BRAINWASHING_CHECK; else if(peffect->code == EFFECT_REVERSE_DECK) core.global_flag |= GLOBALFLAG_DECK_REVERSE_CHECK; if(peffect->type & EFFECT_TYPE_GRANT) effects.grant_effect.emplace(peffect, field_effect::gain_effects()); } else { if (peffect->type & EFFECT_TYPE_IGNITION) it = effects.ignition_effect.emplace(peffect->code, peffect); else if (peffect->type & EFFECT_TYPE_TRIGGER_O && peffect->type & EFFECT_TYPE_FIELD) it = effects.trigger_o_effect.emplace(peffect->code, peffect); else if (peffect->type & EFFECT_TYPE_TRIGGER_F && peffect->type & EFFECT_TYPE_FIELD) it = effects.trigger_f_effect.emplace(peffect->code, peffect); else if (peffect->type & EFFECT_TYPE_QUICK_O) it = effects.quick_o_effect.emplace(peffect->code, peffect); else if (peffect->type & EFFECT_TYPE_QUICK_F) it = effects.quick_f_effect.emplace(peffect->code, peffect); else if (peffect->type & EFFECT_TYPE_ACTIVATE) it = effects.activate_effect.emplace(peffect->code, peffect); else if (peffect->type & EFFECT_TYPE_CONTINUOUS) it = effects.continuous_effect.emplace(peffect->code, peffect); } if(peffect->code == EVENT_DETACH_MATERIAL) core.global_flag |= GLOBALFLAG_DETACH_EVENT; effects.indexer.emplace(peffect, it); if(peffect->is_flag(EFFECT_FLAG_FIELD_ONLY)) { if(peffect->is_disable_related()) update_disable_check_list(peffect); if(peffect->is_flag(EFFECT_FLAG_OATH)) effects.oath.emplace(peffect, core.reason_effect); if(peffect->reset_flag & RESET_PHASE) effects.pheff.insert(peffect); if(peffect->reset_flag & RESET_CHAIN) effects.cheff.insert(peffect); if(peffect->is_flag(EFFECT_FLAG_COUNT_LIMIT)) effects.rechargeable.insert(peffect); if(peffect->is_flag(EFFECT_FLAG_PLAYER_TARGET) && peffect->is_flag(EFFECT_FLAG_CLIENT_HINT)) { bool target_player[2] = {false, false}; if(peffect->s_range) target_player[owner_player] = true; if(peffect->o_range) target_player[1 - owner_player] = true; if(target_player[0]) { auto message = pduel->new_message(MSG_PLAYER_HINT); message->write(0); message->write(PHINT_DESC_ADD); message->write(peffect->description); } if(target_player[1]) { auto message = pduel->new_message(MSG_PLAYER_HINT); message->write(1); message->write(PHINT_DESC_ADD); message->write(peffect->description); } } } } void field::remove_effect(effect* peffect) { auto eit = effects.indexer.find(peffect); if (eit == effects.indexer.end()) return; auto it = eit->second; if (!(peffect->type & EFFECT_TYPE_ACTIONS)) { effects.aura_effect.erase(it); if(peffect->code == EFFECT_SPSUMMON_COUNT_LIMIT) effects.spsummon_count_eff.erase(peffect); if(peffect->type & EFFECT_TYPE_GRANT) erase_grant_effect(peffect); } else { if (peffect->type & EFFECT_TYPE_IGNITION) effects.ignition_effect.erase(it); else if (peffect->type & EFFECT_TYPE_TRIGGER_O) effects.trigger_o_effect.erase(it); else if (peffect->type & EFFECT_TYPE_TRIGGER_F) effects.trigger_f_effect.erase(it); else if (peffect->type & EFFECT_TYPE_QUICK_O) effects.quick_o_effect.erase(it); else if (peffect->type & EFFECT_TYPE_QUICK_F) effects.quick_f_effect.erase(it); else if (peffect->type & EFFECT_TYPE_ACTIVATE) effects.activate_effect.erase(it); else if (peffect->type & EFFECT_TYPE_CONTINUOUS) effects.continuous_effect.erase(it); } effects.indexer.erase(peffect); if(peffect->is_flag(EFFECT_FLAG_FIELD_ONLY)) { if(peffect->is_disable_related()) update_disable_check_list(peffect); if(peffect->is_flag(EFFECT_FLAG_OATH)) effects.oath.erase(peffect); if(peffect->reset_flag & RESET_PHASE) effects.pheff.erase(peffect); if(peffect->reset_flag & RESET_CHAIN) effects.cheff.erase(peffect); if(peffect->is_flag(EFFECT_FLAG_COUNT_LIMIT)) effects.rechargeable.erase(peffect); core.reseted_effects.insert(peffect); if(peffect->is_flag(EFFECT_FLAG_PLAYER_TARGET) && peffect->is_flag(EFFECT_FLAG_CLIENT_HINT)) { bool target_player[2] = {false, false}; if(peffect->s_range) target_player[peffect->effect_owner] = true; if(peffect->o_range) target_player[1 - peffect->effect_owner] = true; if(target_player[0]) { auto message = pduel->new_message(MSG_PLAYER_HINT); message->write(0); message->write(PHINT_DESC_REMOVE); message->write(peffect->description); } if(target_player[1]) { auto message = pduel->new_message(MSG_PLAYER_HINT); message->write(1); message->write(PHINT_DESC_REMOVE); message->write(peffect->description); } } } } void field::remove_oath_effect(effect* reason_effect) { for(auto oeit = effects.oath.begin(); oeit != effects.oath.end();) { auto rm = oeit++; if(rm->second == reason_effect) { effect* peffect = rm->first; effects.oath.erase(rm); if(peffect->is_flag(EFFECT_FLAG_FIELD_ONLY)) remove_effect(peffect); else peffect->handler->remove_effect(peffect); } } } void field::release_oath_relation(effect* reason_effect) { for(auto& oeit : effects.oath) if(oeit.second == reason_effect) oeit.second = nullptr; } void field::reset_phase(uint32_t phase) { for(auto eit = effects.pheff.begin(); eit != effects.pheff.end();) { auto rm = eit++; if((*rm)->reset(phase, RESET_PHASE)) { if((*rm)->is_flag(EFFECT_FLAG_FIELD_ONLY)) remove_effect((*rm)); else (*rm)->handler->remove_effect((*rm)); } } } void field::reset_chain() { for(const auto& ch_lim_p : core.chain_limit_p) ensure_luaL_stack(luaL_unref, pduel->lua->lua_state, LUA_REGISTRYINDEX, ch_lim_p.function); core.chain_limit_p.clear(); core.effect_count_code_chain.clear(); for(auto eit = effects.cheff.begin(); eit != effects.cheff.end();) { auto rm = eit++; if((*rm)->is_flag(EFFECT_FLAG_FIELD_ONLY)) remove_effect((*rm)); else (*rm)->handler->remove_effect((*rm)); } } processor::effect_count_map& field::get_count_map(uint8_t flag) { if(flag & EFFECT_COUNT_CODE_DUEL) return core.effect_count_code_duel; if(flag & EFFECT_COUNT_CODE_CHAIN) return core.effect_count_code_chain; return core.effect_count_code; } static inline uint64_t generate_count_map_key(uint32_t code, uint8_t flag, uint8_t hopt_index, uint8_t playerid) { return static_cast(code) << 32 | (hopt_index << 16 | flag << 8 | playerid); } void field::add_effect_code(uint32_t code, uint8_t flag, uint8_t hopt_index, uint8_t playerid) { auto& count_map = get_count_map(flag); const auto key = generate_count_map_key(code, hopt_index, flag, playerid); ++count_map[key]; } uint32_t field::get_effect_code(uint32_t code, uint8_t flag, uint8_t hopt_index, uint8_t playerid) { const auto& count_map = get_count_map(flag); const auto key = generate_count_map_key(code, hopt_index, flag, playerid); auto iter = count_map.find(key); if(iter == count_map.end()) return 0; return iter->second; } void field::dec_effect_code(uint32_t code, uint8_t flag, uint8_t hopt_index, uint8_t playerid) { auto& count_map = get_count_map(flag); const auto key = generate_count_map_key(code, hopt_index, flag, playerid); auto iter = count_map.find(key); if(iter == count_map.end()) return; if(iter->second != 0) --iter->second; } void field::filter_field_effect(uint32_t code, effect_set* eset, bool sort) { auto rg = effects.aura_effect.equal_range(code); for (; rg.first != rg.second; ) { effect* peffect = rg.first->second; ++rg.first; if (peffect->is_available()) eset->push_back(peffect); } if(sort) std::sort(eset->begin(), eset->end(), effect_sort_id); } void field::filter_affected_cards(effect* peffect, card_set* cset) { if((peffect->type & EFFECT_TYPE_ACTIONS) || !(peffect->type & EFFECT_TYPE_FIELD) || peffect->is_flag(EFFECT_FLAG_PLAYER_TARGET | EFFECT_FLAG_SPSUM_PARAM)) return; uint8_t self = peffect->get_handler_player(); if(self == PLAYER_NONE) return; std::vector cvec; uint16_t range = peffect->s_range; for(uint32_t p = 0; p < 2; ++p) { if(range & LOCATION_MZONE) cvec.push_back(&player[self].list_mzone); if(range & LOCATION_SZONE) cvec.push_back(&player[self].list_szone); if(range & LOCATION_GRAVE) cvec.push_back(&player[self].list_grave); if(range & LOCATION_REMOVED) cvec.push_back(&player[self].list_remove); if(range & LOCATION_HAND) cvec.push_back(&player[self].list_hand); if(range & LOCATION_DECK) cvec.push_back(&player[self].list_main); if(range & LOCATION_EXTRA) cvec.push_back(&player[self].list_extra); range = peffect->o_range; self = 1 - self; } for(auto& cvit : cvec) { for(auto& pcard : *cvit) { if(pcard && peffect->is_target(pcard)) cset->insert(pcard); } } } void field::filter_inrange_cards(effect* peffect, card_set* cset) { if(peffect->is_flag(EFFECT_FLAG_PLAYER_TARGET | EFFECT_FLAG_SPSUM_PARAM)) return; uint8_t self = peffect->get_handler_player(); if(self == PLAYER_NONE) return; uint16_t range = peffect->s_range; std::vector cvec; for(uint32_t p = 0; p < 2; ++p) { if(range & LOCATION_MZONE) cvec.push_back(&player[self].list_mzone); if(range & LOCATION_SZONE) cvec.push_back(&player[self].list_szone); if(range & LOCATION_GRAVE) cvec.push_back(&player[self].list_grave); if(range & LOCATION_REMOVED) cvec.push_back(&player[self].list_remove); if(range & LOCATION_HAND) cvec.push_back(&player[self].list_hand); if(range & LOCATION_DECK) cvec.push_back(&player[self].list_main); if(range & LOCATION_EXTRA) cvec.push_back(&player[self].list_extra); range = peffect->o_range; self = 1 - self; } for(auto& cvit : cvec) { for(auto& pcard : *cvit) { if(pcard && peffect->is_fit_target_function(pcard)) cset->insert(pcard); } } } void field::filter_player_effect(uint8_t playerid, uint32_t code, effect_set* eset, bool sort) { auto rg = effects.aura_effect.equal_range(code); for (; rg.first != rg.second; ++rg.first) { effect* peffect = rg.first->second; if (peffect->is_target_player(playerid) && peffect->is_available()) eset->push_back(peffect); } if(sort) std::sort(eset->begin(), eset->end(), effect_sort_id); } int32_t field::filter_matching_card(int32_t findex, uint8_t self, uint32_t location1, uint32_t location2, group* pgroup, card* pexception, group* pexgroup, uint32_t extraargs, card** pret, int32_t fcount, bool is_target) { if(self != 0 && self != 1) return FALSE; int32_t count = 0; auto checkc = [&](auto* pcard, bool(*extrafil)(card* pcard)=nullptr)->bool { if(pcard && (!extrafil || extrafil(pcard)) && pcard != pexception && !(pexgroup && pexgroup->has_card(pcard)) && (!findex || pduel->lua->check_matching(pcard, findex, extraargs)) && (!is_target || pcard->is_capable_be_effect_target(core.reason_effect, core.reason_player))) { if(pret) { *pret = pcard; return true; } ++count; if(fcount && count >= fcount) return true; if(pgroup) pgroup->container.insert(pcard); } return false; }; auto mzonechk = [&checkc](auto pcard)->bool { return checkc(pcard, [](auto pcard)->bool {return !pcard->get_status(STATUS_SUMMONING | STATUS_SUMMON_DISABLED | STATUS_SPSUMMON_STEP); }); }; auto szonechk = [&checkc](auto pcard)->bool { return checkc(pcard, [](auto pcard)->bool {return !pcard->is_status(STATUS_ACTIVATE_DISABLED); }); }; auto pzonechk = [&checkc](auto pcard)->bool { return checkc(pcard, [](auto pcard)->bool {return pcard->current.pzone && !pcard->is_status(STATUS_ACTIVATE_DISABLED); }); }; auto check_list = [](const auto& list, auto func)->bool { return std::find_if(list.begin(), list.end(), func) != list.end(); }; for(uint32_t p = 0, location = location1; p < 2; ++p, location = location2, self = 1 - self) { if(location & LOCATION_MZONE) { if(check_list(player[self].list_mzone, mzonechk)) return TRUE; } else { if(location & LOCATION_MMZONE) { const auto mzonebegin = player[self].list_mzone.cbegin(); const auto mzoneend = mzonebegin + 5; if(std::find_if(mzonebegin, mzoneend, mzonechk) != mzoneend) return TRUE; } if(location & LOCATION_EMZONE) { auto mzonebegin = player[self].list_mzone.cbegin() + 5; auto mzoneend = mzonebegin + 2; if(is_flag(DUEL_3_COLUMNS_FIELD)) { ++mzonebegin; --mzoneend; } if(std::find_if(mzonebegin, mzoneend, mzonechk) != mzoneend) return TRUE; } } if(location & LOCATION_SZONE) { if(check_list(player[self].list_szone, szonechk)) return TRUE; } else { if(location & LOCATION_STZONE) { auto szonebegin = player[self].list_szone.cbegin(); auto szoneend = szonebegin + 5; if(is_flag(DUEL_3_COLUMNS_FIELD)) { ++szonebegin; --szoneend; } if(std::find_if(szonebegin, szoneend, szonechk) != szoneend) return TRUE; } if((location & LOCATION_FZONE) && szonechk(player[self].list_szone[5])) return TRUE; if((location & LOCATION_PZONE) && (pzonechk(player[self].list_szone[get_pzone_index(0, self)]) || pzonechk(player[self].list_szone[get_pzone_index(1, self)]))) return TRUE; } if((location & LOCATION_DECK) && check_list(player[self].list_main, checkc)) return TRUE; if((location & LOCATION_EXTRA) && check_list(player[self].list_extra, checkc)) return TRUE; if((location & LOCATION_HAND) && check_list(player[self].list_hand, checkc)) return TRUE; if((location & LOCATION_GRAVE) && check_list(player[self].list_grave, checkc)) return TRUE; if((location & LOCATION_REMOVED) && check_list(player[self].list_remove, checkc)) return TRUE; } return FALSE; } // Duel.GetFieldGroup(), Duel.GetFieldGroupCount() int32_t field::filter_field_card(uint8_t self, uint32_t location1, uint32_t location2, group* pgroup) { if(self != 0 && self != 1) return 0; uint32_t location = location1; size_t count = 0; for(uint32_t p = 0; p < 2; ++p, location = location2, self = 1 - self) { if(location & LOCATION_MZONE) { for(auto& pcard : player[self].list_mzone) { if(pcard && !pcard->get_status(STATUS_SUMMONING | STATUS_SPSUMMON_STEP)) { if(pgroup) pgroup->container.insert(pcard); ++count; } } } else { if(location & LOCATION_MMZONE) { auto mzonebegin = player[self].list_mzone.cbegin(); auto mzoneend = mzonebegin + 5; if(is_flag(DUEL_3_COLUMNS_FIELD)) { ++mzonebegin; --mzoneend; } for(; mzonebegin != mzoneend; ++mzonebegin) { auto* pcard = *mzonebegin; if(pcard) { if(pgroup) pgroup->container.insert(pcard); ++count; } } } if(location & LOCATION_EMZONE) { for(auto it = player[self].list_mzone.cbegin() + 5, end = it + 2; it != end; ++it) { auto* pcard = *it; if(pcard) { if(pgroup) pgroup->container.insert(pcard); ++count; } } } } if(location & LOCATION_SZONE) { for(auto& pcard : player[self].list_szone) { if(pcard) { if(pgroup) pgroup->container.insert(pcard); ++count; } } } else { if(location & LOCATION_STZONE) { auto szonebegin = player[self].list_szone.cbegin(); auto szoneend = szonebegin + 5; if(is_flag(DUEL_3_COLUMNS_FIELD)) { ++szonebegin; --szoneend; } for(; szonebegin != szoneend; ++szonebegin) { auto* pcard = *szonebegin; if(pcard) { if(pgroup) pgroup->container.insert(pcard); ++count; } } } if(location & LOCATION_FZONE) { card* pcard = player[self].list_szone[5]; if(pcard) { if(pgroup) pgroup->container.insert(pcard); ++count; } } if(location & LOCATION_PZONE) { for(int32_t i = 0; i < 2; ++i) { card* pcard = player[self].list_szone[get_pzone_index(i, self)]; if(pcard && pcard->current.pzone) { if(pgroup) pgroup->container.insert(pcard); ++count; } } } } if(location & LOCATION_HAND) { if(pgroup) pgroup->container.insert(player[self].list_hand.begin(), player[self].list_hand.end()); count += player[self].list_hand.size(); } if(location & LOCATION_DECK) { if(pgroup) pgroup->container.insert(player[self].list_main.rbegin(), player[self].list_main.rend()); count += player[self].list_main.size(); } if(location & LOCATION_EXTRA) { if(pgroup) pgroup->container.insert(player[self].list_extra.rbegin(), player[self].list_extra.rend()); count += player[self].list_extra.size(); } if(location & LOCATION_GRAVE) { if(pgroup) pgroup->container.insert(player[self].list_grave.rbegin(), player[self].list_grave.rend()); count += player[self].list_grave.size(); } if(location & LOCATION_REMOVED) { if(pgroup) pgroup->container.insert(player[self].list_remove.rbegin(), player[self].list_remove.rend()); count += player[self].list_remove.size(); } } return static_cast(count); } effect* field::is_player_affected_by_effect(uint8_t playerid, uint32_t code) { auto rg = effects.aura_effect.equal_range(code); for (; rg.first != rg.second; ++rg.first) { effect* peffect = rg.first->second; if (peffect->is_target_player(playerid) && peffect->is_available()) return peffect; } return nullptr; } void field::get_player_effect(uint8_t playerid, uint32_t code, effect_set* eset) { for (auto rg = effects.aura_effect.begin(); rg != effects.aura_effect.end(); ++rg) { effect* peffect = rg->second; if((code == 0 || peffect->code == code) && peffect->is_target_player(playerid) && peffect->is_available()) eset->push_back(peffect); } } int32_t field::get_release_list(uint8_t playerid, card_set* release_list, card_set* ex_list, card_set* ex_list_oneof, int32_t use_hand, int32_t fun, int32_t exarg, card* exc, group* exg, bool use_oppo, uint32_t reason) { uint32_t rcount = 0; for(auto& pcard : player[playerid].list_mzone) { if(pcard && pcard != exc && !(exg && exg->has_card(pcard)) && pcard->is_releasable_by_nonsummon(playerid, reason) && (!fun || pduel->lua->check_matching(pcard, fun, exarg))) { if(release_list) release_list->insert(pcard); pcard->release_param = 1; ++rcount; } } if(use_hand) { for(auto& pcard : player[playerid].list_hand) { if(pcard && pcard != exc && !(exg && exg->has_card(pcard)) && pcard->is_releasable_by_nonsummon(playerid, reason) && (!fun || pduel->lua->check_matching(pcard, fun, exarg))) { if(release_list) release_list->insert(pcard); pcard->release_param = 1; ++rcount; } } } int32_t ex_oneof_max = 0; if(use_oppo) { for(auto& pcard : player[1 - playerid].list_mzone) { if(pcard && pcard != exc && !(exg && exg->has_card(pcard)) && (pcard->is_position(POS_FACEUP) || !fun) && pcard->is_releasable_by_nonsummon(playerid, reason) && (!fun || pduel->lua->check_matching(pcard, fun, exarg))) { if(release_list) release_list->insert(pcard); pcard->release_param = 1; ++rcount; } } } else { for(auto& pcard : player[1 - playerid].list_mzone) { if(pcard && pcard != exc && !(exg && exg->has_card(pcard)) && (pcard->is_position(POS_FACEUP) || !fun) && pcard->is_releasable_by_nonsummon(playerid, reason) && (!fun || pduel->lua->check_matching(pcard, fun, exarg))) { pcard->release_param = 1; if(pcard->is_affected_by_effect(EFFECT_EXTRA_RELEASE)) { if(ex_list) ex_list->insert(pcard); ++rcount; } else { effect* peffect = pcard->is_affected_by_effect(EFFECT_EXTRA_RELEASE_NONSUM); if(!peffect || (peffect->is_flag(EFFECT_FLAG_COUNT_LIMIT) && peffect->count_limit == 0)) continue; pduel->lua->add_param(core.reason_effect); pduel->lua->add_param(reason); pduel->lua->add_param(core.reason_player); if(!peffect->check_value_condition(3)) continue; if(ex_list_oneof) ex_list_oneof->insert(pcard); ex_oneof_max = 1; } } } } return rcount + ex_oneof_max; } int32_t field::check_release_list(uint8_t playerid, int32_t min, int32_t /*max*/, int32_t use_hand, int32_t fun, int32_t exarg, card* exc, group* exg, bool check_field, uint8_t to_player, uint8_t zone, card* to_check, bool use_oppo, uint32_t reason) { card_set relcard; //card_set relcard_must; card_set relcard_oneof; bool has_to_choose_one = false; card_set must_choose_one; int32_t rcount = get_release_list(playerid, &relcard, &relcard, &relcard_oneof, use_hand, fun, exarg, exc, exg, use_oppo, reason); if(check_field) { int32_t ct = 0; zone &= (0x1f & get_forced_zones(to_check, playerid, LOCATION_MZONE, to_player, LOCATION_REASON_TOFIELD)); ct = get_useable_count(to_check, playerid, LOCATION_MZONE, to_player, LOCATION_REASON_TOFIELD, zone); if(ct < min) { has_to_choose_one = true; for(auto& pcard : relcard) { if((pcard->current.location == LOCATION_MZONE && pcard->current.controler == playerid && ((zone >> pcard->current.sequence) & 1))) must_choose_one.insert(pcard); } } } if(has_to_choose_one && must_choose_one.empty()) return FALSE; bool has_oneof = false; for(auto& pcard : core.must_select_cards) { auto it = relcard.find(pcard); if(it != relcard.end()) relcard.erase(it); else if(!has_oneof && relcard_oneof.find(pcard) != relcard_oneof.end()) has_oneof = true; else return FALSE; } rcount = (int32_t)relcard.size();// +relcard_must.size(); if(!has_oneof && !relcard_oneof.empty()) ++rcount; return (rcount >= min); } // return: the max release count of mg or all monsters on field int32_t field::get_summon_release_list(card* target, card_set* release_list, card_set* ex_list, card_set* ex_list_oneof, group* mg, uint32_t ex, uint32_t releasable, uint32_t pos) { uint8_t p = target->current.controler; card_set ex_tribute; effect_set eset; target->filter_effect(EFFECT_ADD_EXTRA_TRIBUTE, &eset); for(const auto& peff : eset) { if(peff->get_value() & pos) filter_inrange_cards(peff, &ex_tribute); } uint32_t rcount = 0; for(auto& pcard : player[p].list_mzone) { if(pcard && ((releasable >> pcard->current.sequence) & 1) && pcard->is_releasable_by_summon(p, target)) { if(mg && !mg->has_card(pcard)) continue; if(release_list) release_list->insert(pcard); if(pcard->is_affected_by_effect(EFFECT_TRIPLE_TRIBUTE, target)) pcard->release_param = 3; else if (pcard->is_affected_by_effect(EFFECT_DOUBLE_TRIBUTE, target)) pcard->release_param = 2; else pcard->release_param = 1; rcount += pcard->release_param; } } uint32_t ex_oneof_max = 0; for(auto& pcard : player[1 - p].list_mzone) { if(!pcard || !((releasable >> (pcard->current.sequence + 16)) & 1) || !pcard->is_releasable_by_summon(p, target)) continue; if (mg && !mg->has_card(pcard)) continue; if(pcard->is_affected_by_effect(EFFECT_TRIPLE_TRIBUTE, target)) pcard->release_param = 3; else if (pcard->is_affected_by_effect(EFFECT_DOUBLE_TRIBUTE, target)) pcard->release_param = 2; else pcard->release_param = 1; if(pcard->is_affected_by_effect(EFFECT_EXTRA_RELEASE)) { if(ex_list) ex_list->insert(pcard); rcount += pcard->release_param; } else if(ex || ex_tribute.find(pcard) != ex_tribute.end()) { if(release_list) release_list->insert(pcard); rcount += pcard->release_param; } else { effect* peffect = pcard->is_affected_by_effect(EFFECT_EXTRA_RELEASE_SUM); if(!peffect || (peffect->is_flag(EFFECT_FLAG_COUNT_LIMIT) && peffect->count_limit == 0)) continue; if(ex_list_oneof) ex_list_oneof->insert(pcard); if(ex_oneof_max < pcard->release_param) ex_oneof_max = pcard->release_param; } } for(auto& pcard : ex_tribute) { if(pcard->current.location == LOCATION_MZONE || !pcard->is_releasable_by_summon(p, target)) continue; if(release_list) release_list->insert(pcard); if(pcard->is_affected_by_effect(EFFECT_TRIPLE_TRIBUTE, target)) pcard->release_param = 3; else if (pcard->is_affected_by_effect(EFFECT_DOUBLE_TRIBUTE, target)) pcard->release_param = 2; else pcard->release_param = 1; rcount += pcard->release_param; } return rcount + ex_oneof_max; } int32_t field::get_summon_count_limit(uint8_t playerid) { if(is_flag(DUEL_UNLIMITED_SUMMONS)) { return INT32_MAX - 100; } effect_set eset; filter_player_effect(playerid, EFFECT_SET_SUMMON_COUNT_LIMIT, &eset); int32_t count = 1; for(const auto& peff : eset) { int32_t c = peff->get_value(); if(c > count) count = c; } return count; } int32_t field::get_draw_count(uint8_t playerid) { effect_set eset; filter_player_effect(playerid, EFFECT_DRAW_COUNT, &eset); int32_t count = player[playerid].draw_count; for(const auto& peffect : eset) { int32_t c = peffect->get_value(); if(c == 0) return 0; if(c > count) count = c; } return count; } void field::get_ritual_material(uint8_t playerid, effect* peffect, card_set* material, bool check_level) { auto mzonecheck = [&](card* pcard) { return (!check_level || pcard->get_level() > 0) && pcard->is_affect_by_effect(peffect) && pcard->is_releasable_by_effect(playerid, peffect); }; for(auto& pcard : player[playerid].list_mzone) { if(pcard && mzonecheck(pcard) && pcard->is_releasable_by_nonsummon(playerid, REASON_EFFECT)) material->insert(pcard); } for(auto& pcard : player[1 - playerid].list_mzone) { if(pcard && pcard->is_position(POS_FACEUP) && mzonecheck(pcard) && pcard->is_releasable_by_nonsummon(playerid, REASON_EFFECT) && pcard->is_affected_by_effect(EFFECT_EXTRA_RELEASE)) material->insert(pcard); } for(auto& pcard : player[playerid].list_hand) if((pcard->data.type & TYPE_MONSTER) && pcard->is_releasable_by_nonsummon(playerid, REASON_EFFECT)) material->insert(pcard); for(auto& pcard : player[playerid].list_grave) if((pcard->data.type & TYPE_MONSTER) && pcard->is_affected_by_effect(EFFECT_EXTRA_RITUAL_MATERIAL) && pcard->is_removeable(playerid, POS_FACEUP, REASON_EFFECT)) material->insert(pcard); for(auto& pcard : player[playerid].list_main) if(pcard->is_affected_by_effect(EFFECT_EXTRA_RITUAL_MATERIAL) && (!check_level || pcard->get_level() > 0)) material->insert(pcard); for(auto& pcard : player[playerid].list_extra) if(pcard->is_affected_by_effect(EFFECT_EXTRA_RITUAL_MATERIAL) && (!check_level || pcard->get_level() > 0)) material->insert(pcard); for(auto& pcard : player[playerid].list_mzone) { if(!pcard) continue; for(auto& ppcard : pcard->xyz_materials) if(ppcard->is_affected_by_effect(EFFECT_EXTRA_RITUAL_MATERIAL)) material->insert(ppcard); } } void field::get_fusion_material(uint8_t playerid, card_set* material) { for(auto& pcard : player[playerid].list_mzone) { if(pcard) material->insert(pcard); } for(auto& pcard : player[playerid].list_szone) { if(pcard && pcard->is_affected_by_effect(EFFECT_EXTRA_FUSION_MATERIAL)) material->insert(pcard); } for(auto& pcard : player[playerid].list_hand) if(pcard->data.type & TYPE_MONSTER || pcard->is_affected_by_effect(EFFECT_EXTRA_FUSION_MATERIAL)) material->insert(pcard); for(auto& pcard : player[playerid].list_grave) if(pcard->is_affected_by_effect(EFFECT_EXTRA_FUSION_MATERIAL)) material->insert(pcard); } void field::ritual_release(const card_set& material) { card_set rel, rem, tograve; for(auto& pcard : material) { if(pcard->current.location == LOCATION_GRAVE) rem.insert(pcard); else if((pcard->current.location & (LOCATION_OVERLAY | LOCATION_EXTRA | LOCATION_DECK)) != 0) tograve.insert(pcard); else rel.insert(pcard); } send_to(std::move(tograve), core.reason_effect, REASON_RITUAL + REASON_EFFECT + REASON_MATERIAL, core.reason_player, PLAYER_NONE, LOCATION_GRAVE, 0, POS_FACEUP); release(std::move(rel), core.reason_effect, REASON_RITUAL + REASON_EFFECT + REASON_MATERIAL, core.reason_player); send_to(std::move(rem), core.reason_effect, REASON_RITUAL + REASON_EFFECT + REASON_MATERIAL, core.reason_player, PLAYER_NONE, LOCATION_REMOVED, 0, POS_FACEUP); } void field::get_overlay_group(uint8_t playerid, uint8_t self, uint8_t oppo, card_set* pset, group* pgroup) { if(pgroup) { for(auto& pcard : pgroup->container) { if(pcard && !pcard->get_status(STATUS_SUMMONING | STATUS_SPSUMMON_STEP)) pset->insert(pcard->xyz_materials.begin(), pcard->xyz_materials.end()); } } else { for(int i = 0; i < 2; ++i) { if((i == playerid && self) || (i == (1 - playerid) && oppo)) { for(auto& pcard : player[i].list_mzone) { if(pcard && !pcard->get_status(STATUS_SUMMONING | STATUS_SPSUMMON_STEP)) pset->insert(pcard->xyz_materials.begin(), pcard->xyz_materials.end()); } } } } } int32_t field::get_overlay_count(uint8_t playerid, uint8_t self, uint8_t oppo, group* pgroup) { size_t count = 0; if(pgroup) { for(auto& pcard : pgroup->container) { if(pcard && !pcard->get_status(STATUS_SUMMONING | STATUS_SPSUMMON_STEP)) count += pcard->xyz_materials.size(); } return static_cast(count); } for(int i = 0; i < 2; ++i) { if((i == playerid && self) || (i == (1 - playerid) && oppo)) { for(auto& pcard : player[i].list_mzone) { if(pcard && !pcard->get_status(STATUS_SUMMONING | STATUS_SPSUMMON_STEP)) count += pcard->xyz_materials.size(); } } } return static_cast(count); } // put all cards in the target of peffect into effects.disable_check_set void field::update_disable_check_list(effect* peffect) { card_set cset; filter_affected_cards(peffect, &cset); for (auto& pcard : cset) add_to_disable_check_list(pcard); } void field::add_to_disable_check_list(card* pcard) { effects.disable_check_set.insert(pcard); } void field::adjust_disable_check_list() { for(const auto& pcard : effects.disable_check_set) { if(!pcard->is_status(STATUS_TO_ENABLE | STATUS_TO_DISABLE)) { // prevent loop int32_t pre_disable = pcard->get_status(STATUS_DISABLED | STATUS_FORBIDDEN); pcard->refresh_disable_status(); int32_t new_disable = pcard->get_status(STATUS_DISABLED | STATUS_FORBIDDEN); if(pre_disable != new_disable && pcard->is_status(STATUS_EFFECT_ENABLED)) { pcard->filter_disable_related_cards(); // change effects.disable_check_list if(pre_disable) pcard->set_status(STATUS_TO_ENABLE, TRUE); else pcard->set_status(STATUS_TO_DISABLE, TRUE); } } if(pcard->is_status(STATUS_DISABLED) && pcard->is_status(STATUS_TO_DISABLE) && !pcard->is_status(STATUS_TO_ENABLE)) pcard->reset(RESET_DISABLE, RESET_EVENT); pcard->set_status(STATUS_TO_ENABLE | STATUS_TO_DISABLE, FALSE); } effects.disable_check_set.clear(); } // adjust SetUniqueOnField(), EFFECT_SELF_DESTROY, EFFECT_SELF_TOGRAVE void field::adjust_self_destroy_set() { if(core.selfdes_disabled || !core.unique_destroy_set.empty() || !core.self_destroy_set.empty() || !core.self_tograve_set.empty()) return; int32_t p = infos.turn_player; for(int32_t p1 = 0; p1 < 2; ++p1) { std::vector uniq_set; for(auto& ucard : core.unique_cards[p]) { if(ucard->is_position(POS_FACEUP) && ucard->get_status(STATUS_EFFECT_ENABLED) && !ucard->get_status(STATUS_DISABLED | STATUS_FORBIDDEN)) { card_set cset; ucard->get_unique_target(&cset, p); if(cset.size() == 0) ucard->unique_fieldid = 0; else if(cset.size() == 1) { auto cit = cset.begin(); ucard->unique_fieldid = (*cit)->fieldid; } else uniq_set.push_back(ucard); } } std::sort(uniq_set.begin(), uniq_set.end(), [](card* lhs, card* rhs) { return lhs->fieldid < rhs->fieldid; }); for(auto& pcard : uniq_set) { emplace_process(pcard, p); core.unique_destroy_set.insert(pcard); } p = 1 - p; } card_set cset; for(p = 0; p < 2; ++p) { for(auto& pcard : player[p].list_mzone) { if(pcard && pcard->is_position(POS_FACEUP) && !pcard->is_status(STATUS_BATTLE_DESTROYED)) cset.insert(pcard); } for(auto& pcard : player[p].list_szone) { if(pcard && pcard->is_position(POS_FACEUP)) cset.insert(pcard); } } for(auto& pcard : cset) { effect* peffect = pcard->is_affected_by_effect(EFFECT_SELF_DESTROY); if(peffect) { core.self_destroy_set.insert(pcard); } } if(core.global_flag & GLOBALFLAG_SELF_TOGRAVE) { for(auto& pcard : cset) { effect* peffect = pcard->is_affected_by_effect(EFFECT_SELF_TOGRAVE); if(peffect) { core.self_tograve_set.insert(pcard); } } } if(!core.self_destroy_set.empty()) emplace_process(); if(!core.self_tograve_set.empty()) emplace_process(); } void field::erase_grant_effect(effect* peffect) { auto eit = effects.grant_effect.find(peffect); for(auto& it : eit->second) it.first->remove_effect(it.second); effects.grant_effect.erase(eit); } int32_t field::adjust_grant_effect() { auto CheckValidLabel = [this](int32_t lref) { if(!lref) return false; const auto L = pduel->lua->current_state; bool res = false; lua_rawgeti(L, LUA_REGISTRYINDEX, lref); if(auto obj = lua_touserdata(L, -1)) { res = (*static_cast(obj))->lua_type == LuaParam::EFFECT; } lua_pop(L, 1); return res; }; int32_t adjusted = FALSE; for(auto& eit : effects.grant_effect.sorted) { effect* peffect = eit.first; if(!CheckValidLabel(peffect->label_object)) continue; card_set cset; if(peffect->is_available()) filter_affected_cards(peffect, &cset); card_set add_set; for(auto& pcard : cset) { if(pcard->is_affect_by_effect(peffect) && !eit.second->count(pcard)) add_set.insert(pcard); } card_set remove_set; for(auto& cit : *eit.second) { card* pcard = cit.first; if(!pcard->is_affect_by_effect(peffect) || !cset.count(pcard)) remove_set.insert(pcard); } for(auto& pcard : add_set) { effect* geffect = (effect*)peffect->get_label_object(); effect* ceffect = geffect->clone(); ceffect->owner = pcard; pcard->add_effect(ceffect); eit.second->emplace(pcard, ceffect); } for(auto& pcard : remove_set) { auto it = eit.second->find(pcard); pcard->remove_effect(it->second); eit.second->erase(it); } if(!add_set.empty() || !remove_set.empty()) adjusted = TRUE; } return adjusted; } void field::add_unique_card(card* pcard) { uint8_t con = pcard->current.controler; if(pcard->unique_pos[0]) core.unique_cards[con].insert(pcard); if(pcard->unique_pos[1]) core.unique_cards[1 - con].insert(pcard); pcard->unique_fieldid = 0; } void field::remove_unique_card(card* pcard) { uint8_t con = pcard->current.controler; if(con == PLAYER_NONE) return; if(pcard->unique_pos[0]) core.unique_cards[con].erase(pcard); if(pcard->unique_pos[1]) core.unique_cards[1 - con].erase(pcard); } // return: pcard->unique_effect or 0 effect* field::check_unique_onfield(card* pcard, uint8_t controler, uint8_t location, card* icard) { for(auto& ucard : core.unique_cards[controler]) { if((ucard != pcard) && (ucard != icard) && ucard->is_position(POS_FACEUP) && ucard->get_status(STATUS_EFFECT_ENABLED) && !ucard->get_status(STATUS_DISABLED | STATUS_FORBIDDEN) && ucard->unique_fieldid && ucard->check_unique_code(pcard) && (ucard->unique_location & location)) return ucard->unique_effect; } if(!pcard->unique_code || !(pcard->unique_location & location) || pcard->get_status(STATUS_DISABLED | STATUS_FORBIDDEN)) return nullptr; card_set cset; pcard->get_unique_target(&cset, controler, icard); if(pcard->check_unique_code(pcard)) cset.insert(pcard); if(cset.size() >= 2) return pcard->unique_effect; return nullptr; } int32_t field::check_spsummon_once(card* pcard, uint8_t playerid) { if(pcard->spsummon_code == 0) return TRUE; auto iter = core.spsummon_once_map[playerid].find(pcard->spsummon_code); return (iter == core.spsummon_once_map[playerid].end()) || (iter->second == 0); } // increase the binary custom counter 1~5 void field::check_card_counter(card* pcard, ActivityType counter_type, int32_t playerid) { for(auto& iter : core.get_counter_map(counter_type)) { auto& info = iter.second; auto& player_counter = info.player_amount[playerid]; if(player_counter != 0) continue; pduel->lua->add_param(pcard); if(!pduel->lua->check_condition(info.check_function, 1)) ++player_counter; } } void field::check_card_counter(group* pgroup, ActivityType counter_type, int32_t playerid) { for(auto& iter : core.get_counter_map(counter_type)) { auto& info = iter.second; auto& player_counter = info.player_amount[playerid]; if(player_counter != 0) continue; for(auto& pcard : pgroup->container) { pduel->lua->add_param(pcard); if(!pduel->lua->check_condition(info.check_function, 1)) { ++player_counter; break; } } } } chain::applied_chain_counter_t* field::check_chain_counter(effect* peffect, int32_t playerid, int32_t chainid) { chain::applied_chain_counter_t* ret = nullptr; for(auto& iter : core.chain_counter) { auto& info = iter.second; pduel->lua->add_param(peffect); pduel->lua->add_param(playerid); pduel->lua->add_param(chainid); if(!pduel->lua->check_condition(info.check_function, 3)) { if(ret == nullptr) ret = new chain::applied_chain_counter_t; ++info.player_amount[playerid]; ret->push_back(iter.first); } } return ret; } void field::restore_chain_counter(uint8_t playerid, const chain::applied_chain_counter_t& counters) { for(auto& id : counters) --core.chain_counter[id].player_amount[playerid]; } void field::set_spsummon_counter(uint8_t playerid, bool add, bool chain) { if(is_flag(DUEL_CANNOT_SUMMON_OATH_OLD)) { if(add) { ++core.spsummon_state_count[playerid]; if(chain) ++core.spsummon_state_count_rst[playerid]; } else { if(chain) { core.spsummon_state_count[playerid] -= core.spsummon_state_count_rst[playerid]; core.spsummon_state_count_rst[playerid] = 0; } else --core.spsummon_state_count[playerid]; } } else ++core.spsummon_state_count[playerid]; for(auto& peffect : effects.spsummon_count_eff) { card* pcard = peffect->get_handler(); if(is_flag(DUEL_CANNOT_SUMMON_OATH_OLD)) { if(add) { if(peffect->is_available()) { if(((playerid == pcard->current.controler) && peffect->s_range) || ((playerid != pcard->current.controler) && peffect->o_range)) { ++pcard->spsummon_counter[playerid]; if(chain) ++pcard->spsummon_counter_rst[playerid]; } } } else { pcard->spsummon_counter[playerid] -= pcard->spsummon_counter_rst[playerid]; pcard->spsummon_counter_rst[playerid] = 0; } } else { if(peffect->is_available()) { if(((playerid == pcard->current.controler) && peffect->s_range) || ((playerid != pcard->current.controler) && peffect->o_range)) { ++pcard->spsummon_counter[playerid]; } } } } } int32_t field::check_spsummon_counter(uint8_t playerid, uint8_t ct) { for(auto& peffect : effects.spsummon_count_eff) { card* pcard = peffect->get_handler(); uint16_t val = (uint16_t)peffect->value; if(peffect->is_available()) { if(pcard->spsummon_counter[playerid] + ct > val) return FALSE; } } return TRUE; } int32_t field::check_lp_cost(uint8_t playerid, uint32_t lp) { effect_set eset; int32_t val = lp; filter_player_effect(playerid, EFFECT_LPCOST_CHANGE, &eset); for(const auto& peff : eset) { pduel->lua->add_param(core.reason_effect); pduel->lua->add_param(playerid); pduel->lua->add_param(val); val = peff->get_value(3); } if(val <= 0) return TRUE; tevent e; e.event_cards = nullptr; e.event_player = playerid; e.event_value = lp; e.reason = 0; e.reason_effect = core.reason_effect; e.reason_player = playerid; if(effect_replace_check(EFFECT_LPCOST_REPLACE, e)) return TRUE; //cost[playerid].amount += val; if(val <= player[playerid].lp) return TRUE; return FALSE; } /* void field::save_lp_cost() { for(uint8_t playerid = 0; playerid < 2; ++playerid) { if(cost[playerid].count < 8) cost[playerid].lpstack[cost[playerid].count] = cost[playerid].amount; ++cost[playerid].count; } } void field::restore_lp_cost() { for(uint8_t playerid = 0; playerid < 2; ++playerid) { --cost[playerid].count; if(cost[playerid].count < 8) cost[playerid].amount = cost[playerid].lpstack[cost[playerid].count]; } } */ uint32_t field::get_field_counter(uint8_t playerid, uint8_t self, uint8_t oppo, uint16_t countertype) { uint8_t c = self; uint32_t count = 0; for(int32_t p = 0; p < 2; ++p, playerid = 1 - playerid, c = oppo) { if(c) { for(auto& pcard : player[playerid].list_mzone) { if(pcard) count += pcard->get_counter(countertype); } for(auto& pcard : player[playerid].list_szone) { if(pcard) count += pcard->get_counter(countertype); } } } return count; } int32_t field::effect_replace_check(uint32_t code, const tevent& e) { auto pr = effects.continuous_effect.equal_range(code); for(auto eit = pr.first; eit != pr.second;) { effect* peffect = eit->second; ++eit; if(peffect->is_activateable(peffect->get_handler_player(), e)) return TRUE; } return FALSE; } 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; for(auto& atarget : player[1 - p].list_mzone) { if(atarget) { if(atarget->is_affected_by_effect(EFFECT_ONLY_BE_ATTACKED)) auto_attack.push_back(atarget); if(pcard->is_affected_by_effect(EFFECT_ONLY_ATTACK_MONSTER, atarget)) only_attack.push_back(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; int32_t atype = 0; if(auto_attack.size()) { atype = 1; if(auto_attack.size() != 1) return atype; pv = &auto_attack; } else if(pcard->is_affected_by_effect(EFFECT_ONLY_ATTACK_MONSTER)) { atype = 2; if(only_attack.size() != 1) return atype; pv = &only_attack; } else if(pcard->is_affected_by_effect(EFFECT_MUST_ATTACK_MONSTER)) { atype = 3; if(must_attack.empty()) return atype; pv = &must_attack; } else { atype = 4; for(auto& atarget : player[1 - p].list_mzone) if(atarget != core.attacker) attack_tg.push_back(atarget); if(is_player_affected_by_effect(p, EFFECT_SELF_ATTACK) && (!pcard->is_affected_by_effect(EFFECT_ATTACK_ALL) || !attack_tg.size())) { for(auto& atarget : player[p].list_mzone) if (atarget != core.attacker) attack_tg.push_back(atarget); } pv = &attack_tg; } int32_t extra_count = 0; effect_set eset; pcard->filter_effect(EFFECT_EXTRA_ATTACK, &eset); for(const auto& peff : eset) { int32_t val = peff->get_value(pcard); if(val > extra_count) extra_count = val; } int32_t extra_count_m = 0; eset.clear(); pcard->filter_effect(EFFECT_EXTRA_ATTACK_MONSTER, &eset); for(const auto& peff : eset) { int32_t val = peff->get_value(pcard); if(val > extra_count_m) extra_count_m = val; } if(!chain_attack && pcard->announce_count > extra_count && (!extra_count_m || pcard->announce_count > extra_count_m || pcard->announced_cards.findcard(nullptr))) { effect* peffect; if((peffect = pcard->is_affected_by_effect(EFFECT_ATTACK_ALL)) != nullptr && pcard->attack_all_target) { for(auto& atarget : *pv) { if(!atarget) continue; pduel->lua->add_param(atarget); if(!peffect->check_value_condition(1)) continue; if(pcard->announced_cards.findcard(atarget) >= (uint32_t)peffect->get_value(atarget)) continue; if(atype >= 2 && atarget->is_affected_by_effect(EFFECT_IGNORE_BATTLE_TARGET, pcard)) continue; if(select_target && (atype == 2 || atype == 4)) { if(atarget->is_affected_by_effect(EFFECT_CANNOT_BE_BATTLE_TARGET, pcard)) continue; if(pcard->is_affected_by_effect(EFFECT_CANNOT_SELECT_BATTLE_TARGET, atarget)) continue; } v->push_back(atarget); } } return atype; } //chain attack or announce count check passed uint32_t mcount = 0; for(auto& atarget : *pv) { if(!atarget) continue; if(atype >= 2 && atarget->is_affected_by_effect(EFFECT_IGNORE_BATTLE_TARGET, pcard)) continue; if(atarget->current.controler != p) ++mcount; if(chain_attack && core.chain_attack_target && atarget != core.chain_attack_target) continue; if(select_target && (atype == 2 || atype == 4)) { if(atarget->is_affected_by_effect(EFFECT_CANNOT_BE_BATTLE_TARGET, pcard)) continue; if(pcard->is_affected_by_effect(EFFECT_CANNOT_SELECT_BATTLE_TARGET, atarget)) continue; } v->push_back(atarget); } if(atype <= 3) return atype; if((mcount == 0 || pcard->is_affected_by_effect(EFFECT_DIRECT_ATTACK) || core.attack_player) && !pcard->is_affected_by_effect(EFFECT_CANNOT_DIRECT_ATTACK) && !(!chain_attack && extra_count_m && pcard->announce_count > extra_count) && !(chain_attack && core.chain_attack_target)) pcard->direct_attackable = 1; return atype; } bool field::confirm_attack_target() { card_vector cv; get_attack_target(core.attacker, &cv, core.chain_attack, false); return (core.attack_target && std::find(cv.begin(), cv.end(), core.attack_target) != cv.end()) || (!core.attack_target && core.attacker->direct_attackable); } // update the validity for EFFECT_ATTACK_ALL (approximate solution) void field::attack_all_target_check() { if(!core.attacker) return; if(!core.attack_target) { core.attacker->attack_all_target = FALSE; return; } effect* peffect = core.attacker->is_affected_by_effect(EFFECT_ATTACK_ALL); if(!peffect) return; if(!peffect->get_value(core.attack_target)) core.attacker->attack_all_target = FALSE; } int32_t field::check_tribute(card* pcard, int32_t min, int32_t max, group* mg, uint8_t toplayer, uint32_t zone, uint32_t releasable, uint32_t pos) { int32_t ex = FALSE; uint32_t sumplayer = pcard->current.controler; if(toplayer == 1 - sumplayer) ex = TRUE; card_set release_list, ex_list; int32_t m = get_summon_release_list(pcard, &release_list, &ex_list, nullptr, mg, ex, releasable, pos); if(max > m) max = m; if(min > max) return FALSE; zone &= (0x1f & get_forced_zones(pcard, toplayer, LOCATION_MZONE, sumplayer, LOCATION_REASON_TOFIELD)); int32_t s = 0; int32_t ct = get_tofield_count(pcard, toplayer, LOCATION_MZONE, sumplayer, LOCATION_REASON_TOFIELD, zone); if(ct <= 0 && max <= 0) return FALSE; const auto& to_check_release_list = [&] { if(ex_list.size() > 0 && static_cast(ex_list.size()) >= min) return ex_list; return release_list; }(); for(auto& _pcard : to_check_release_list) { if(_pcard->current.location == LOCATION_MZONE && _pcard->current.controler == toplayer) { ++s; if((zone >> _pcard->current.sequence) & 1) ++ct; } } if(ct <= 0) return FALSE; max -= (int32_t)ex_list.size(); int32_t fcount = get_mzone_limit(toplayer, sumplayer, LOCATION_REASON_TOFIELD); if(s < -fcount + 1) return FALSE; if(max < 0) max = 0; if(max < -fcount + 1) return FALSE; return TRUE; } int32_t field::check_with_sum_limit(const card_vector& mats, int32_t acc, int32_t index, int32_t count, int32_t min, int32_t max, int32_t* should_continue) { if(count > max) return FALSE; while(index < (int32_t)mats.size()) { int32_t op1 = mats[index]->sum_param & 0xffff; int32_t op2 = (mats[index]->sum_param >> 16) & 0xffff; if((op1 == acc || op2 == acc) && count >= min) return TRUE; ++index; if(acc > op1 && check_with_sum_limit(mats, acc - op1, index, count + 1, min, max, should_continue)) return TRUE; if(op2 && acc > op2 && check_with_sum_limit(mats, acc - op2, index, count + 1, min, max, should_continue)) return TRUE; } if(count + 1 < min && should_continue) *should_continue = FALSE; return FALSE; } int32_t field::check_with_sum_limit_m(const card_vector& mats, int32_t acc, int32_t index, int32_t min, int32_t max, int32_t must_count, int32_t* should_continue) { if(acc == 0) return index == must_count && 0 >= min && 0 <= max; if(index == must_count) return check_with_sum_limit(mats, acc, index, 1, min, max, should_continue); if(index >= (int32_t)mats.size()) return FALSE; int32_t op1 = mats[index]->sum_param & 0xffff; int32_t op2 = (mats[index]->sum_param >> 16) & 0xffff; if(acc >= op1 && check_with_sum_limit_m(mats, acc - op1, index + 1, min, max, must_count, should_continue)) return TRUE; if(op2 && acc >= op2 && check_with_sum_limit_m(mats, acc - op2, index + 1, min, max, must_count, should_continue)) return TRUE; return FALSE; } int32_t field::check_with_sum_greater_limit(const card_vector& mats, int32_t acc, int32_t index, int32_t opmin, int32_t* should_continue) { while(index < (int32_t)mats.size()) { int32_t op1 = mats[index]->sum_param & 0xffff; int32_t op2 = (mats[index]->sum_param >> 16) & 0xffff; if((acc <= op1 && acc + opmin > op1) || (op2 && acc <= op2 && acc + opmin > op2)) return TRUE; ++index; if(check_with_sum_greater_limit(mats, acc - op1, index, std::min(opmin, op1), should_continue)) return TRUE; if(op2 && check_with_sum_greater_limit(mats, acc - op2, index, std::min(opmin, op2), should_continue)) return TRUE; } return FALSE; } int32_t field::check_with_sum_greater_limit_m(const card_vector& mats, int32_t acc, int32_t index, int32_t opmin, int32_t must_count, int32_t* should_continue) { if(acc <= 0) return index == must_count && acc + opmin > 0; if(index == must_count) return check_with_sum_greater_limit(mats, acc, index, opmin, should_continue); if(index >= (int32_t)mats.size()) return FALSE; int32_t op1 = mats[index]->sum_param & 0xffff; int32_t op2 = (mats[index]->sum_param >> 16) & 0xffff; if(check_with_sum_greater_limit_m(mats, acc - op1, index + 1, std::min(opmin, op1), must_count, should_continue)) return TRUE; if(op2 && check_with_sum_greater_limit_m(mats, acc - op2, index + 1, std::min(opmin, op2), must_count, should_continue)) return TRUE; if(should_continue && index < must_count && (acc - op1 <= 0 && (!op2 || acc - op2 <= 0))) *should_continue = FALSE; return FALSE; } int32_t field::is_player_can_draw(uint8_t playerid) { return !is_player_affected_by_effect(playerid, EFFECT_CANNOT_DRAW); } int32_t field::is_player_can_discard_deck(uint8_t playerid, uint32_t count) { if(player[playerid].list_main.size() < count) return FALSE; return !is_player_affected_by_effect(playerid, EFFECT_CANNOT_DISCARD_DECK); } int32_t field::is_player_can_discard_deck_as_cost(uint8_t playerid, uint32_t count) { if(player[playerid].list_main.size() < count) return FALSE; if(is_player_affected_by_effect(playerid, EFFECT_CANNOT_DISCARD_DECK)) return FALSE; if(player[playerid].list_main.empty()) return TRUE; card* topcard = player[playerid].list_main.back(); if((count == 1) && topcard->is_position(POS_FACEUP)) return topcard->is_capable_cost_to_grave(playerid); bool cant_remove_s = !is_player_can_action(playerid, EFFECT_CANNOT_REMOVE); bool cant_remove_o = !is_player_can_action(1 - playerid, EFFECT_CANNOT_REMOVE); effect_set eset; filter_field_effect(EFFECT_TO_GRAVE_REDIRECT, &eset); for(const auto& peff : eset) { uint32_t redirect = peff->get_value(); uint8_t p = peff->get_handler_player(); if(redirect & LOCATION_REMOVED) { if(topcard->is_affected_by_effect(EFFECT_CANNOT_REMOVE)) continue; if((cant_remove_s && (p == playerid)) || (cant_remove_o && (p != playerid))) continue; } if((p == playerid && peff->s_range & LOCATION_DECK) || (p != playerid && peff->o_range & LOCATION_DECK)) return FALSE; } return TRUE; } int32_t field::is_player_can_discard_hand(uint8_t playerid, card* pcard, effect* peffect, uint32_t reason) { if(pcard->current.location != LOCATION_HAND) return FALSE; effect_set eset; filter_player_effect(playerid, EFFECT_CANNOT_DISCARD_HAND, &eset); for(const auto& peff : eset) { if(!peff->target) return FALSE; pduel->lua->add_param(peff); pduel->lua->add_param(pcard); pduel->lua->add_param(peffect); pduel->lua->add_param(reason); if (pduel->lua->check_condition(peff->target, 4)) return FALSE; } return TRUE; } int32_t field::is_player_can_action(uint8_t playerid, uint32_t actionlimit) { effect_set eset; filter_player_effect(playerid, actionlimit, &eset); for(const auto& peff : eset) { if(!peff->target) return FALSE; } return TRUE; } int32_t field::is_player_can_summon(uint32_t sumtype, uint8_t playerid, card* pcard, uint8_t toplayer) { effect_set eset; sumtype |= SUMMON_TYPE_NORMAL; filter_player_effect(playerid, EFFECT_CANNOT_SUMMON, &eset); for(const auto& peff : eset) { if(!peff->target) return FALSE; pduel->lua->add_param(peff); pduel->lua->add_param(pcard); pduel->lua->add_param(playerid); pduel->lua->add_param(sumtype); pduel->lua->add_param(POS_FACEUP); pduel->lua->add_param(toplayer); if(pduel->lua->check_condition(peff->target, 6)) return FALSE; } eset.clear(); filter_player_effect(playerid, EFFECT_FORCE_NORMAL_SUMMON_POSITION, &eset); uint8_t sumpos = POS_FACEUP_ATTACK; if(is_flag(DUEL_NORMAL_SUMMON_FACEUP_DEF) || is_player_affected_by_effect(playerid, EFFECT_DEVINE_LIGHT)) sumpos = POS_FACEUP; for(auto& eff : eset) { if(eff->target) { pduel->lua->add_param(eff); pduel->lua->add_param(pcard); pduel->lua->add_param(playerid); pduel->lua->add_param(sumtype); pduel->lua->add_param(sumpos); pduel->lua->add_param(toplayer); if(!pduel->lua->check_condition(eff->target, 6)) continue; } sumpos &= eff->get_value(); if(sumpos == 0) return FALSE; } return TRUE; } int32_t field::is_player_can_mset(uint32_t sumtype, uint8_t playerid, card* pcard, uint8_t toplayer) { effect_set eset; sumtype |= SUMMON_TYPE_NORMAL; filter_player_effect(playerid, EFFECT_CANNOT_MSET, &eset); for(const auto& peff : eset) { if(!peff->target) return FALSE; pduel->lua->add_param(peff); pduel->lua->add_param(pcard); pduel->lua->add_param(playerid); pduel->lua->add_param(sumtype); pduel->lua->add_param(toplayer); pduel->lua->add_param(POS_FACEDOWN); if (pduel->lua->check_condition(peff->target, 6)) return FALSE; } return TRUE; } int32_t field::is_player_can_sset(uint8_t playerid, card* pcard) { effect_set eset; filter_player_effect(playerid, EFFECT_CANNOT_SSET, &eset); for(const auto& peff : eset) { if(!peff->target) return FALSE; pduel->lua->add_param(peff); pduel->lua->add_param(pcard); pduel->lua->add_param(playerid); if (pduel->lua->check_condition(peff->target, 3)) return FALSE; } return TRUE; } // check player-effect EFFECT_CANNOT_SPECIAL_SUMMON without target int32_t field::is_player_can_spsummon(uint8_t playerid) { effect_set eset; filter_player_effect(playerid, EFFECT_CANNOT_SPECIAL_SUMMON, &eset); for(auto& eff : eset) { if(!eff->target) return FALSE; } return is_player_can_spsummon_count(playerid, 1); } int32_t field::is_player_can_spsummon(effect* peffect, uint32_t sumtype, uint8_t sumpos, uint8_t playerid, uint8_t toplayer, card* pcard, effect* proc_effect) { if(pcard->is_affected_by_effect(EFFECT_CANNOT_SPECIAL_SUMMON)) return FALSE; if(pcard->is_status(STATUS_FORBIDDEN)) return FALSE; if((pcard->data.type & TYPE_TOKEN) && (pcard->current.location & LOCATION_ONFIELD)) return FALSE; if((pcard->data.type & TYPE_LINK) && (pcard->data.type & TYPE_MONSTER)) sumpos &= POS_FACEUP_ATTACK; if(sumpos == 0) return FALSE; sumtype |= SUMMON_TYPE_SPECIAL; save_lp_cost(); if(!pcard->check_cost_condition(EFFECT_SPSUMMON_COST, playerid, sumtype)) { restore_lp_cost(); return FALSE; } restore_lp_cost(); if(sumpos & POS_FACEDOWN && is_player_affected_by_effect(playerid, EFFECT_DEVINE_LIGHT)) sumpos = (sumpos & POS_FACEUP) | ((sumpos & POS_FACEDOWN) >> 1); effect_set eset; filter_player_effect(playerid, EFFECT_CANNOT_SPECIAL_SUMMON, &eset); for(auto& eff : eset) { if(!eff->target) return FALSE; pduel->lua->add_param(eff); pduel->lua->add_param(pcard); pduel->lua->add_param(playerid); pduel->lua->add_param(sumtype); pduel->lua->add_param(sumpos); pduel->lua->add_param(toplayer); pduel->lua->add_param(peffect); pduel->lua->add_param(proc_effect); if(pduel->lua->check_condition(eff->target, 8)) return FALSE; } eset.clear(); filter_player_effect(playerid, EFFECT_FORCE_SPSUMMON_POSITION, &eset); for(auto& eff : eset) { if(eff->target) { pduel->lua->add_param(eff); pduel->lua->add_param(pcard); pduel->lua->add_param(playerid); pduel->lua->add_param(sumtype); pduel->lua->add_param(sumpos); pduel->lua->add_param(toplayer); pduel->lua->add_param(peffect); if(!pduel->lua->check_condition(eff->target, 7)) continue; } sumpos &= eff->get_value(); if(sumpos == 0) return FALSE; } if(!check_spsummon_once(pcard, playerid)) return FALSE; if(!check_spsummon_counter(playerid)) return FALSE; return TRUE; } int32_t field::is_player_can_flipsummon(uint8_t playerid, card* pcard) { effect_set eset; filter_player_effect(playerid, EFFECT_CANNOT_FLIP_SUMMON, &eset); for(const auto& peff : eset) { if(!peff->target) return FALSE; pduel->lua->add_param(peff); pduel->lua->add_param(pcard); pduel->lua->add_param(playerid); if (pduel->lua->check_condition(peff->target, 3)) return FALSE; } return TRUE; } int32_t field::is_player_can_spsummon_monster(uint8_t playerid, uint8_t toplayer, uint8_t sumpos, uint32_t sumtype, card_data* pdata) { temp_card->data = *pdata; int32_t result = is_player_can_spsummon(core.reason_effect, sumtype, sumpos, playerid, toplayer, temp_card); temp_card->data = {}; return result; } int32_t field::is_player_can_release(uint8_t playerid, card* pcard, uint32_t reason) { effect_set eset; filter_player_effect(playerid, EFFECT_CANNOT_RELEASE, &eset); for(const auto& peff : eset) { if(!peff->target) return FALSE; pduel->lua->add_param(peff); pduel->lua->add_param(pcard); pduel->lua->add_param(playerid); pduel->lua->add_param(reason); pduel->lua->add_param(core.reason_effect); if (pduel->lua->check_condition(peff->target, 5)) return FALSE; } return TRUE; } int32_t field::is_player_can_spsummon_count(uint8_t playerid, uint32_t count) { effect_set eset; filter_player_effect(playerid, EFFECT_LEFT_SPSUMMON_COUNT, &eset); for(const auto& peff : eset) { pduel->lua->add_param(core.reason_effect); pduel->lua->add_param(playerid); int32_t v = peff->get_value(2); if(v < (int32_t)count) return FALSE; } return check_spsummon_counter(playerid, count); } int32_t field::is_player_can_place_counter(uint8_t playerid, card* pcard, uint16_t countertype, uint16_t count) { effect_set eset; filter_player_effect(playerid, EFFECT_CANNOT_PLACE_COUNTER, &eset); for(const auto& peff : eset) { if(!peff->target) return FALSE; pduel->lua->add_param(peff); pduel->lua->add_param(pcard); pduel->lua->add_param(playerid); pduel->lua->add_param(countertype); pduel->lua->add_param(count); if (pduel->lua->check_condition(peff->target, 5)) return FALSE; } return TRUE; } int32_t field::is_player_can_remove_counter(uint8_t playerid, card* pcard, uint8_t self, uint8_t oppo, uint16_t countertype, uint16_t count, uint32_t reason) { if((pcard && pcard->get_counter(countertype) >= count) || (!pcard && get_field_counter(playerid, self, oppo, countertype) >= count)) return TRUE; auto pr = effects.continuous_effect.equal_range(EFFECT_RCOUNTER_REPLACE + countertype); tevent e; e.event_cards = nullptr; e.event_player = playerid; e.event_value = count; e.reason = reason; e.reason_effect = core.reason_effect; e.reason_player = playerid; for(auto eit = pr.first; eit != pr.second;) { effect* peffect = eit->second; ++eit; if(peffect->is_activateable(peffect->get_handler_player(), e)) return TRUE; } return FALSE; } int32_t field::is_player_can_remove_overlay_card(uint8_t playerid, group* pgroup, uint8_t self, uint8_t oppo, uint16_t min, uint32_t reason) { if(get_overlay_count(playerid, self, oppo, pgroup) >= min) return TRUE; auto pr = effects.continuous_effect.equal_range(EFFECT_OVERLAY_REMOVE_REPLACE); tevent e; e.event_cards = nullptr; e.event_player = playerid; e.event_value = min; e.reason = reason; e.reason_effect = core.reason_effect; e.reason_player = playerid; for(auto eit = pr.first; eit != pr.second;) { effect* peffect = eit->second; ++eit; if(peffect->is_activateable(peffect->get_handler_player(), e)) return TRUE; } return FALSE; } int32_t field::is_player_can_send_to_grave(uint8_t playerid, card* pcard) { effect_set eset; filter_player_effect(playerid, EFFECT_CANNOT_TO_GRAVE, &eset); for(const auto& peff : eset) { if(!peff->target) return FALSE; pduel->lua->add_param(peff); pduel->lua->add_param(pcard); pduel->lua->add_param(playerid); if (pduel->lua->check_condition(peff->target, 3)) return FALSE; } return TRUE; } int32_t field::is_player_can_send_to_hand(uint8_t playerid, card* pcard) { effect_set eset; filter_player_effect(playerid, EFFECT_CANNOT_TO_HAND, &eset); for(const auto& peff : eset) { if(!peff->target) return FALSE; pduel->lua->add_param(peff); pduel->lua->add_param(pcard); pduel->lua->add_param(playerid); pduel->lua->add_param(core.reason_effect); if(pduel->lua->check_condition(peff->target, 4)) return FALSE; } if(pcard->is_extra_deck_monster() && !is_player_can_send_to_deck(playerid, pcard)) return FALSE; return TRUE; } int32_t field::is_player_can_send_to_deck(uint8_t playerid, card* pcard) { effect_set eset; filter_player_effect(playerid, EFFECT_CANNOT_TO_DECK, &eset); for(const auto& peff : eset) { if(!peff->target) return FALSE; pduel->lua->add_param(peff); pduel->lua->add_param(pcard); pduel->lua->add_param(playerid); if(pduel->lua->check_condition(peff->target, 3)) return FALSE; } return TRUE; } int32_t field::is_player_can_remove(uint8_t playerid, card* pcard, uint32_t reason) { effect_set eset; filter_player_effect(playerid, EFFECT_CANNOT_REMOVE, &eset); for(const auto& peff : eset) { if(!peff->target) return FALSE; pduel->lua->add_param(peff); pduel->lua->add_param(pcard); pduel->lua->add_param(playerid); pduel->lua->add_param(reason); pduel->lua->add_param(core.reason_effect); if(pduel->lua->check_condition(peff->target, 5)) return FALSE; } return TRUE; } int32_t field::is_chain_negatable(uint8_t chaincount) { effect_set eset; if(chaincount > core.current_chain.size()) return FALSE; effect* peffect; if(chaincount == 0) peffect = core.current_chain.back().triggering_effect; else peffect = core.current_chain[chaincount - 1].triggering_effect; if(peffect->is_flag(EFFECT_FLAG_CANNOT_INACTIVATE)) return FALSE; filter_field_effect(EFFECT_CANNOT_INACTIVATE, &eset); for(const auto& peff : eset) { pduel->lua->add_param(chaincount); if(peff->check_value_condition(1)) return FALSE; } return TRUE; } int32_t field::is_chain_disablable(uint8_t chaincount) { effect_set eset; if(chaincount > core.current_chain.size()) return FALSE; effect* peffect; if(chaincount == 0) peffect = core.current_chain.back().triggering_effect; else peffect = core.current_chain[chaincount - 1].triggering_effect; if(!peffect->get_handler()->get_status(STATUS_FORBIDDEN)) { if(peffect->is_flag(EFFECT_FLAG_CANNOT_DISABLE)) return FALSE; filter_field_effect(EFFECT_CANNOT_DISEFFECT, &eset); for(const auto& peff : eset) { pduel->lua->add_param(chaincount); if(peff->check_value_condition(1)) return FALSE; } } return TRUE; } int32_t field::is_chain_disabled(uint8_t chaincount) { if(chaincount > core.current_chain.size()) return FALSE; chain* pchain; if(chaincount == 0) pchain = &core.current_chain.back(); else pchain = &core.current_chain[chaincount - 1]; if(pchain->flag & CHAIN_DISABLE_EFFECT) return TRUE; card* pcard = pchain->triggering_effect->get_handler(); auto rg = pcard->single_effect.equal_range(EFFECT_DISABLE_CHAIN); for(; rg.first != rg.second; ++rg.first) { effect* peffect = rg.first->second; if(peffect->get_value() == pchain->chain_id) { peffect->reset_flag |= RESET_CHAIN; return TRUE; } } return FALSE; } int32_t field::check_chain_target(uint8_t chaincount, card* pcard) { if(chaincount > core.current_chain.size()) return FALSE; chain* pchain; if(chaincount == 0) pchain = &core.current_chain.back(); else pchain = &core.current_chain[chaincount - 1]; effect* peffect = pchain->triggering_effect; uint8_t tp = pchain->triggering_player; if(!peffect->is_flag(EFFECT_FLAG_CARD_TARGET) || !peffect->target) return FALSE; pduel->lua->add_param(peffect); pduel->lua->add_param(tp); pduel->lua->add_param(pchain->evt.event_cards ); pduel->lua->add_param(pchain->evt.event_player); pduel->lua->add_param(pchain->evt.event_value); pduel->lua->add_param(pchain->evt.reason_effect ); pduel->lua->add_param(pchain->evt.reason); pduel->lua->add_param(pchain->evt.reason_player); pduel->lua->add_param(0); pduel->lua->add_param(pcard); return pduel->lua->check_condition(peffect->target, 10); } chain* field::get_chain(uint8_t chaincount) { if(chaincount == 0 && core.continuous_chain.size() && (core.reason_effect != nullptr && core.reason_effect->type & EFFECT_TYPE_CONTINUOUS)) return &core.continuous_chain.back(); if(chaincount == 0 || chaincount > core.current_chain.size()) { chaincount = (uint8_t)core.current_chain.size(); if(chaincount == 0) return nullptr; } return &core.current_chain[chaincount - 1]; } int32_t field::get_cteffect(effect* peffect, int32_t playerid, int32_t store) { card* phandler = peffect->get_handler(); if(phandler->data.type != (TYPE_TRAP | TYPE_CONTINUOUS)) return FALSE; if(!(peffect->type & EFFECT_TYPE_ACTIVATE)) return FALSE; if(peffect->code != EVENT_FREE_CHAIN) return FALSE; if(peffect->cost || peffect->target || peffect->operation) return FALSE; if(store) { core.select_chains.clear(); core.select_options.clear(); } for(auto& efit : phandler->field_effect) { effect* feffect = efit.second; if(!(feffect->type & (EFFECT_TYPE_TRIGGER_F | EFFECT_TYPE_TRIGGER_O | EFFECT_TYPE_QUICK_O))) continue; if(!feffect->in_range(phandler)) continue; uint32_t code = efit.first; if(code == EVENT_FREE_CHAIN || code == EVENT_PHASE + (uint32_t)infos.phase) { nil_event.event_code = code; if(get_cteffect_evt(feffect, playerid, nil_event, store) && !store) return TRUE; } else { for(const auto& ev : core.point_event) { if(code != ev.event_code) continue; if(get_cteffect_evt(feffect, playerid, ev, store) && !store) return TRUE; } for(const auto& ev : core.instant_event) { if(code != ev.event_code) continue; if(get_cteffect_evt(feffect, playerid, ev, store) && !store) return TRUE; } } } return (store && !core.select_chains.empty()) ? TRUE : FALSE; } int32_t field::get_cteffect_evt(effect* feffect, int32_t playerid, const tevent& e, int32_t store) { if(!feffect->is_activateable(playerid, e, FALSE, FALSE, FALSE, FALSE, TRUE)) return FALSE; if(store) { auto& newchain = core.select_chains.emplace_back(); newchain.evt = e; newchain.triggering_effect = feffect; core.select_options.push_back(feffect->description); } return TRUE; } int32_t field::check_cteffect_hint(effect* peffect, uint8_t playerid) { card* phandler = peffect->get_handler(); if(phandler->data.type != (TYPE_TRAP | TYPE_CONTINUOUS)) return FALSE; if(!(peffect->type & EFFECT_TYPE_ACTIVATE)) return FALSE; if(peffect->code != EVENT_FREE_CHAIN) return FALSE; if(peffect->cost || peffect->target || peffect->operation) return FALSE; for(auto& efit : phandler->field_effect) { effect* feffect = efit.second; if(!(feffect->type & (EFFECT_TYPE_TRIGGER_F | EFFECT_TYPE_TRIGGER_O | EFFECT_TYPE_QUICK_O))) continue; if(!feffect->in_range(phandler)) continue; uint32_t code = efit.first; if(code == EVENT_FREE_CHAIN || code == (uint32_t)(EVENT_PHASE | infos.phase)) { nil_event.event_code = code; if(get_cteffect_evt(feffect, playerid, nil_event, FALSE) && (code != EVENT_FREE_CHAIN || check_hint_timing(feffect))) return TRUE; } else { for(const auto& ev : core.point_event) { if(code != ev.event_code) continue; if(get_cteffect_evt(feffect, playerid, ev, FALSE)) return TRUE; } for(const auto& ev : core.instant_event) { if(code != ev.event_code) continue; if(get_cteffect_evt(feffect, playerid, ev, FALSE)) return TRUE; } } } return FALSE; } int32_t field::check_nonpublic_trigger(chain& ch) { effect* peffect = ch.triggering_effect; card* phandler = peffect->get_handler(); if(!peffect->is_flag(EFFECT_FLAG_FIELD_ONLY) && (((peffect->type & EFFECT_TYPE_SINGLE) && !peffect->is_flag(EFFECT_FLAG_SINGLE_RANGE) && phandler->is_has_relation(ch) && (ch.triggering_location & (LOCATION_HAND | LOCATION_DECK))) || (peffect->range & (LOCATION_HAND | LOCATION_DECK)))) { ch.flag |= CHAIN_HAND_TRIGGER; core.new_ochain_h.push_back(ch); //TCG segoc if(!is_flag(DUEL_TCG_SEGOC_NONPUBLIC) && ((ch.triggering_location == LOCATION_HAND && phandler->is_position(POS_FACEDOWN)) || ch.triggering_location == LOCATION_DECK || (peffect->range && !peffect->in_range(ch)))) return FALSE; } return TRUE; } int32_t field::check_trigger_effect(const chain& ch) const { effect* peffect = ch.triggering_effect; card* phandler = peffect->get_handler(); if(pduel->game_field->is_flag(DUEL_TRIGGER_ONLY_IN_LOCATION)) return phandler->is_has_relation(ch); if((peffect->type & EFFECT_TYPE_FIELD) && !phandler->is_has_relation(ch)) return FALSE; if(peffect->code == EVENT_FLIP && infos.phase == PHASE_DAMAGE) return TRUE; if(pduel->game_field->is_flag(DUEL_TRIGGER_WHEN_PRIVATE_KNOWLEDGE)) return TRUE; if((phandler->current.location & LOCATION_DECK) && !(ch.triggering_location == LOCATION_DECK)) return FALSE; if((ch.triggering_location & (LOCATION_DECK | LOCATION_HAND | LOCATION_EXTRA)) && (ch.triggering_position & POS_FACEDOWN)) return TRUE; if(!(phandler->current.location & (LOCATION_DECK | LOCATION_HAND | LOCATION_EXTRA)) || phandler->is_position(POS_FACEUP)) return TRUE; return FALSE; } int32_t field::check_spself_from_hand_trigger(const chain& ch) const { effect* peffect = ch.triggering_effect; uint8_t tp = ch.triggering_player; if((peffect->status & EFFECT_STATUS_SUMMON_SELF) && (ch.flag & CHAIN_HAND_TRIGGER)) { return std::none_of(core.current_chain.begin(), core.current_chain.end(), [tp](const chain& ch) { return ch.triggering_player == tp && (ch.triggering_effect->status & EFFECT_STATUS_SUMMON_SELF) && (ch.flag & CHAIN_HAND_TRIGGER); }); } return TRUE; } int32_t field::is_able_to_enter_bp() { return (is_flag(DUEL_ATTACK_FIRST_TURN) || infos.turn_id != 1 || is_player_affected_by_effect(infos.turn_player, EFFECT_BP_FIRST_TURN)) && infos.phase < PHASE_BATTLE_START && !is_player_affected_by_effect(infos.turn_player, EFFECT_CANNOT_BP); }