dep-ygopro-core/card.cpp
2025-12-05 21:14:15 +01:00

4003 lines
139 KiB
C++

/*
* Copyright (c) 2010-2015, Argon Sun (Fluorohydride)
* Copyright (c) 2016-2025, Edoardo Lolletti (edo9300) <edoardo762@gmail.com>
*
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
#include <algorithm> //std::sort, std::min, std::transform
#include <cstring> //std::memcpy
#include <iterator> //std::back_inserter
#include <set>
#include <vector>
#include <utility> //std::pair, std::make_pair, std::swap
#include "card.h"
#include "duel.h"
#include "effect.h"
#include "field.h"
#include "interpreter.h"
bool card_sort::operator()(const card* c1, const card* c2) const {
return c1->cardid < c2->cardid;
}
template<typename T>
static constexpr void set_max_property_val(T& val) {
val = static_cast<T>(~T());
}
template<typename T>
static constexpr bool has_valid_property_val(T val) {
return val != ~T();
}
void card_state::set0xff() {
set_max_property_val(code);
set_max_property_val(code2);
set_max_property_val(type);
set_max_property_val(level);
set_max_property_val(rank);
set_max_property_val(link);
set_max_property_val(link_marker);
set_max_property_val(lscale);
set_max_property_val(rscale);
set_max_property_val(attribute);
set_max_property_val(race);
set_max_property_val(attack);
set_max_property_val(defense);
set_max_property_val(base_attack);
set_max_property_val(base_defense);
set_max_property_val(controler);
set_max_property_val(location);
set_max_property_val(sequence);
set_max_property_val(position);
set_max_property_val(reason);
set_max_property_val(reason_player);
}
bool card::card_operation_sort(card* c1, card* c2) {
duel* pduel = c1->pduel;
int32_t cp1 = c1->overlay_target ? c1->overlay_target->current.controler : c1->current.controler;
int32_t cp2 = c2->overlay_target ? c2->overlay_target->current.controler : c2->current.controler;
if(cp1 != cp2) {
if(cp1 == PLAYER_NONE || cp2 == PLAYER_NONE)
return cp1 < cp2;
if(pduel->game_field->infos.turn_player == 0)
return cp1 < cp2;
else
return cp1 > cp2;
}
if(c1->current.location != c2->current.location)
return c1->current.location < c2->current.location;
if(c1->current.location & LOCATION_OVERLAY) {
if(c1->overlay_target->current.sequence != c2->overlay_target->current.sequence)
return c1->overlay_target->current.sequence < c2->overlay_target->current.sequence;
else return c1->current.sequence < c2->current.sequence;
} else {
if(c1->current.location & (LOCATION_DECK | LOCATION_EXTRA | LOCATION_GRAVE | LOCATION_REMOVED))
return c1->current.sequence > c2->current.sequence;
else
return c1->current.sequence < c2->current.sequence;
}
}
void card::attacker_map::addcard(card* pcard) {
uint32_t fid = pcard ? pcard->fieldid_r : 0;
auto pr = emplace(fid, std::make_pair(pcard, 0));
++pr.first->second.second;
}
uint32_t card::attacker_map::findcard(card* pcard) {
uint32_t fid = pcard ? pcard->fieldid_r : 0;
auto it = find(fid);
if(it == end())
return 0;
else
return it->second.second;
}
card::card(duel* pd) : lua_obj_helper(pd) {
temp.set0xff();
current.controler = PLAYER_NONE;
}
bool card::is_extra_deck_monster() const {
if(!(data.type & TYPE_MONSTER))
return false;
if(!(data.type & pduel->game_field->get_extra_deck_types()))
return false;
return true;
}
template<typename T>
void insert_value(std::vector<uint8_t>& vec, const T& _val) {
T val = _val;
const auto vec_size = vec.size();
const auto val_size = sizeof(T);
vec.resize(vec_size + val_size);
std::memcpy(&vec[vec_size], &val, val_size);
}
#define CHECK_AND_INSERT_T(query, value, type) do { \
if(query_flag & query) {\
insert_value<uint16_t>(pduel->query_buffer, sizeof(uint32_t) + sizeof(type)); \
insert_value<uint32_t>(pduel->query_buffer, query); \
insert_value<type>(pduel->query_buffer, value); \
} \
} while(0)
#define CHECK_AND_INSERT(query, value)CHECK_AND_INSERT_T(query, value, uint32_t)
void card::get_infos(uint32_t query_flag) {
CHECK_AND_INSERT(QUERY_CODE, data.code);
CHECK_AND_INSERT(QUERY_POSITION, get_info_location().position);
CHECK_AND_INSERT(QUERY_ALIAS, get_code());
CHECK_AND_INSERT(QUERY_TYPE, get_type());
CHECK_AND_INSERT(QUERY_LEVEL, get_level());
CHECK_AND_INSERT(QUERY_RANK, get_rank());
CHECK_AND_INSERT(QUERY_ATTRIBUTE, get_attribute());
CHECK_AND_INSERT_T(QUERY_RACE, get_race(), uint64_t);
CHECK_AND_INSERT(QUERY_ATTACK, get_attack());
CHECK_AND_INSERT(QUERY_DEFENSE, get_defense());
CHECK_AND_INSERT(QUERY_BASE_ATTACK, get_base_attack());
CHECK_AND_INSERT(QUERY_BASE_DEFENSE, get_base_defense());
CHECK_AND_INSERT(QUERY_REASON, current.reason);
CHECK_AND_INSERT(QUERY_COVER, cover);
if(query_flag & QUERY_REASON_CARD) {
insert_value<uint16_t>(pduel->query_buffer, sizeof(uint32_t) + sizeof(uint16_t) + sizeof(uint64_t));
insert_value<uint32_t>(pduel->query_buffer, QUERY_REASON_CARD);
if(current.reason_card) {
loc_info info = current.reason_card->get_info_location();
insert_value<uint8_t>(pduel->query_buffer, info.controler);
insert_value<uint8_t>(pduel->query_buffer, info.location);
insert_value<uint32_t>(pduel->query_buffer, info.sequence);
insert_value<uint32_t>(pduel->query_buffer, info.position);
} else {
insert_value<uint16_t>(pduel->query_buffer, 0);
insert_value<uint64_t>(pduel->query_buffer, 0);
}
}
if(query_flag & QUERY_EQUIP_CARD) {
insert_value<uint16_t>(pduel->query_buffer, sizeof(uint32_t) + sizeof(uint16_t) + sizeof(uint64_t));
insert_value<uint32_t>(pduel->query_buffer, QUERY_EQUIP_CARD);
if(equiping_target) {
loc_info info = equiping_target->get_info_location();
insert_value<uint8_t>(pduel->query_buffer, info.controler);
insert_value<uint8_t>(pduel->query_buffer, info.location);
insert_value<uint32_t>(pduel->query_buffer, info.sequence);
insert_value<uint32_t>(pduel->query_buffer, info.position);
} else {
insert_value<uint16_t>(pduel->query_buffer, 0);
insert_value<uint64_t>(pduel->query_buffer, 0);
}
}
if(query_flag & QUERY_TARGET_CARD) {
insert_value<uint16_t>(pduel->query_buffer, sizeof(uint32_t) + sizeof(uint32_t) + static_cast<uint16_t>(effect_target_cards.size()) * (sizeof(uint16_t) + sizeof(uint64_t)));
insert_value<uint32_t>(pduel->query_buffer, QUERY_TARGET_CARD);
insert_value<uint32_t>(pduel->query_buffer, static_cast<uint32_t>(effect_target_cards.size()));
for(auto& pcard : effect_target_cards) {
loc_info info = pcard->get_info_location();
insert_value<uint8_t>(pduel->query_buffer, info.controler);
insert_value<uint8_t>(pduel->query_buffer, info.location);
insert_value<uint32_t>(pduel->query_buffer, info.sequence);
insert_value<uint32_t>(pduel->query_buffer, info.position);
}
}
if(query_flag & QUERY_OVERLAY_CARD) {
insert_value<uint16_t>(pduel->query_buffer, sizeof(uint32_t) + sizeof(uint32_t) + static_cast<uint16_t>(xyz_materials.size()) * sizeof(uint32_t));
insert_value<uint32_t>(pduel->query_buffer, QUERY_OVERLAY_CARD);
insert_value<uint32_t>(pduel->query_buffer, static_cast<uint32_t>(xyz_materials.size()));
for(auto& xcard : xyz_materials)
insert_value<uint32_t>(pduel->query_buffer, xcard->data.code);
}
if(query_flag & QUERY_COUNTERS) {
insert_value<uint16_t>(pduel->query_buffer, sizeof(uint32_t) + sizeof(uint32_t) + static_cast<uint16_t>(counters.size()) * sizeof(uint32_t));
insert_value<uint32_t>(pduel->query_buffer, QUERY_COUNTERS);
insert_value<uint32_t>(pduel->query_buffer, static_cast<uint32_t>(counters.size()));
for(auto& cmit : counters)
insert_value<uint32_t>(pduel->query_buffer, cmit.first + ((cmit.second[0] + cmit.second[1]) << 16));
}
CHECK_AND_INSERT_T(QUERY_OWNER, owner, uint8_t);
CHECK_AND_INSERT(QUERY_STATUS, status);
CHECK_AND_INSERT_T(QUERY_IS_PUBLIC, (is_position(POS_FACEUP) || is_related_to_chains() || (current.location == LOCATION_HAND && is_affected_by_effect(EFFECT_PUBLIC))) ? 1 : 0, uint8_t);
CHECK_AND_INSERT(QUERY_LSCALE, get_lscale());
CHECK_AND_INSERT(QUERY_RSCALE, get_rscale());
if(query_flag & QUERY_LINK) {
insert_value<uint16_t>(pduel->query_buffer, sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint32_t));
insert_value<uint32_t>(pduel->query_buffer, QUERY_LINK);
insert_value<uint32_t>(pduel->query_buffer, get_link());
insert_value<uint32_t>(pduel->query_buffer, get_link_marker());
}
CHECK_AND_INSERT_T(QUERY_IS_HIDDEN, is_affected_by_effect(EFFECT_DARKNESS_HIDE) ? 1 : 0, uint8_t);
insert_value<uint16_t>(pduel->query_buffer, sizeof(uint32_t));
insert_value<uint32_t>(pduel->query_buffer, QUERY_END);
}
int32_t card::is_related_to_chains() {
if(!is_status(STATUS_CHAINING))
return FALSE;
for(const auto& chain : pduel->game_field->core.current_chain){
if(chain.triggering_effect->get_handler() == this && is_has_relation(chain.triggering_effect)) {
return TRUE;
}
}
return FALSE;
}
#undef CHECK_AND_INSERT_T
#undef CHECK_AND_INSERT
loc_info card::get_info_location() {
if(overlay_target) {
return { overlay_target->current.controler, (uint8_t)((overlay_target->current.location | LOCATION_OVERLAY) & 0xff), overlay_target->current.sequence, current.sequence };
} else {
return { current.controler, current.location , current.sequence, current.position };
}
}
// mapping of double-name cards
uint32_t card::second_code(uint32_t code){
switch(code){
case CARD_MARINE_DOLPHIN:
return 17955766u;
case CARD_TWINKLE_MOSS:
return 17732278u;
default:
return 0;
}
}
// return: the current card name
// for double-name card, it returns printed name
uint32_t card::get_code() {
auto search = assume.find(ASSUME_CODE);
if(search != assume.end())
return search->second;
if (has_valid_property_val(temp.code))
return temp.code;
effect_set effects;
uint32_t code = data.code;
temp.code = data.code;
filter_effect(EFFECT_CHANGE_CODE, &effects);
if (effects.size())
code = effects.back()->get_value(this);
set_max_property_val(temp.code);
if (code == data.code) {
effects.clear();
filter_effect(EFFECT_ADD_CODE, &effects);
effect* addcode = nullptr;
for(auto it = effects.rbegin(); it != effects.rend(); ++it) {
if(!(*it)->operation) {
addcode = *it;
break;
}
}
if(data.alias && !addcode)
code = data.alias;
} else {
const auto& dat = pduel->read_card(code);
if (dat.alias && !second_code(code))
code = dat.alias;
}
return code;
}
// return: the current second card name
// for double-name cards, it returns the name in description
uint32_t card::get_another_code() {
uint32_t code = get_code();
if(code != data.code){
return second_code(code);
}
effect_set eset;
filter_effect(EFFECT_ADD_CODE, &eset);
effect* addcode = nullptr;
for(auto it = eset.rbegin(); it != eset.rend(); ++it) {
if(!(*it)->operation) {
addcode = *it;
break;
}
}
if(!addcode)
return 0;
uint32_t otcode = addcode->get_value(this);
if(get_code() != otcode)
if(code != otcode)
return otcode;
return 0;
}
void card::get_summon_code(std::set<uint32_t>& codes, card* scard, uint64_t sumtype, uint8_t playerid) {
effect_set eset;
bool changed = false;
filter_effect(EFFECT_ADD_CODE, &eset, FALSE);
filter_effect(EFFECT_REMOVE_CODE, &eset, FALSE);
filter_effect(EFFECT_CHANGE_CODE, &eset);
for(const auto& peffect : eset) {
if (!peffect->operation)
continue;
pduel->lua->add_param<LuaParam::CARD>(scard);
pduel->lua->add_param<LuaParam::INT>(sumtype);
pduel->lua->add_param<LuaParam::INT>(playerid);
if (!pduel->lua->check_condition(peffect->operation, 3))
continue;
if (peffect->code == EFFECT_ADD_CODE)
codes.insert(peffect->get_value(this));
else if (peffect->code == EFFECT_REMOVE_CODE) {
auto cit = codes.find(peffect->get_value(this));
if (cit != codes.end())
codes.erase(cit);
} else {
codes.clear();
codes.insert(peffect->get_value(this));
changed = true;
}
}
if (!changed) {
codes.insert(get_code());
uint32_t code = get_another_code();
if (code)
codes.insert(code);
}
}
void card::get_set_card(std::set<uint16_t>& setcodes) {
uint32_t code = get_code();
const auto& og_setcodes = (code != data.code) ? pduel->read_card(code).setcodes : data.setcodes;
setcodes.insert(og_setcodes.begin(), og_setcodes.end());
//add set code
effect_set eset;
filter_effect(EFFECT_ADD_SETCODE, &eset);
for(auto& eff : eset) {
if(eff->operation)
continue;
uint32_t value = eff->get_value(this);
for(; value > 0; value >>= 16)
setcodes.insert(value & 0xffff);
}
//another code
uint32_t code2 = get_another_code();
if (code2 != 0) {
for(auto& setcode : pduel->read_card(code2).setcodes)
setcodes.insert(setcode);
}
}
void card::get_pre_set_card(std::set<uint16_t>& setcodes) {
uint32_t code = previous.code;
const auto& og_setcodes = (code != data.code) ? pduel->read_card(code).setcodes : data.setcodes;
setcodes.insert(og_setcodes.begin(), og_setcodes.end());
//add set code
setcodes.insert(previous.setcodes.begin(), previous.setcodes.end());
//another code
if (previous.code2 != 0) {
const auto& other_setcodes = pduel->read_card(previous.code2).setcodes;
setcodes.insert(other_setcodes.begin(), other_setcodes.end());
}
}
void card::get_summon_set_card(std::set<uint16_t>& setcodes, card* scard, uint64_t sumtype, uint8_t playerid) {
effect_set eset;
std::set<uint32_t> codes;
bool changed = false;
filter_effect(EFFECT_ADD_CODE, &eset, FALSE);
filter_effect(EFFECT_REMOVE_CODE, &eset, FALSE);
filter_effect(EFFECT_CHANGE_CODE, &eset);
for(const auto& peffect : eset) {
if (!peffect->operation)
continue;
pduel->lua->add_param<LuaParam::CARD>(scard);
pduel->lua->add_param<LuaParam::INT>(sumtype);
pduel->lua->add_param<LuaParam::INT>(playerid);
if (!pduel->lua->check_condition(peffect->operation, 3))
continue;
if (peffect->code == EFFECT_ADD_CODE)
codes.insert(peffect->get_value(this));
else if (peffect->code == EFFECT_REMOVE_CODE) {
auto cit = codes.find(peffect->get_value(this));
if (cit != codes.end())
codes.erase(cit);
} else {
codes.clear();
codes.insert(peffect->get_value(this));
changed = true;
}
}
for (uint32_t code : codes) {
const auto& sets = pduel->read_card(code).setcodes;
if(sets.size())
setcodes.insert(sets.begin(), sets.end());
}
eset.clear();
filter_effect(EFFECT_ADD_SETCODE, &eset, FALSE);
filter_effect(EFFECT_CHANGE_SETCODE, &eset);
for(const auto& peffect : eset) {
if (!peffect->operation)
continue;
pduel->lua->add_param<LuaParam::CARD>(scard);
pduel->lua->add_param<LuaParam::INT>(sumtype);
pduel->lua->add_param<LuaParam::INT>(playerid);
if (!pduel->lua->check_condition(peffect->operation, 3))
continue;
uint32_t setcode = peffect->get_value(this);
if (peffect->code == EFFECT_CHANGE_SETCODE) {
setcodes.clear();
changed = true;
}
setcodes.insert(setcode & 0xffff);
}
if(!changed)
get_set_card(setcodes);
}
uint32_t card::get_type(card* scard, uint64_t sumtype, uint8_t playerid) {
auto search = assume.find(ASSUME_TYPE);
if(search != assume.end())
return search->second;
if(!(current.location & (LOCATION_ONFIELD | LOCATION_HAND | LOCATION_GRAVE)))
return data.type;
if(current.is_location(LOCATION_PZONE) && !sumtype)
return TYPE_PENDULUM + TYPE_SPELL;
if (has_valid_property_val(temp.type))
return temp.type;
effect_set effects;
int32_t type = data.type;
int32_t alttype = 0;
if(sumtype && current.location == LOCATION_SZONE)
alttype = data.type;
bool changed = false;
temp.type = data.type;
filter_effect(EFFECT_ADD_TYPE, &effects, FALSE);
filter_effect(EFFECT_REMOVE_TYPE, &effects, FALSE);
filter_effect(EFFECT_CHANGE_TYPE, &effects);
for(const auto& peffect : effects) {
if (peffect->operation && !sumtype)
continue;
if (peffect->operation) {
pduel->lua->add_param<LuaParam::CARD>(scard);
pduel->lua->add_param<LuaParam::INT>(sumtype);
pduel->lua->add_param<LuaParam::INT>(playerid);
if (!pduel->lua->check_condition(peffect->operation, 3))
continue;
pduel->lua->add_param<LuaParam::INT>(playerid);
if (peffect->code == EFFECT_ADD_TYPE)
alttype |= peffect->get_value(this,1);
else if (peffect->code == EFFECT_REMOVE_TYPE)
alttype &= ~(peffect->get_value(this,1));
else {
alttype = peffect->get_value(this, 1);
changed = true;
}
} else {
pduel->lua->add_param<LuaParam::INT>(playerid);
if (peffect->code == EFFECT_ADD_TYPE)
type |= peffect->get_value(this,1);
else if (peffect->code == EFFECT_REMOVE_TYPE)
type &= ~(peffect->get_value(this,1));
else
type = peffect->get_value(this,1);
temp.type = type;
}
}
type |= alttype;
if (changed)
type = alttype;
set_max_property_val(temp.type);
return type;
}
// Atk and def are sepcial cases since text atk/def ? are involved.
// Asuumption: we can only change the atk/def of cards in LOCATION_MZONE.
int32_t card::get_base_attack() {
if(!(data.type & TYPE_MONSTER) && !(get_type() & TYPE_MONSTER) && !is_affected_by_effect(EFFECT_PRE_MONSTER))
return 0;
if (current.location != LOCATION_MZONE || get_status(STATUS_SUMMONING | STATUS_SPSUMMON_STEP))
return data.attack;
if (has_valid_property_val(temp.base_attack))
return temp.base_attack;
int32_t batk = data.attack;
if(batk < 0)
batk = 0;
int32_t bdef = data.defense;
if(bdef < 0)
bdef = 0;
temp.base_attack = batk;
effect_set eset;
bool swap = false;
if(!(data.type & TYPE_LINK)) {
filter_effect(EFFECT_SWAP_BASE_AD, &eset, FALSE);
swap = !eset.empty();
}
filter_effect(EFFECT_SET_BASE_ATTACK, &eset, FALSE);
if(swap)
filter_effect(EFFECT_SET_BASE_DEFENSE, &eset, FALSE);
std::sort(eset.begin(), eset.end(), effect_sort_id);
// calculate continuous effects of this first
for(effect_set::size_type i = 0; i < eset.size();) {
if(!(eset[i]->type & EFFECT_TYPE_SINGLE) || eset[i]->is_flag(EFFECT_FLAG_SINGLE_RANGE)) {
switch(eset[i]->code) {
case EFFECT_SET_BASE_ATTACK:
batk = eset[i]->get_value(this);
if(batk < 0)
batk = 0;
temp.base_attack = batk;
eset.erase(eset.begin() + i);
continue;
case EFFECT_SET_BASE_DEFENSE:
bdef = eset[i]->get_value(this);
if(bdef < 0)
bdef = 0;
eset.erase(eset.begin() + i);
continue;
}
}
++i;
}
for(const auto& peffect : eset) {
switch(peffect->code) {
case EFFECT_SET_BASE_ATTACK:
batk = peffect->get_value(this);
if(batk < 0)
batk = 0;
break;
case EFFECT_SET_BASE_DEFENSE:
bdef = peffect->get_value(this);
if(bdef < 0)
bdef = 0;
break;
case EFFECT_SWAP_BASE_AD:
std::swap(batk, bdef);
break;
}
temp.base_attack = batk;
}
set_max_property_val(temp.base_attack);
return batk;
}
int32_t card::get_attack() {
auto search = assume.find(ASSUME_ATTACK);
if(search != assume.end())
return search->second;
if(!(data.type & TYPE_MONSTER) && !(get_type() & TYPE_MONSTER) && !is_affected_by_effect(EFFECT_PRE_MONSTER))
return 0;
if (current.location != LOCATION_MZONE || get_status(STATUS_SUMMONING | STATUS_SPSUMMON_STEP))
return data.attack;
if (has_valid_property_val(temp.attack))
return temp.attack;
int32_t batk = data.attack;
if(batk < 0)
batk = 0;
int32_t bdef = data.defense;
if(bdef < 0)
bdef = 0;
temp.base_attack = batk;
temp.attack = batk;
int32_t atk = -1;
int32_t up_atk = 0, upc_atk = 0;
int32_t swap_final = FALSE;
effect_set eset;
filter_effect(EFFECT_UPDATE_ATTACK, &eset, FALSE);
filter_effect(EFFECT_SET_ATTACK, &eset, FALSE);
filter_effect(EFFECT_SET_ATTACK_FINAL, &eset, FALSE);
filter_effect(EFFECT_SWAP_ATTACK_FINAL, &eset, FALSE);
filter_effect(EFFECT_SET_BASE_ATTACK, &eset, FALSE);
if(!(data.type & TYPE_LINK)) {
filter_effect(EFFECT_SWAP_AD, &eset, FALSE);
filter_effect(EFFECT_SWAP_BASE_AD, &eset, FALSE);
filter_effect(EFFECT_SET_BASE_DEFENSE, &eset, FALSE);
}
std::sort(eset.begin(), eset.end(), effect_sort_id);
int32_t rev = FALSE;
if(is_affected_by_effect(EFFECT_REVERSE_UPDATE))
rev = TRUE;
effect_set effects_atk, effects_atk_r;
for(effect_set::size_type i = 0; i < eset.size();) {
if(!(eset[i]->type & EFFECT_TYPE_SINGLE) || eset[i]->is_flag(EFFECT_FLAG_SINGLE_RANGE)) {
switch(eset[i]->code) {
case EFFECT_SET_BASE_ATTACK:
batk = eset[i]->get_value(this);
if(batk < 0)
batk = 0;
temp.base_attack = batk;
eset.erase(eset.begin() + i);
continue;
case EFFECT_SET_BASE_DEFENSE:
bdef = eset[i]->get_value(this);
if(bdef < 0)
bdef = 0;
eset.erase(eset.begin() + i);
continue;
}
}
++i;
}
temp.attack = batk;
for(const auto& peffect : eset) {
switch(peffect->code) {
case EFFECT_UPDATE_ATTACK:
if((peffect->type & EFFECT_TYPE_SINGLE) && !peffect->is_flag(EFFECT_FLAG_SINGLE_RANGE))
up_atk += peffect->get_value(this);
else
upc_atk += peffect->get_value(this);
break;
case EFFECT_SET_ATTACK:
atk = peffect->get_value(this);
if(!(peffect->type & EFFECT_TYPE_SINGLE))
up_atk = 0;
break;
case EFFECT_SET_ATTACK_FINAL:
if((peffect->type & EFFECT_TYPE_SINGLE) && !peffect->is_flag(EFFECT_FLAG_SINGLE_RANGE)) {
atk = peffect->get_value(this);
up_atk = 0;
upc_atk = 0;
} else {
if(!peffect->is_flag(EFFECT_FLAG_DELAY))
effects_atk.push_back(peffect);
else
effects_atk_r.push_back(peffect);
}
break;
case EFFECT_SET_BASE_ATTACK:
batk = peffect->get_value(this);
if(batk < 0)
batk = 0;
atk = -1;
break;
case EFFECT_SWAP_ATTACK_FINAL:
atk = peffect->get_value(this);
up_atk = 0;
upc_atk = 0;
break;
case EFFECT_SET_BASE_DEFENSE:
bdef = peffect->get_value(this);
if(bdef < 0)
bdef = 0;
break;
case EFFECT_SWAP_AD:
swap_final = !swap_final;
break;
case EFFECT_SWAP_BASE_AD:
std::swap(batk, bdef);
break;
}
temp.base_attack = batk;
if(!rev) {
temp.attack = ((atk < 0) ? batk : atk) + up_atk + upc_atk;
} else {
temp.attack = ((atk < 0) ? batk : atk) - up_atk - upc_atk;
}
if(temp.attack < 0)
temp.attack = 0;
}
for(const auto& peffect : effects_atk)
temp.attack = peffect->get_value(this);
if(!has_valid_property_val(temp.defense)) {
if(swap_final) {
temp.attack = get_defense();
}
for(const auto& peffect : effects_atk_r) {
temp.attack = peffect->get_value(this);
if(peffect->is_flag(EFFECT_FLAG_REPEAT))
temp.attack = peffect->get_value(this);
}
}
atk = temp.attack;
if(atk < 0)
atk = 0;
set_max_property_val(temp.base_attack);
set_max_property_val(temp.attack);
return atk;
}
int32_t card::get_base_defense() {
if(data.type & TYPE_LINK)
return 0;
if(!(data.type & TYPE_MONSTER) && !(get_type() & TYPE_MONSTER) && !is_affected_by_effect(EFFECT_PRE_MONSTER))
return 0;
if (current.location != LOCATION_MZONE || get_status(STATUS_SUMMONING | STATUS_SPSUMMON_STEP))
return data.defense;
if (has_valid_property_val(temp.base_defense))
return temp.base_defense;
int32_t batk = data.attack;
if(batk < 0)
batk = 0;
int32_t bdef = data.defense;
if(bdef < 0)
bdef = 0;
temp.base_defense = bdef;
effect_set eset;
filter_effect(EFFECT_SWAP_BASE_AD, &eset, FALSE);
bool swap = !eset.empty();
filter_effect(EFFECT_SET_BASE_DEFENSE, &eset, FALSE);
if(swap)
filter_effect(EFFECT_SET_BASE_ATTACK, &eset, FALSE);
std::sort(eset.begin(), eset.end(), effect_sort_id);
for(effect_set::size_type i = 0; i < eset.size();) {
if(!(eset[i]->type & EFFECT_TYPE_SINGLE) || eset[i]->is_flag(EFFECT_FLAG_SINGLE_RANGE)) {
switch(eset[i]->code) {
case EFFECT_SET_BASE_ATTACK:
batk = eset[i]->get_value(this);
if(batk < 0)
batk = 0;
eset.erase(eset.begin() + i);
continue;
case EFFECT_SET_BASE_DEFENSE:
bdef = eset[i]->get_value(this);
if(bdef < 0)
bdef = 0;
temp.base_defense = bdef;
eset.erase(eset.begin() + i);
continue;
}
}
++i;
}
for(const auto& peffect : eset) {
switch(peffect->code) {
case EFFECT_SET_BASE_ATTACK:
batk = peffect->get_value(this);
if(batk < 0)
batk = 0;
break;
case EFFECT_SET_BASE_DEFENSE:
bdef = peffect->get_value(this);
if(bdef < 0)
bdef = 0;
break;
case EFFECT_SWAP_BASE_AD:
std::swap(batk, bdef);
break;
}
temp.base_defense = bdef;
}
set_max_property_val(temp.base_defense);
return bdef;
}
int32_t card::get_defense() {
if(data.type & TYPE_LINK)
return 0;
auto search = assume.find(ASSUME_DEFENSE);
if(search != assume.end())
return search->second;
if(!(data.type & TYPE_MONSTER) && !(get_type() & TYPE_MONSTER) && !is_affected_by_effect(EFFECT_PRE_MONSTER))
return 0;
if (current.location != LOCATION_MZONE || get_status(STATUS_SUMMONING | STATUS_SPSUMMON_STEP))
return data.defense;
if (has_valid_property_val(temp.defense))
return temp.defense;
int32_t batk = data.attack;
if(batk < 0)
batk = 0;
int32_t bdef = data.defense;
if(bdef < 0)
bdef = 0;
temp.base_defense = bdef;
temp.defense = bdef;
int32_t def = -1;
int32_t up_def = 0, upc_def = 0;
int32_t swap_final = FALSE;
effect_set eset;
filter_effect(EFFECT_SWAP_AD, &eset, FALSE);
filter_effect(EFFECT_UPDATE_DEFENSE, &eset, FALSE);
filter_effect(EFFECT_SET_DEFENSE, &eset, FALSE);
filter_effect(EFFECT_SET_DEFENSE_FINAL, &eset, FALSE);
filter_effect(EFFECT_SWAP_DEFENSE_FINAL, &eset, FALSE);
filter_effect(EFFECT_SWAP_BASE_AD, &eset, FALSE);
filter_effect(EFFECT_SET_BASE_ATTACK, &eset, FALSE);
filter_effect(EFFECT_SET_BASE_DEFENSE, &eset, FALSE);
std::sort(eset.begin(), eset.end(), effect_sort_id);
int32_t rev = FALSE;
if(is_affected_by_effect(EFFECT_REVERSE_UPDATE))
rev = TRUE;
effect_set effects_def, effects_def_r;
for(effect_set::size_type i = 0; i < eset.size();) {
if(!(eset[i]->type & EFFECT_TYPE_SINGLE) || eset[i]->is_flag(EFFECT_FLAG_SINGLE_RANGE)) {
switch(eset[i]->code) {
case EFFECT_SET_BASE_ATTACK:
batk = eset[i]->get_value(this);
if(batk < 0)
batk = 0;
eset.erase(eset.begin() + i);
continue;
case EFFECT_SET_BASE_DEFENSE:
bdef = eset[i]->get_value(this);
if(bdef < 0)
bdef = 0;
temp.base_defense = bdef;
eset.erase(eset.begin() + i);
continue;
}
}
++i;
}
temp.defense = bdef;
for(const auto& peffect : eset) {
switch(peffect->code) {
case EFFECT_UPDATE_DEFENSE:
if((peffect->type & EFFECT_TYPE_SINGLE) && !peffect->is_flag(EFFECT_FLAG_SINGLE_RANGE))
up_def += peffect->get_value(this);
else
upc_def += peffect->get_value(this);
break;
case EFFECT_SET_DEFENSE:
def = peffect->get_value(this);
if(!(peffect->type & EFFECT_TYPE_SINGLE))
up_def = 0;
break;
case EFFECT_SET_DEFENSE_FINAL:
if((peffect->type & EFFECT_TYPE_SINGLE) && !peffect->is_flag(EFFECT_FLAG_SINGLE_RANGE)) {
def = peffect->get_value(this);
up_def = 0;
upc_def = 0;
} else {
if(!peffect->is_flag(EFFECT_FLAG_DELAY))
effects_def.push_back(peffect);
else
effects_def_r.push_back(peffect);
}
break;
case EFFECT_SET_BASE_DEFENSE:
bdef = peffect->get_value(this);
if(bdef < 0)
bdef = 0;
def = -1;
break;
case EFFECT_SWAP_DEFENSE_FINAL:
def = peffect->get_value(this);
up_def = 0;
upc_def = 0;
break;
case EFFECT_SET_BASE_ATTACK:
batk = peffect->get_value(this);
if(batk < 0)
batk = 0;
break;
case EFFECT_SWAP_AD:
swap_final = !swap_final;
break;
case EFFECT_SWAP_BASE_AD:
std::swap(batk, bdef);
break;
}
temp.base_defense = bdef;
if(!rev) {
temp.defense = ((def < 0) ? bdef : def) + up_def + upc_def;
} else {
temp.defense = ((def < 0) ? bdef : def) - up_def - upc_def;
}
if(temp.defense < 0)
temp.defense = 0;
}
for(const auto& peffect : effects_def)
temp.defense = peffect->get_value(this);
if(!has_valid_property_val(temp.attack)) {
if(swap_final) {
temp.defense = get_attack();
}
for(const auto& peffect : effects_def_r) {
temp.defense = peffect->get_value(this);
if(peffect->is_flag(EFFECT_FLAG_REPEAT))
temp.defense = peffect->get_value(this);
}
}
def = temp.defense;
if(def < 0)
def = 0;
set_max_property_val(temp.base_defense);
set_max_property_val(temp.defense);
return def;
}
// Level/Attribute/Race is available for:
// 1. cards with original type TYPE_MONSTER or
// 2. cards with current type TYPE_MONSTER or
// 3. cards with EFFECT_PRE_MONSTER
uint32_t card::get_level() {
if(((data.type & TYPE_XYZ) && !(is_affected_by_effect(EFFECT_RANK_LEVEL) || is_affected_by_effect(EFFECT_RANK_LEVEL_S)))
|| (data.type & TYPE_LINK) || (status & STATUS_NO_LEVEL)
|| (!(data.type & TYPE_MONSTER) && !(get_type() & TYPE_MONSTER) && !is_affected_by_effect(EFFECT_PRE_MONSTER)))
return 0;
auto search = assume.find(ASSUME_LEVEL);
if(search != assume.end())
return search->second;
if (has_valid_property_val(temp.level))
return temp.level;
effect_set effects;
int32_t level = data.level;
temp.level = level;
int32_t up = 0, upc = 0;
if (is_affected_by_effect(EFFECT_RANK_LEVEL_S)|| is_affected_by_effect(EFFECT_LEVEL_RANK_S)) {
filter_effect(EFFECT_UPDATE_RANK, &effects, FALSE);
filter_effect(EFFECT_UPDATE_LEVEL, &effects, FALSE);
filter_effect(EFFECT_CHANGE_RANK, &effects, FALSE);
filter_effect(EFFECT_CHANGE_LEVEL, &effects, FALSE);
filter_effect(EFFECT_CHANGE_RANK_FINAL, &effects, FALSE);
filter_effect(EFFECT_CHANGE_LEVEL_FINAL, &effects);
}
else {
filter_effect(EFFECT_UPDATE_LEVEL, &effects, FALSE);
filter_effect(EFFECT_CHANGE_LEVEL, &effects, FALSE);
filter_effect(EFFECT_CHANGE_LEVEL_FINAL, &effects);
}
for(const auto& peffect : effects) {
switch (peffect->code) {
case EFFECT_UPDATE_RANK:
case EFFECT_UPDATE_LEVEL:
if ((peffect->type & EFFECT_TYPE_SINGLE) && !peffect->is_flag(EFFECT_FLAG_SINGLE_RANGE))
up += peffect->get_value(this);
else
upc += peffect->get_value(this);
break;
case EFFECT_CHANGE_RANK:
case EFFECT_CHANGE_LEVEL:
level = peffect->get_value(this);
up = 0;
break;
case EFFECT_CHANGE_RANK_FINAL:
case EFFECT_CHANGE_LEVEL_FINAL:
level = peffect->get_value(this);
up = 0;
upc = 0;
break;
}
temp.level = level + up + upc;
}
level += up + upc;
if(level < 1 && (get_type() & TYPE_MONSTER) && !is_affected_by_effect(EFFECT_ALLOW_NEGATIVE))
level = 1;
set_max_property_val(temp.level);
return level;
}
uint32_t card::get_rank() {
if(((!(data.type & TYPE_XYZ) || (status & STATUS_NO_LEVEL)) && !(is_affected_by_effect(EFFECT_LEVEL_RANK) || is_affected_by_effect(EFFECT_LEVEL_RANK_S)))
|| (data.type & TYPE_LINK))
return 0;
auto search = assume.find(ASSUME_RANK);
if(search != assume.end())
return search->second;
if(!(current.location & LOCATION_MZONE))
return data.level;
if (has_valid_property_val(temp.rank))
return temp.rank;
effect_set effects;
int32_t rank = data.level;
temp.rank = rank;
int32_t up = 0, upc = 0;
if (is_affected_by_effect(EFFECT_RANK_LEVEL_S) || is_affected_by_effect(EFFECT_LEVEL_RANK_S)) {
filter_effect(EFFECT_UPDATE_RANK, &effects, FALSE);
filter_effect(EFFECT_UPDATE_LEVEL, &effects, FALSE);
filter_effect(EFFECT_CHANGE_RANK, &effects, FALSE);
filter_effect(EFFECT_CHANGE_LEVEL, &effects, FALSE);
filter_effect(EFFECT_CHANGE_RANK_FINAL, &effects, FALSE);
filter_effect(EFFECT_CHANGE_LEVEL_FINAL, &effects);
}
else {
filter_effect(EFFECT_UPDATE_RANK, &effects, FALSE);
filter_effect(EFFECT_CHANGE_RANK, &effects, FALSE);
filter_effect(EFFECT_CHANGE_RANK_FINAL, &effects);
}
for(const auto& peffect : effects) {
switch (peffect->code) {
case EFFECT_UPDATE_RANK:
case EFFECT_UPDATE_LEVEL:
if ((peffect->type & EFFECT_TYPE_SINGLE) && !peffect->is_flag(EFFECT_FLAG_SINGLE_RANGE))
up += peffect->get_value(this);
else
upc += peffect->get_value(this);
break;
case EFFECT_CHANGE_RANK:
case EFFECT_CHANGE_LEVEL:
rank = peffect->get_value(this);
up = 0;
break;
case EFFECT_CHANGE_RANK_FINAL:
case EFFECT_CHANGE_LEVEL_FINAL:
rank = peffect->get_value(this);
up = 0;
upc = 0;
break;
}
temp.rank = rank + up + upc;
}
rank += up + upc;
if(rank < 1 && (get_type() & TYPE_MONSTER) && !is_affected_by_effect(EFFECT_ALLOW_NEGATIVE))
rank = 1;
set_max_property_val(temp.rank);
return rank;
}
uint32_t card::get_link() {
if(!(data.type & TYPE_LINK) || (status & STATUS_NO_LEVEL))
return 0;
auto search = assume.find(ASSUME_LINK);
if(search != assume.end())
return search->second;
if(!(current.location & LOCATION_MZONE))
return data.level;
if (has_valid_property_val(temp.link))
return temp.link;
effect_set effects;
int32_t link = data.level;
temp.link = link;
int32_t up = 0, upc = 0;
filter_effect(EFFECT_UPDATE_LINK, &effects, FALSE);
filter_effect(EFFECT_CHANGE_LINK, &effects, FALSE);
filter_effect(EFFECT_CHANGE_LINK_FINAL, &effects);
for(const auto& peffect : effects) {
switch (peffect->code) {
case EFFECT_UPDATE_LINK:
if ((peffect->type & EFFECT_TYPE_SINGLE) && !peffect->is_flag(EFFECT_FLAG_SINGLE_RANGE))
up += peffect->get_value(this);
else
upc += peffect->get_value(this);
break;
case EFFECT_CHANGE_LINK:
link = peffect->get_value(this);
up = 0;
break;
case EFFECT_CHANGE_LINK_FINAL:
link = peffect->get_value(this);
up = 0;
upc = 0;
break;
}
temp.link = link + up + upc;
}
link += up + upc;
if(link < 1 && (get_type() & TYPE_MONSTER) && !is_affected_by_effect(EFFECT_ALLOW_NEGATIVE))
link = 1;
set_max_property_val(temp.link);
return link;
}
uint32_t card::get_synchro_level(card* pcard) {
if((data.type & TYPE_LINK) && !(is_affected_by_effect(EFFECT_SYNCHRO_LEVEL)))
return 0;
if(((data.type & TYPE_XYZ) || (status & STATUS_NO_LEVEL))
&& !(is_affected_by_effect(EFFECT_RANK_LEVEL) || is_affected_by_effect(EFFECT_RANK_LEVEL_S)))
return 0;
uint32_t lev;
effect_set eset;
filter_effect(EFFECT_SYNCHRO_LEVEL, &eset);
if(eset.size())
lev = eset[0]->get_value(pcard);
else
lev = get_level();
return lev;
}
uint32_t card::get_ritual_level(card* pcard) {
if(data.type & TYPE_LINK)
return 0;
if(((data.type & TYPE_XYZ) || (status & STATUS_NO_LEVEL))
&& !(is_affected_by_effect(EFFECT_RANK_LEVEL) || is_affected_by_effect(EFFECT_RANK_LEVEL_S)))
return 0;
uint32_t lev;
effect_set eset;
filter_effect(EFFECT_RITUAL_LEVEL, &eset);
if(eset.size())
lev = eset[0]->get_value(pcard);
else
lev = get_level();
return lev;
}
uint32_t card::check_xyz_level(card* pcard, uint32_t lv) {
if(status & STATUS_NO_LEVEL)
return FALSE;
effect_set eset;
filter_effect(EFFECT_XYZ_LEVEL, &eset);
for(auto& eff : eset) {
std::vector<lua_Integer> res;
pduel->lua->add_param<LuaParam::CARD>(this);
pduel->lua->add_param<LuaParam::CARD>(pcard);
eff->get_value(2, res);
if(res.size() == 1) {
uint32_t lev = static_cast<uint32_t>(res[0]);
if((lev & 0xfff) == lv || ((lev >> 16) & 0xfff) == lv)
return TRUE;
} else {
if(std::find(res.begin(), res.end(), lv) != res.end())
return TRUE;
}
}
return get_level() == lv;
}
// see get_level()
uint32_t card::get_attribute(card* scard, uint64_t sumtype, uint8_t playerid) {
auto search = assume.find(ASSUME_ATTRIBUTE);
if(search != assume.end())
return search->second;
if(!(data.type & TYPE_MONSTER) && !(get_type() & TYPE_MONSTER) && !is_affected_by_effect(EFFECT_PRE_MONSTER))
return 0;
if (has_valid_property_val(temp.attribute))
return temp.attribute;
effect_set effects;
int32_t attribute = data.attribute, altattribute = 0;
bool changed = false;
temp.attribute = data.attribute;
filter_effect(EFFECT_ADD_ATTRIBUTE, &effects, FALSE);
filter_effect(EFFECT_REMOVE_ATTRIBUTE, &effects, FALSE);
filter_effect(EFFECT_CHANGE_ATTRIBUTE, &effects);
for(const auto& peffect : effects) {
if (peffect->operation && !sumtype)
continue;
if (peffect->operation) {
pduel->lua->add_param<LuaParam::CARD>(scard);
pduel->lua->add_param<LuaParam::INT>(sumtype);
pduel->lua->add_param<LuaParam::INT>(playerid);
if (!pduel->lua->check_condition(peffect->operation, 3))
continue;
pduel->lua->add_param<LuaParam::INT>(playerid);
if (peffect->code == EFFECT_ADD_ATTRIBUTE)
altattribute |= peffect->get_value(this, 1);
else if (peffect->code == EFFECT_REMOVE_ATTRIBUTE)
altattribute &= ~(peffect->get_value(this, 1));
else if (peffect->code == EFFECT_CHANGE_ATTRIBUTE) {
altattribute = peffect->get_value(this, 1);
changed = true;
}
} else {
pduel->lua->add_param<LuaParam::INT>(playerid);
if (peffect->code == EFFECT_ADD_ATTRIBUTE)
attribute |= peffect->get_value(this, 1);
else if (peffect->code == EFFECT_REMOVE_ATTRIBUTE)
attribute &= ~(peffect->get_value(this, 1));
else if (peffect->code == EFFECT_CHANGE_ATTRIBUTE)
attribute = peffect->get_value(this, 1);
temp.attribute = attribute;
}
}
attribute |= altattribute;
if (changed)
attribute = altattribute;
set_max_property_val(temp.attribute);
return attribute;
}
// see get_level()
uint64_t card::get_race(card* scard, uint64_t sumtype, uint8_t playerid) {
auto search = assume.find(ASSUME_RACE);
if(search != assume.end())
return search->second;
if(!(data.type & TYPE_MONSTER) && !(get_type() & TYPE_MONSTER) && !is_affected_by_effect(EFFECT_PRE_MONSTER))
return 0;
if (has_valid_property_val(temp.race))
return temp.race;
effect_set effects;
uint64_t race = data.race, altrace = 0;
bool changed = false;
temp.race = data.race;
filter_effect(EFFECT_ADD_RACE, &effects, FALSE);
filter_effect(EFFECT_REMOVE_RACE, &effects, FALSE);
filter_effect(EFFECT_CHANGE_RACE, &effects);
for(const auto& peffect : effects) {
if (peffect->operation && !sumtype)
continue;
if (peffect->operation) {
pduel->lua->add_param<LuaParam::CARD>(scard);
pduel->lua->add_param<LuaParam::INT>(sumtype);
pduel->lua->add_param<LuaParam::INT>(playerid);
if (!pduel->lua->check_condition(peffect->operation, 3))
continue;
pduel->lua->add_param<LuaParam::INT>(playerid);
if (peffect->code == EFFECT_ADD_RACE)
altrace |= peffect->get_value(this,1);
else if (peffect->code == EFFECT_REMOVE_RACE)
altrace &= ~(peffect->get_value(this,1));
else if (peffect->code == EFFECT_CHANGE_RACE) {
altrace = peffect->get_value(this, 1);
changed = true;
}
} else {
pduel->lua->add_param<LuaParam::INT>(playerid);
if (peffect->code == EFFECT_ADD_RACE)
race |= peffect->get_value(this,1);
else if (peffect->code == EFFECT_REMOVE_RACE)
race &= ~(peffect->get_value(this, 1));
else if (peffect->code == EFFECT_CHANGE_RACE)
race = peffect->get_value(this, 1);
temp.race = race;
}
}
race |= altrace;
if (changed)
race = altrace;
set_max_property_val(temp.race);
return race;
}
uint32_t card::get_lscale() {
if(!current.is_location(LOCATION_PZONE))
return data.lscale;
if (has_valid_property_val(temp.lscale))
return temp.lscale;
effect_set effects;
int32_t lscale = data.lscale;
temp.lscale = data.lscale;
int32_t up = 0, upc = 0;
filter_effect(EFFECT_UPDATE_LSCALE, &effects, FALSE);
filter_effect(EFFECT_CHANGE_LSCALE, &effects);
for(const auto& peffect : effects) {
if (peffect->code == EFFECT_UPDATE_LSCALE) {
if ((peffect->type & EFFECT_TYPE_SINGLE) && !peffect->is_flag(EFFECT_FLAG_SINGLE_RANGE))
up += peffect->get_value(this);
else
upc += peffect->get_value(this);
} else {
lscale = peffect->get_value(this);
up = 0;
}
temp.lscale = lscale;
}
lscale += up + upc;
set_max_property_val(temp.lscale);
return lscale;
}
uint32_t card::get_rscale() {
if(!current.is_location(LOCATION_PZONE))
return data.rscale;
if (has_valid_property_val(temp.rscale))
return temp.rscale;
effect_set effects;
int32_t rscale = data.rscale;
temp.rscale = data.rscale;
int32_t up = 0, upc = 0;
filter_effect(EFFECT_UPDATE_RSCALE, &effects, FALSE);
filter_effect(EFFECT_CHANGE_RSCALE, &effects);
for(const auto& peffect : effects) {
if (peffect->code == EFFECT_UPDATE_RSCALE) {
if ((peffect->type & EFFECT_TYPE_SINGLE) && !peffect->is_flag(EFFECT_FLAG_SINGLE_RANGE))
up += peffect->get_value(this);
else
upc += peffect->get_value(this);
} else {
rscale = peffect->get_value(this);
up = 0;
}
temp.rscale = rscale;
}
rscale += up + upc;
set_max_property_val(temp.rscale);
return rscale;
}
uint32_t card::get_link_marker() {
auto rotate = [](uint32_t& marker) {
marker=(((marker & LINK_MARKER_BOTTOM_LEFT) ? LINK_MARKER_BOTTOM_RIGHT : 0) |
((marker & LINK_MARKER_BOTTOM) ? LINK_MARKER_RIGHT : 0) |
((marker & LINK_MARKER_BOTTOM_RIGHT) ? LINK_MARKER_TOP_RIGHT : 0) |
((marker & LINK_MARKER_RIGHT) ? LINK_MARKER_TOP : 0) |
((marker & LINK_MARKER_TOP_RIGHT) ? LINK_MARKER_TOP_LEFT : 0) |
((marker & LINK_MARKER_TOP) ? LINK_MARKER_LEFT : 0) |
((marker & LINK_MARKER_TOP_LEFT) ? LINK_MARKER_BOTTOM_LEFT : 0) |
((marker & LINK_MARKER_LEFT) ? LINK_MARKER_BOTTOM : 0));
};
if((current.position & POS_FACEDOWN) && (current.is_location(LOCATION_ONFIELD)))
return 0;
auto search = assume.find(ASSUME_LINKMARKER);
if(search != assume.end())
return search->second;
if(!(get_type() & TYPE_LINK))
return 0;
if (has_valid_property_val(temp.link_marker))
return temp.link_marker;
effect_set effects;
uint32_t link_marker = data.link_marker;
temp.link_marker = data.link_marker;
filter_effect(EFFECT_ADD_LINKMARKER, &effects, FALSE);
filter_effect(EFFECT_REMOVE_LINKMARKER, &effects, FALSE);
filter_effect(EFFECT_CHANGE_LINKMARKER, &effects);
for(const auto& peffect : effects) {
if (peffect->code == EFFECT_ADD_LINKMARKER)
link_marker |= peffect->get_value(this);
else if (peffect->code == EFFECT_REMOVE_LINKMARKER)
link_marker &= ~(peffect->get_value(this));
else if (peffect->code == EFFECT_CHANGE_LINKMARKER)
link_marker = peffect->get_value(this);
temp.link_marker = link_marker;
}
set_max_property_val(temp.link_marker);
if((current.position & POS_ATTACK) == 0 && current.is_location(LOCATION_ONFIELD))
rotate(link_marker);
return link_marker;
}
int32_t card::is_link_marker(uint32_t dir) {
return (int32_t)(get_link_marker() & dir);
}
uint32_t card::get_linked_zone(bool free) {
if(!(get_type() & TYPE_LINK) || !(current.location & LOCATION_ONFIELD) || get_status(STATUS_SUMMONING | STATUS_SPSUMMON_STEP))
return 0;
int32_t zones = 0;
int32_t s = current.sequence;
int32_t location = current.location;
auto marker = get_link_marker();
if(location == LOCATION_MZONE) {
if(s > 0 && s <= 4 && is_link_marker(LINK_MARKER_LEFT, marker))
zones |= 1u << (s - 1);
if(s <= 3 && is_link_marker(LINK_MARKER_RIGHT, marker))
zones |= 1u << (s + 1);
if(s < 5) {
if(s > 0 && is_link_marker(LINK_MARKER_BOTTOM_LEFT, marker))
zones |= 1u << (s + 7);
if(s < 4 && is_link_marker(LINK_MARKER_BOTTOM_RIGHT, marker))
zones |= 1u << (s + 9);
if(is_link_marker(LINK_MARKER_BOTTOM, marker))
zones |= 1u << (s + 8);
}
if(pduel->game_field->is_flag(DUEL_EMZONE)) {
if((s == 0 && is_link_marker(LINK_MARKER_TOP_RIGHT, marker))
|| (s == 1 && is_link_marker(LINK_MARKER_TOP, marker))
|| (s == 2 && is_link_marker(LINK_MARKER_TOP_LEFT, marker)))
zones |= (1u << 5) | (1u << (16 + 6));
if((s == 2 && is_link_marker(LINK_MARKER_TOP_RIGHT, marker))
|| (s == 3 && is_link_marker(LINK_MARKER_TOP, marker))
|| (s == 4 && is_link_marker(LINK_MARKER_TOP_LEFT, marker)))
zones |= (1u << 6) | (1u << (16 + 5));
if(s == 5) {
if(is_link_marker(LINK_MARKER_BOTTOM_LEFT, marker))
zones |= 1u << 0;
if(is_link_marker(LINK_MARKER_BOTTOM, marker))
zones |= 1u << 1;
if(is_link_marker(LINK_MARKER_BOTTOM_RIGHT, marker))
zones |= 1u << 2;
if(is_link_marker(LINK_MARKER_TOP_LEFT, marker))
zones |= 1u << (16 + 4);
if(is_link_marker(LINK_MARKER_TOP, marker))
zones |= 1u << (16 + 3);
if(is_link_marker(LINK_MARKER_TOP_RIGHT, marker))
zones |= 1u << (16 + 2);
}
if(s == 6) {
if(is_link_marker(LINK_MARKER_BOTTOM_LEFT, marker))
zones |= 1u << 2;
if(is_link_marker(LINK_MARKER_BOTTOM, marker))
zones |= 1u << 3;
if(is_link_marker(LINK_MARKER_BOTTOM_RIGHT, marker))
zones |= 1u << 4;
if(is_link_marker(LINK_MARKER_TOP_LEFT, marker))
zones |= 1u << (16 + 2);
if(is_link_marker(LINK_MARKER_TOP, marker))
zones |= 1u << (16 + 1);
if(is_link_marker(LINK_MARKER_TOP_RIGHT, marker))
zones |= 1u << (16 + 0);
}
}
} else if(location == LOCATION_SZONE) {
if(s > 4)
return 0;
if(s > 0) {
if(is_link_marker(LINK_MARKER_LEFT, marker))
zones |= 1u << (s + 7);
if(is_link_marker(LINK_MARKER_TOP_LEFT, marker))
zones |= 1u << (s - 1);
}
if(s < 4) {
if(is_link_marker(LINK_MARKER_RIGHT, marker))
zones |= 1u << (s + 9);
if(is_link_marker(LINK_MARKER_TOP_RIGHT, marker))
zones |= 1u << (s + 1);
}
if(is_link_marker(LINK_MARKER_TOP, marker))
zones |= 1u << s;
}
if(free) {
for(int i = 0; i < 8; ++i) {
if(!pduel->game_field->is_location_useable(current.controler, LOCATION_MZONE, i)) {
zones &= ~(1u << i);
}
}
for(int i = 0; i < 8; ++i) {
if(!pduel->game_field->is_location_useable(current.controler, LOCATION_SZONE, i)) {
zones &= ~(1u << (i + 8));
}
}
for(int i = 0; i < 8; ++i) {
if(!pduel->game_field->is_location_useable(1 - current.controler, LOCATION_MZONE, i)) {
zones &= ~(1u << (i + 16));
}
}
for(int i = 0; i < 8; ++i) {
if(!pduel->game_field->is_location_useable(1 - current.controler, LOCATION_SZONE, i)) {
zones &= ~(1u << (i + 24));
}
}
}
return zones;
}
void card::get_linked_cards(card_set* cset, uint32_t zones) {
cset->clear();
if(!(data.type & TYPE_LINK) || !(current.location & LOCATION_ONFIELD) || get_status(STATUS_SUMMONING | STATUS_SPSUMMON_STEP))
return;
int32_t p = current.controler;
uint32_t linked_zone = (zones) ? zones : get_linked_zone();
pduel->game_field->get_cards_in_zone(cset, linked_zone, p, LOCATION_ONFIELD);
pduel->game_field->get_cards_in_zone(cset, linked_zone >> 16, 1 - p, LOCATION_ONFIELD);
for(auto it = cset->begin(); it != cset->end();) {
if((*it)->current.location == LOCATION_SZONE && !((*it)->get_type() & TYPE_LINK)) {
it = cset->erase(it);
continue;
}
++it;
}
}
uint32_t card::get_mutual_linked_zone() {
uint32_t linked_zone = get_linked_zone();
if(!linked_zone)
return 0;
int32_t zones = 0;
card_set cset;
get_linked_cards(&cset, linked_zone);
for(auto& pcard : cset) {
auto zone = pcard->get_linked_zone();
uint32_t is_szone = pcard->current.location == LOCATION_SZONE ? 8 : 0;
uint32_t is_player = (current.controler == pcard->current.controler) ? 0 : 16;
if(is_mutual_linked(pcard, linked_zone, zone)) {
zones |= 1 << (pcard->current.sequence + is_szone + is_player);
}
}
return zones;
}
void card::get_mutual_linked_cards(card_set* cset) {
cset->clear();
if(!(data.type & TYPE_LINK) || !(current.location & LOCATION_ONFIELD))
return;
int32_t p = current.controler;
uint32_t mutual_linked_zone = get_mutual_linked_zone();
pduel->game_field->get_cards_in_zone(cset, mutual_linked_zone, p, LOCATION_ONFIELD);
pduel->game_field->get_cards_in_zone(cset, mutual_linked_zone >> 16, 1 - p, LOCATION_ONFIELD);
}
int32_t card::is_link_state() {
if(!(current.location & LOCATION_ONFIELD))
return FALSE;
card_set cset;
get_linked_cards(&cset);
if(cset.size())
return TRUE;
int32_t p = current.controler;
uint32_t is_szone = current.location == LOCATION_SZONE ? 8 : 0;
uint32_t linked_zone = pduel->game_field->get_linked_zone(p, false, true);
if((linked_zone >> (current.sequence + is_szone)) & 1)
return TRUE;
return FALSE;
}
int32_t card::is_mutual_linked(card* pcard, uint32_t zones1, uint32_t zones2) {
int32_t ret = FALSE;
if(!zones1)
zones1 = get_linked_zone();
uint32_t is_szone = pcard->current.location == LOCATION_SZONE ? 8 : 0;
uint32_t is_player = (current.controler == pcard->current.controler) ? 0 : 16;
if(zones1 & (1 << (pcard->current.sequence + is_szone + is_player))) {
zones2 = zones2 ? zones2 : pcard->get_linked_zone();
is_szone = current.location == LOCATION_SZONE ? 8 : 0;
is_player = (current.controler == pcard->current.controler) ? 0 : 16;
ret = zones2 & (1 << (current.sequence + is_szone + is_player));
}
return ret;
}
int32_t card::is_extra_link_state() {
if(current.location != LOCATION_MZONE)
return FALSE;
uint32_t checked = (current.location == LOCATION_MZONE) ? (1u << current.sequence) : (1u << (current.sequence + 8));
uint32_t linked_zone = get_mutual_linked_zone();
const auto& list_mzone0 = pduel->game_field->player[current.controler].list_mzone;
const auto& list_szone0 = pduel->game_field->player[current.controler].list_szone;
const auto& list_mzone1 = pduel->game_field->player[1 - current.controler].list_mzone;
const auto& list_szone1 = pduel->game_field->player[1 - current.controler].list_szone;
while(true) {
if(((linked_zone >> 5) | (linked_zone >> (16 + 6))) & ((linked_zone >> 6) | (linked_zone >> (16 + 5))) & 1)
return TRUE;
int32_t checking = (int32_t)(linked_zone & ~checked);
if(!checking)
return FALSE;
int32_t rightmost = checking & (-checking);
checked |= (uint32_t)rightmost;
if(rightmost < 0x10000) {
for(int32_t i = 0; i < 16; ++i) {
if(rightmost & 1) {
card* pcard = (i < 8) ? list_mzone0[i] : list_szone0[i - 8];
linked_zone |= pcard->get_mutual_linked_zone();
break;
}
rightmost >>= 1;
}
} else {
rightmost >>= 16;
for(int32_t i = 0; i < 16; ++i) {
if(rightmost & 1) {
card* pcard = (i < 8) ? list_mzone1[i] : list_szone1[i - 8];
uint32_t zone = pcard->get_mutual_linked_zone();
linked_zone |= ((zone & 0xffff) << 16) | (zone >> 16);
break;
}
rightmost >>= 1;
}
}
}
return FALSE;
}
uint32_t card::get_column_zone(int32_t loc1, int32_t left, int32_t right) {
int32_t zones = 0;
int32_t loc2 = current.location;
int32_t seq = current.sequence;
if(!(loc1 & LOCATION_ONFIELD) || !(loc2 & LOCATION_ONFIELD) || left < 0 || right < 0)
return 0;
if(loc2 & LOCATION_SZONE && seq >= 5)
return 0;
if (loc2 & LOCATION_MZONE && (seq == 5 || seq == 6))
seq = (seq == 5) ? 1 : 3;
int32_t seq1 = seq - left < 0 ? 0 : seq - left;
int32_t seq2 = seq + right > 4 ? 4 : seq + right;
auto chkextra = [seq = current.sequence, mzone = (current.location == LOCATION_MZONE)](uint8_t s)->bool {
return !mzone || seq < 5 || seq != s;
};
if (loc1 & LOCATION_MZONE) {
for (int32_t s = seq1; s <= seq2; ++s) {
zones |= (1u << s) | (1u << (16 + (4 - s)));
if (pduel->game_field->is_flag(DUEL_EMZONE)) {
if (s == 1 && chkextra(5))
zones |= (1u << 5) | (1u << (16 + 6));
else if (s == 3 && chkextra(6))
zones |= (1u << 6) | (1u << (16 + 5));
}
}
}
if (loc1 & LOCATION_SZONE) {
for (int32_t s = seq1; s <= seq2; ++s)
zones |= (1u << (8 + s)) | (1u << (16 + 8 + (4 - s)));
}
zones &= ~((1 << current.sequence) << ((loc2 == LOCATION_SZONE) ? 8 : 0));
return zones;
}
void card::get_column_cards(card_set* cset, int32_t left, int32_t right) {
cset->clear();
if(!(current.location & LOCATION_ONFIELD))
return;
int32_t p = current.controler;
uint32_t column_zone = get_column_zone(LOCATION_ONFIELD, left, right);
pduel->game_field->get_cards_in_zone(cset, column_zone, p, LOCATION_ONFIELD);
pduel->game_field->get_cards_in_zone(cset, column_zone >> 16, 1 - p, LOCATION_ONFIELD);
}
int32_t card::is_all_column() {
if(!(current.location & LOCATION_ONFIELD))
return FALSE;
card_set cset;
get_column_cards(&cset, 0, 0);
uint32_t full = 3;
if(pduel->game_field->is_flag(DUEL_EMZONE) && (current.sequence == 1 || current.sequence == 3))
++full;
if(cset.size() == full)
return TRUE;
return FALSE;
}
void card::equip(card* target, uint32_t send_msg) {
if (equiping_target)
return;
target->equiping_cards.insert(this);
equiping_target = target;
for (auto& it : equip_effect) {
if (it.second->is_disable_related())
pduel->game_field->add_to_disable_check_list(equiping_target);
}
if(send_msg) {
auto message = pduel->new_message(MSG_EQUIP);
message->write(get_info_location());
message->write(target->get_info_location());
}
return;
}
void card::unequip() {
if (!equiping_target)
return;
for (auto it = equip_effect.begin(); it != equip_effect.end(); ++it) {
if (it->second->is_disable_related())
pduel->game_field->add_to_disable_check_list(equiping_target);
}
equiping_target->equiping_cards.erase(this);
pre_equip_target = equiping_target;
equiping_target = nullptr;
return;
}
int32_t card::get_union_count() {
int32_t count = 0;
for(auto& pcard : equiping_cards) {
if((pcard->data.type & TYPE_UNION) && pcard->is_affected_by_effect(EFFECT_UNION_STATUS))
++count;
}
return count;
}
int32_t card::get_old_union_count() {
int32_t count = 0;
for(auto& pcard : equiping_cards) {
if((pcard->data.type & TYPE_UNION) && pcard->is_affected_by_effect(EFFECT_OLDUNION_STATUS))
++count;
}
return count;
}
void card::xyz_add(card* mat) {
if(mat->current.location != 0)
return;
xyz_materials.push_back(mat);
mat->overlay_target = this;
mat->pre_overlay_target = nullptr;
mat->current.controler = PLAYER_NONE;
mat->current.location = LOCATION_OVERLAY;
mat->current.sequence = static_cast<uint32_t>(xyz_materials.size() - 1);
for(auto& eit : mat->xmaterial_effect) {
effect* peffect = eit.second;
if(peffect->type & EFFECT_TYPE_FIELD)
pduel->game_field->add_effect(peffect);
}
}
void card::xyz_remove(card* mat) {
if(mat->overlay_target != this)
return;
xyz_materials.erase(xyz_materials.begin() + mat->current.sequence);
if(pduel->game_field->core.current_chain.size() > 0)
pduel->game_field->core.just_sent_cards.insert(mat);
mat->previous.controler = mat->current.controler;
mat->previous.location = mat->current.location;
mat->previous.sequence = mat->current.sequence;
mat->previous.pzone = mat->current.pzone;
mat->current.controler = PLAYER_NONE;
mat->current.location = 0;
mat->current.sequence = 0;
mat->pre_overlay_target = std::exchange(mat->overlay_target, nullptr);
for(auto clit = xyz_materials.begin(); clit != xyz_materials.end(); ++clit)
(*clit)->current.sequence = clit - xyz_materials.begin();
for(auto eit = mat->xmaterial_effect.begin(); eit != mat->xmaterial_effect.end(); ++eit) {
effect* peffect = eit->second;
if(peffect->type & EFFECT_TYPE_FIELD)
pduel->game_field->remove_effect(peffect);
}
}
void card::apply_field_effect() {
if (current.controler == PLAYER_NONE)
return;
for (auto& it : field_effect) {
if (it.second->in_range(this)
|| ((it.second->range & LOCATION_HAND) && (it.second->type & EFFECT_TYPE_TRIGGER_O) && !(it.second->code & EVENT_PHASE))) {
pduel->game_field->add_effect(it.second);
}
}
if(unique_code && (current.location & unique_location))
pduel->game_field->add_unique_card(this);
spsummon_counter[0] = spsummon_counter[1] = 0;
spsummon_counter_rst[0] = spsummon_counter_rst[1] = 0;
}
void card::cancel_field_effect() {
if (current.controler == PLAYER_NONE)
return;
for (auto& it : field_effect) {
if (it.second->in_range(this)
|| ((it.second->range & LOCATION_HAND) && (it.second->type & EFFECT_TYPE_TRIGGER_O) && !(it.second->code & EVENT_PHASE))) {
pduel->game_field->remove_effect(it.second);
}
}
if(unique_code && (current.location & unique_location))
pduel->game_field->remove_unique_card(this);
}
// STATUS_EFFECT_ENABLED: the card is ready to use
// false: before moving, summoning, chaining
// true: ready
void card::enable_field_effect(bool enabled) {
if (current.location == 0)
return;
if ((enabled && get_status(STATUS_EFFECT_ENABLED)) || (!enabled && !get_status(STATUS_EFFECT_ENABLED)))
return;
refresh_disable_status();
if (enabled) {
set_status(STATUS_EFFECT_ENABLED, TRUE);
for (auto& it : single_effect) {
if (it.second->is_flag(EFFECT_FLAG_SINGLE_RANGE) && it.second->in_range(this))
it.second->id = pduel->game_field->infos.field_id++;
}
for (auto& it : field_effect) {
if (it.second->in_range(this))
it.second->id = pduel->game_field->infos.field_id++;
}
if(current.location == LOCATION_SZONE) {
for (auto& it : equip_effect)
it.second->id = pduel->game_field->infos.field_id++;
}
for (auto& it : target_effect) {
if (it.second->in_range(this))
it.second->id = pduel->game_field->infos.field_id++;
}
if (get_status(STATUS_DISABLED))
reset(RESET_DISABLE, RESET_EVENT);
} else
set_status(STATUS_EFFECT_ENABLED, FALSE);
if (get_status(STATUS_DISABLED | STATUS_FORBIDDEN))
return;
filter_disable_related_cards();
}
int32_t card::add_effect(effect* peffect) {
if (get_status(STATUS_COPYING_EFFECT) && peffect->is_flag(EFFECT_FLAG_UNCOPYABLE)) {
pduel->uncopy.insert(peffect);
return 0;
}
if (indexer.find(peffect) != indexer.end())
return 0;
card_set check_target = { this };
effect_container::iterator eit;
if (peffect->type & EFFECT_TYPE_SINGLE) {
if((peffect->code == EFFECT_SET_ATTACK || peffect->code == EFFECT_SET_BASE_ATTACK) && !peffect->is_flag(EFFECT_FLAG_SINGLE_RANGE)) {
for(auto it = single_effect.begin(); it != single_effect.end();) {
auto rm = it++;
if((rm->second->code == EFFECT_SET_ATTACK || rm->second->code == EFFECT_SET_ATTACK_FINAL || rm->second->code == EFFECT_SET_BASE_ATTACK)
&& !rm->second->is_flag(EFFECT_FLAG_SINGLE_RANGE))
remove_effect(rm->second);
}
}
if(peffect->code == EFFECT_SET_ATTACK_FINAL && !peffect->is_flag(EFFECT_FLAG_SINGLE_RANGE)) {
for(auto it = single_effect.begin(); it != single_effect.end();) {
auto rm = it++;
if((rm->second->code == EFFECT_UPDATE_ATTACK || rm->second->code == EFFECT_SET_ATTACK
|| rm->second->code == EFFECT_SET_ATTACK_FINAL) && !rm->second->is_flag(EFFECT_FLAG_SINGLE_RANGE))
remove_effect(rm->second);
}
}
if((peffect->code == EFFECT_SET_DEFENSE || peffect->code == EFFECT_SET_BASE_DEFENSE) && !peffect->is_flag(EFFECT_FLAG_SINGLE_RANGE)) {
for(auto it = single_effect.begin(); it != single_effect.end();) {
auto rm = it++;
if((rm->second->code == EFFECT_SET_DEFENSE || rm->second->code == EFFECT_SET_DEFENSE_FINAL || rm->second->code == EFFECT_SET_BASE_DEFENSE)
&& !rm->second->is_flag(EFFECT_FLAG_SINGLE_RANGE))
remove_effect(rm->second);
}
}
if(peffect->code == EFFECT_SET_DEFENSE_FINAL && !peffect->is_flag(EFFECT_FLAG_SINGLE_RANGE)) {
for(auto it = single_effect.begin(); it != single_effect.end();) {
auto rm = it++;
if((rm->second->code == EFFECT_UPDATE_DEFENSE || rm->second->code == EFFECT_SET_DEFENSE
|| rm->second->code == EFFECT_SET_DEFENSE_FINAL) && !rm->second->is_flag(EFFECT_FLAG_SINGLE_RANGE))
remove_effect(rm->second);
}
}
eit = single_effect.emplace(peffect->code, peffect);
} else if (peffect->type & EFFECT_TYPE_EQUIP) {
eit = equip_effect.emplace(peffect->code, peffect);
if (equiping_target)
check_target = { equiping_target };
else
check_target.clear();
} else if(peffect->type & EFFECT_TYPE_TARGET) {
eit = target_effect.emplace(peffect->code, peffect);
if(!effect_target_cards.empty())
check_target = effect_target_cards;
else
check_target.clear();
} else if (peffect->type & EFFECT_TYPE_XMATERIAL) {
eit = xmaterial_effect.emplace(peffect->code, peffect);
if (overlay_target)
check_target = { overlay_target };
else
check_target.clear();
} else if (peffect->type & EFFECT_TYPE_FIELD) {
eit = field_effect.emplace(peffect->code, peffect);
} else
return 0;
if(peffect->code == EFFECT_SELF_TOGRAVE)
pduel->game_field->core.global_flag |= GLOBALFLAG_SELF_TOGRAVE;
else if(peffect->code == EVENT_DETACH_MATERIAL)
pduel->game_field->core.global_flag |= GLOBALFLAG_DETACH_EVENT;
peffect->id = peffect->initial_id = pduel->game_field->infos.field_id++;
peffect->card_type = data.type;
if(get_status(STATUS_INITIALIZING))
peffect->flag[0] |= EFFECT_FLAG_INITIAL;
if (get_status(STATUS_COPYING_EFFECT)) {
peffect->copy_id = pduel->game_field->infos.copy_id;
peffect->reset_flag |= pduel->game_field->core.copy_reset;
peffect->reset_count = pduel->game_field->core.copy_reset_count;
}
effect* reason_effect = pduel->game_field->core.reason_effect;
if(peffect->is_flag(EFFECT_FLAG_COPY_INHERIT) && reason_effect && reason_effect->copy_id) {
peffect->copy_id = reason_effect->copy_id;
peffect->reset_flag |= reason_effect->reset_flag;
if(peffect->reset_count > reason_effect->reset_count)
peffect->reset_count = reason_effect->reset_count;
}
indexer.emplace(peffect, eit);
peffect->handler = this;
if((peffect->type & EFFECT_TYPE_FIELD)) {
if(peffect->in_range(this)
|| (current.controler != PLAYER_NONE && ((peffect->range & LOCATION_HAND) && (peffect->type & EFFECT_TYPE_TRIGGER_O) && !(peffect->code & EVENT_PHASE))))
pduel->game_field->add_effect(peffect);
}
if (current.controler != PLAYER_NONE && !check_target.empty()) {
if(peffect->is_disable_related())
for(auto& target : check_target)
pduel->game_field->add_to_disable_check_list(target);
}
if(peffect->is_flag(EFFECT_FLAG_OATH)) {
pduel->game_field->effects.oath.emplace(peffect, reason_effect);
}
if(peffect->reset_flag & RESET_PHASE) {
pduel->game_field->effects.pheff.insert(peffect);
if(peffect->reset_count == 0)
peffect->reset_count += 1;
}
if(peffect->reset_flag & RESET_CHAIN)
pduel->game_field->effects.cheff.insert(peffect);
if(peffect->is_flag(EFFECT_FLAG_COUNT_LIMIT))
pduel->game_field->effects.rechargeable.insert(peffect);
if(peffect->is_flag(EFFECT_FLAG_CLIENT_HINT)) {
auto message = pduel->new_message(MSG_CARD_HINT);
message->write(get_info_location());
message->write<uint8_t>(CHINT_DESC_ADD);
message->write<uint64_t>(peffect->description);
}
if(peffect->type & EFFECT_TYPE_SINGLE && peffect->code == EFFECT_UPDATE_LEVEL && !peffect->is_flag(EFFECT_FLAG_SINGLE_RANGE)) {
int32_t val = peffect->get_value(this);
if(val > 0) {
pduel->game_field->raise_single_event(this, nullptr, EVENT_LEVEL_UP, peffect, 0, 0, 0, val);
pduel->game_field->process_single_event();
}
}
return peffect->id;
}
void card::remove_effect(effect* peffect) {
auto it = indexer.find(peffect);
if (it == indexer.end())
return;
remove_effect(peffect, it->second);
}
void card::remove_effect(effect* peffect, effect_container::iterator it) {
card_set check_target = { this };
if (peffect->type & EFFECT_TYPE_SINGLE) {
single_effect.erase(it);
} else if (peffect->type & EFFECT_TYPE_EQUIP) {
equip_effect.erase(it);
if (equiping_target)
check_target = { equiping_target };
else
check_target.clear();
} else if(peffect->type & EFFECT_TYPE_TARGET) {
target_effect.erase(it);
if(!effect_target_cards.empty())
check_target = effect_target_cards;
else
check_target.clear();
} else if (peffect->type & EFFECT_TYPE_XMATERIAL) {
xmaterial_effect.erase(it);
if (overlay_target)
check_target = { overlay_target };
else
check_target.clear();
} else if (peffect->type & EFFECT_TYPE_FIELD) {
check_target.clear();
if (peffect->is_available() && peffect->is_disable_related()) {
pduel->game_field->update_disable_check_list(peffect);
}
field_effect.erase(it);
if(peffect->in_range(this)
|| (current.controler != PLAYER_NONE && ((peffect->range & LOCATION_HAND) && (peffect->type & EFFECT_TYPE_TRIGGER_O) && !(peffect->code & EVENT_PHASE))))
pduel->game_field->remove_effect(peffect);
}
if ((current.controler != PLAYER_NONE) && !check_target.empty()) {
if (peffect->is_disable_related())
for(auto& target : check_target)
pduel->game_field->add_to_disable_check_list(target);
}
if (peffect->is_flag(EFFECT_FLAG_INITIAL) && peffect->copy_id && is_status(STATUS_EFFECT_REPLACED)) {
set_status(STATUS_EFFECT_REPLACED, FALSE);
if (!(data.type & TYPE_NORMAL) || (data.type & TYPE_PENDULUM)) {
set_status(STATUS_INITIALIZING, TRUE);
pduel->lua->add_param<LuaParam::CARD>(this);
pduel->lua->call_card_function(this, "initial_effect", 1, 0);
set_status(STATUS_INITIALIZING, FALSE);
}
}
indexer.erase(peffect);
if(peffect->is_flag(EFFECT_FLAG_OATH))
pduel->game_field->effects.oath.erase(peffect);
if(peffect->reset_flag & RESET_PHASE)
pduel->game_field->effects.pheff.erase(peffect);
if(peffect->reset_flag & RESET_CHAIN)
pduel->game_field->effects.cheff.erase(peffect);
if(peffect->is_flag(EFFECT_FLAG_COUNT_LIMIT))
pduel->game_field->effects.rechargeable.erase(peffect);
if(((peffect->code & 0xf0000) == EFFECT_COUNTER_PERMIT) && (peffect->type & EFFECT_TYPE_SINGLE)) {
auto cmit = counters.find(peffect->code & 0xffff);
if(cmit != counters.end()) {
auto message = pduel->new_message(MSG_REMOVE_COUNTER);
message->write<uint16_t>(cmit->first);
message->write<uint8_t>(current.controler);
message->write<uint8_t>(current.location);
message->write<uint8_t>(current.sequence);
message->write<uint16_t>(cmit->second[0] + cmit->second[1]);
counters.erase(cmit);
}
}
if(peffect->is_flag(EFFECT_FLAG_CLIENT_HINT)) {
auto message = pduel->new_message(MSG_CARD_HINT);
message->write(get_info_location());
message->write<uint8_t>(CHINT_DESC_REMOVE);
message->write<uint64_t>(peffect->description);
}
if(peffect->code == EFFECT_UNIQUE_CHECK) {
pduel->game_field->remove_unique_card(this);
unique_pos[0] = unique_pos[1] = 0;
unique_code = 0;
}
pduel->game_field->core.reseted_effects.insert(peffect);
}
int32_t card::copy_effect(uint32_t code, uint32_t reset, uint32_t count) {
if(pduel->read_card(code).type & TYPE_NORMAL)
return -1;
set_status(STATUS_COPYING_EFFECT, TRUE);
uint32_t cr = pduel->game_field->core.copy_reset;
uint8_t crc = pduel->game_field->core.copy_reset_count;
pduel->game_field->core.copy_reset = reset;
pduel->game_field->core.copy_reset_count = count;
pduel->lua->add_param<LuaParam::CARD>(this);
pduel->lua->call_code_function(code, "initial_effect", 1, 0);
++pduel->game_field->infos.copy_id;
set_status(STATUS_COPYING_EFFECT, FALSE);
pduel->game_field->core.copy_reset = cr;
pduel->game_field->core.copy_reset_count = crc;
for(auto& peffect : pduel->uncopy)
pduel->delete_effect(peffect);
pduel->uncopy.clear();
if((data.type & TYPE_MONSTER) && !(data.type & TYPE_EFFECT)) {
effect* peffect = pduel->new_effect();
if(pduel->game_field->core.reason_effect)
peffect->owner = pduel->game_field->core.reason_effect->get_handler();
else
peffect->owner = this;
peffect->handler = this;
peffect->type = EFFECT_TYPE_SINGLE;
peffect->code = EFFECT_ADD_TYPE;
peffect->value = TYPE_EFFECT;
peffect->flag[0] = EFFECT_FLAG_CANNOT_DISABLE;
peffect->reset_flag = reset;
peffect->reset_count = count;
this->add_effect(peffect);
}
return pduel->game_field->infos.copy_id - 1;
}
int32_t card::replace_effect(uint32_t code, uint32_t reset, uint32_t count, bool recreating) {
if(pduel->read_card(code).type & TYPE_NORMAL)
return -1;
if(is_status(STATUS_EFFECT_REPLACED))
set_status(STATUS_EFFECT_REPLACED, FALSE);
for(auto i = indexer.begin(); i != indexer.end();) {
auto rm = i++;
effect* peffect = rm->first;
auto it = rm->second;
if (peffect->is_flag(EFFECT_FLAG_INITIAL | EFFECT_FLAG_COPY_INHERIT))
remove_effect(peffect, it);
}
uint32_t cr = pduel->game_field->core.copy_reset;
uint8_t crc = pduel->game_field->core.copy_reset_count;
pduel->game_field->core.copy_reset = reset;
pduel->game_field->core.copy_reset_count = count;
set_status(STATUS_INITIALIZING, TRUE);
if(!recreating)
set_status(STATUS_COPYING_EFFECT, TRUE);
pduel->lua->load_card_script(code);
pduel->lua->add_param<LuaParam::CARD>(this);
pduel->lua->call_code_function(code, "initial_effect", 1, 0);
set_status(STATUS_INITIALIZING | STATUS_COPYING_EFFECT, FALSE);
++pduel->game_field->infos.copy_id;
pduel->game_field->core.copy_reset = cr;
pduel->game_field->core.copy_reset_count = crc;
set_status(STATUS_EFFECT_REPLACED, TRUE);
for(auto& peffect : pduel->uncopy)
pduel->delete_effect(peffect);
pduel->uncopy.clear();
if((data.type & TYPE_MONSTER) && !(data.type & TYPE_EFFECT)) {
effect* peffect = pduel->new_effect();
if(pduel->game_field->core.reason_effect)
peffect->owner = pduel->game_field->core.reason_effect->get_handler();
else
peffect->owner = this;
peffect->handler = this;
peffect->type = EFFECT_TYPE_SINGLE;
peffect->code = EFFECT_ADD_TYPE;
peffect->value = TYPE_EFFECT;
peffect->flag[0] = EFFECT_FLAG_CANNOT_DISABLE;
peffect->reset_flag = reset;
peffect->reset_count = count;
this->add_effect(peffect);
}
return pduel->game_field->infos.copy_id - 1;
}
void card::reset(uint32_t id, uint32_t reset_type) {
if (reset_type != RESET_EVENT && reset_type != RESET_PHASE && reset_type != RESET_CODE && reset_type != RESET_COPY && reset_type != RESET_CARD)
return;
if (reset_type == RESET_EVENT) {
for (auto rit = relations.begin(); rit != relations.end();) {
auto rrm = rit++;
if (rrm->second & 0xffff0000 & id)
relations.erase(rrm);
}
if(id & (RESET_TODECK | RESET_TOHAND | RESET_TOGRAVE | RESET_REMOVE | RESET_TEMP_REMOVE
| RESET_OVERLAY | RESET_MSCHANGE))
clear_relate_effect();
if(id & (RESET_TODECK | RESET_TOHAND | RESET_TOGRAVE | RESET_REMOVE | RESET_TEMP_REMOVE
| RESET_OVERLAY | RESET_MSCHANGE | RESET_LEAVE | RESET_TOFIELD)) {
indestructable_effects.clear();
announced_cards.clear();
attacked_cards.clear();
attack_announce_count = 0;
announce_count = 0;
attacked_count = 0;
attack_all_target = TRUE;
}
if(id & (RESET_TODECK | RESET_TOHAND | RESET_TOGRAVE | RESET_REMOVE | RESET_TEMP_REMOVE
| RESET_OVERLAY | RESET_MSCHANGE | RESET_LEAVE | RESET_TOFIELD | RESET_TURN_SET)) {
battled_cards.clear();
reset_effect_count();
auto pr = field_effect.equal_range(EFFECT_DISABLE_FIELD);
for(; pr.first != pr.second; ++pr.first)
pr.first->second->value = 0;
}
if(id & (RESET_TODECK | RESET_TOHAND | RESET_TOGRAVE | RESET_REMOVE | RESET_TEMP_REMOVE
| RESET_OVERLAY | RESET_MSCHANGE | RESET_TOFIELD | RESET_TURN_SET)) {
counters.clear();
}
if(id & (RESET_TODECK | RESET_TOHAND | RESET_TOGRAVE | RESET_REMOVE | RESET_TEMP_REMOVE
| RESET_LEAVE | RESET_TOFIELD | RESET_TURN_SET | RESET_CONTROL)) {
auto pr = field_effect.equal_range(EFFECT_USE_EXTRA_MZONE);
for(; pr.first != pr.second; ++pr.first)
pr.first->second->value = pr.first->second->value & 0xffff;
pr = field_effect.equal_range(EFFECT_USE_EXTRA_SZONE);
for(; pr.first != pr.second; ++pr.first)
pr.first->second->value = pr.first->second->value & 0xffff;
}
if(id & RESET_TOFIELD) {
pre_equip_target = nullptr;
}
if(id & RESET_DISABLE) {
for(auto cmit = counters.begin(); cmit != counters.end();) {
auto rm = cmit++;
if(rm->second[1] > 0) {
auto message = pduel->new_message(MSG_REMOVE_COUNTER);
message->write<uint16_t>(rm->first);
message->write<uint8_t>(current.controler);
message->write<uint8_t>(current.location);
message->write<uint8_t>(current.sequence);
message->write<uint16_t>(rm->second[1]);
rm->second[1] = 0;
if(rm->second[0] == 0)
counters.erase(rm);
}
}
}
if(id & RESET_TURN_SET) {
effect* peffect = std::get<effect*>(refresh_control_status());
if(peffect && (!(peffect->type & EFFECT_TYPE_SINGLE) || peffect->condition)) {
effect* new_effect = pduel->new_effect();
new_effect->id = peffect->id;
new_effect->owner = this;
new_effect->handler = this;
new_effect->type = EFFECT_TYPE_SINGLE;
new_effect->code = EFFECT_SET_CONTROL;
new_effect->value = current.controler;
new_effect->flag[0] = EFFECT_FLAG_CANNOT_DISABLE;
new_effect->reset_flag = RESET_EVENT | 0xec0000;
this->add_effect(new_effect);
}
}
}
for (auto i = indexer.begin(); i != indexer.end();) {
auto rm = i++;
effect* peffect = rm->first;
auto it = rm->second;
if (peffect->reset(id, reset_type))
remove_effect(peffect, it);
}
}
void card::reset_effect_count() {
for (auto& i : indexer) {
effect* peffect = i.first;
if (peffect->is_flag(EFFECT_FLAG_COUNT_LIMIT))
peffect->recharge();
}
}
// refresh STATUS_DISABLED based on EFFECT_DISABLE and EFFECT_CANNOT_DISABLE
// refresh STATUS_FORBIDDEN based on EFFECT_FORBIDDEN
void card::refresh_disable_status() {
filter_immune_effect();
// forbidden
if (is_affected_by_effect(EFFECT_FORBIDDEN))
set_status(STATUS_FORBIDDEN, TRUE);
else
set_status(STATUS_FORBIDDEN, FALSE);
// disabled
if (!is_affected_by_effect(EFFECT_CANNOT_DISABLE) && is_affected_by_effect(EFFECT_DISABLE))
set_status(STATUS_DISABLED, TRUE);
else
set_status(STATUS_DISABLED, FALSE);
}
std::pair<uint8_t, effect*> card::refresh_control_status() {
uint8_t final = owner;
effect* ceffect = nullptr;
uint32_t last_id = 0;
if(pduel->game_field->core.remove_brainwashing && is_affected_by_effect(EFFECT_REMOVE_BRAINWASHING))
last_id = pduel->game_field->core.last_control_changed_id;
effect_set eset;
filter_effect(EFFECT_SET_CONTROL, &eset);
if(eset.size()) {
effect* peffect = eset.back();
if(peffect->id >= last_id) {
final = (uint8_t)peffect->get_value(this);
ceffect = peffect;
}
}
return std::make_pair(final, ceffect);
}
void card::count_turn(uint16_t ct) {
turn_counter = ct;
auto message = pduel->new_message(MSG_CARD_HINT);
message->write(get_info_location());
message->write<uint8_t>(CHINT_TURN);
message->write<uint64_t>(ct);
}
void card::create_relation(card* target, uint32_t reset) {
relations.insert(std::make_pair(target, reset));
}
int32_t card::is_has_relation(card* target) const {
return relations.find(target) != relations.end();
}
void card::release_relation(card* target) {
relations.erase(target);
}
void card::create_relation(const chain& ch) {
relate_effect.emplace(ch.triggering_effect, ch.chain_id);
}
int32_t card::is_has_relation(const chain& ch) const {
return relate_effect.find(std::make_pair(ch.triggering_effect, ch.chain_id)) != relate_effect.end();
}
void card::release_relation(const chain& ch) {
relate_effect.erase(std::make_pair(ch.triggering_effect, ch.chain_id));
}
void card::clear_relate_effect() {
relate_effect.clear();
}
void card::create_relation(effect* peffect) {
for(auto it = pduel->game_field->core.current_chain.rbegin(); it != pduel->game_field->core.current_chain.rend(); ++it) {
if(it->triggering_effect == peffect) {
create_relation(*it);
return;
}
}
relate_effect.emplace(peffect, (uint16_t)0);
}
int32_t card::is_has_relation(effect* peffect) const {
for(auto& it : relate_effect) {
if(it.first == peffect)
return TRUE;
}
return FALSE;
}
void card::release_relation(effect* peffect) {
for(auto it = pduel->game_field->core.current_chain.rbegin(); it != pduel->game_field->core.current_chain.rend(); ++it) {
if(it->triggering_effect == peffect) {
release_relation(*it);
return;
}
}
relate_effect.erase(std::make_pair(peffect, (uint16_t)0));
}
int32_t card::leave_field_redirect(uint32_t reason) {
effect_set es;
uint32_t redirect;
uint32_t redirects = 0;
if(data.type & TYPE_TOKEN)
return 0;
filter_effect(EFFECT_LEAVE_FIELD_REDIRECT, &es);
for(const auto& peff : es) {
pduel->lua->add_param<LuaParam::INT>(reason);
redirect = peff->get_value(this, 1);
if((redirect & LOCATION_HAND) && !is_affected_by_effect(EFFECT_CANNOT_TO_HAND) && pduel->game_field->is_player_can_send_to_hand(peff->get_handler_player(), this))
redirects |= redirect;
else if((redirect & LOCATION_DECK) && !is_affected_by_effect(EFFECT_CANNOT_TO_DECK) && pduel->game_field->is_player_can_send_to_deck(peff->get_handler_player(), this))
redirects |= redirect;
else if((redirect & LOCATION_REMOVED) && !is_affected_by_effect(EFFECT_CANNOT_REMOVE) && pduel->game_field->is_player_can_remove(peff->get_handler_player(), this, REASON_EFFECT))
redirects |= redirect;
}
if(redirects & LOCATION_REMOVED)
return LOCATION_REMOVED;
// the ruling for the priority of the following redirects can't be confirmed for now
if(redirects & LOCATION_DECK) {
if((redirects & LOCATION_DECKBOT) == LOCATION_DECKBOT)
return LOCATION_DECKBOT;
if((redirects & LOCATION_DECKSHF) == LOCATION_DECKSHF)
return LOCATION_DECKSHF;
return LOCATION_DECK;
}
if(redirects & LOCATION_HAND)
return LOCATION_HAND;
return 0;
}
int32_t card::destination_redirect(uint8_t destination, uint32_t reason) {
effect_set es;
uint32_t redirect;
if(data.type & TYPE_TOKEN)
return 0;
if(destination == LOCATION_HAND)
filter_effect(EFFECT_TO_HAND_REDIRECT, &es);
else if(destination == LOCATION_DECK)
filter_effect(EFFECT_TO_DECK_REDIRECT, &es);
else if(destination == LOCATION_GRAVE)
filter_effect(EFFECT_TO_GRAVE_REDIRECT, &es);
else if(destination == LOCATION_REMOVED)
filter_effect(EFFECT_REMOVE_REDIRECT, &es);
else
return 0;
for(const auto& peff : es) {
pduel->lua->add_param<LuaParam::INT>(reason);
redirect = peff->get_value(this, 1);
if((redirect & LOCATION_HAND) && !is_affected_by_effect(EFFECT_CANNOT_TO_HAND) && pduel->game_field->is_player_can_send_to_hand(peff->get_handler_player(), this))
return redirect;
if((redirect & LOCATION_DECK) && !is_affected_by_effect(EFFECT_CANNOT_TO_DECK) && pduel->game_field->is_player_can_send_to_deck(peff->get_handler_player(), this))
return redirect;
if((redirect & LOCATION_REMOVED) && !is_affected_by_effect(EFFECT_CANNOT_REMOVE) && pduel->game_field->is_player_can_remove(peff->get_handler_player(), this, REASON_EFFECT))
return redirect;
if((redirect & LOCATION_GRAVE) && !is_affected_by_effect(EFFECT_CANNOT_TO_GRAVE) && pduel->game_field->is_player_can_send_to_grave(peff->get_handler_player(), this))
return redirect;
}
return 0;
}
// cmit->second[0]: permanent
// cmit->second[1]: reset while negated
int32_t card::add_counter(uint8_t playerid, uint16_t countertype, uint16_t count, bool singly) {
if(!is_can_add_counter(playerid, countertype, count, singly, 0))
return FALSE;
uint16_t cttype = countertype & ~COUNTER_NEED_ENABLE;
auto pr = counters.emplace(cttype, counter_map::mapped_type());
auto cmit = pr.first;
if(pr.second) {
cmit->second[0] = 0;
cmit->second[1] = 0;
}
uint16_t pcount = count;
if(singly) {
effect_set eset;
uint32_t limit = UINT16_MAX + 1;
filter_effect(EFFECT_COUNTER_LIMIT + cttype, &eset);
for(const auto& peffect : eset)
limit = std::min<uint32_t>(static_cast<uint16_t>(peffect->get_value()), limit);
if(limit != (UINT16_MAX + 1)) {
uint16_t mcount = static_cast<uint16_t>(limit) - get_counter(cttype);
if(pcount > mcount)
pcount = mcount;
}
}
if((countertype & COUNTER_WITHOUT_PERMIT) && !(countertype & COUNTER_NEED_ENABLE))
cmit->second[0] += pcount;
else
cmit->second[1] += pcount;
auto message = pduel->new_message(MSG_ADD_COUNTER);
message->write<uint16_t>(cttype);
message->write<uint8_t>(current.controler);
message->write<uint8_t>(current.location);
message->write<uint8_t>(current.sequence);
message->write<uint16_t>(pcount);
pduel->game_field->raise_single_event(this, nullptr, EVENT_ADD_COUNTER + countertype, pduel->game_field->core.reason_effect, REASON_EFFECT, playerid, playerid, pcount);
pduel->game_field->process_single_event();
return TRUE;
}
int32_t card::remove_counter(uint16_t countertype, uint16_t count) {
auto cmit = counters.find(countertype);
if(cmit == counters.end())
return FALSE;
if(cmit->second[1] <= count) {
uint16_t remains = count;
remains -= cmit->second[1];
cmit->second[1] = 0;
if(cmit->second[0] <= remains)
counters.erase(cmit);
else cmit->second[0] -= remains;
} else {
cmit->second[1] -= count;
}
auto message = pduel->new_message(MSG_REMOVE_COUNTER);
message->write<uint16_t>(countertype);
message->write<uint8_t>(current.controler);
message->write<uint8_t>(current.location);
message->write<uint8_t>(current.sequence);
message->write<uint16_t>(count);
return TRUE;
}
int32_t card::is_can_add_counter(uint8_t playerid, uint16_t countertype, uint16_t count, bool singly, uint32_t loc) {
effect_set eset;
if(count > 0) {
if(!pduel->game_field->is_player_can_place_counter(playerid, this, countertype, count))
return FALSE;
if(!loc && (!(current.location & LOCATION_ONFIELD) || !is_position(POS_FACEUP)))
return FALSE;
if((countertype & COUNTER_NEED_ENABLE) && is_status(STATUS_DISABLED))
return FALSE;
}
uint32_t check = countertype & COUNTER_WITHOUT_PERMIT;
if(!check) {
filter_effect(EFFECT_COUNTER_PERMIT + (countertype & 0xffff), &eset);
for(const auto& peffect : eset) {
uint32_t prange = peffect->get_value();
if(loc)
check = loc & prange;
else if(current.location & LOCATION_ONFIELD) {
uint32_t filter = TRUE;
if(peffect->target) {
pduel->lua->add_param<LuaParam::EFFECT>(peffect);
pduel->lua->add_param<LuaParam::CARD>(this);
filter = pduel->lua->check_condition(peffect->target, 2);
}
check = current.is_location(prange) && is_position(POS_FACEUP) && filter;
} else
check = TRUE;
if(check)
break;
}
eset.clear();
}
if(!check)
return FALSE;
uint16_t cttype = countertype & ~COUNTER_NEED_ENABLE;
uint32_t limit = UINT16_MAX + 1;
uint32_t cur = 0;
auto cmit = counters.find(cttype);
if(cmit != counters.end())
cur = cmit->second[0] + cmit->second[1];
filter_effect(EFFECT_COUNTER_LIMIT + cttype, &eset);
for(const auto& peffect : eset)
limit = std::min<uint32_t>(static_cast<uint16_t>(peffect->get_value()), limit);
if(singly)
count = 1;
if((limit != (UINT16_MAX + 1)) && (cur + count > limit))
return FALSE;
return TRUE;
}
int32_t card::get_counter(uint16_t countertype) {
auto cmit = counters.find(countertype);
if(cmit == counters.end())
return 0;
return cmit->second[0] + cmit->second[1];
}
void card::set_material(card_set materials) {
material_cards = std::move(materials);
for(auto& pcard : material_cards)
pcard->current.reason_card = this;
effect_set eset;
filter_effect(EFFECT_MATERIAL_CHECK, &eset);
for(const auto& peffect : eset) {
peffect->get_value(this);
}
}
void card::add_card_target(card* pcard) {
effect_target_cards.insert(pcard);
pcard->effect_target_owner.insert(this);
for(auto& it : target_effect) {
if(it.second->is_disable_related())
pduel->game_field->add_to_disable_check_list(pcard);
}
auto message = pduel->new_message(MSG_CARD_TARGET);
message->write(get_info_location());
message->write(pcard->get_info_location());
}
void card::cancel_card_target(card* pcard) {
auto cit = effect_target_cards.find(pcard);
if(cit != effect_target_cards.end()) {
effect_target_cards.erase(cit);
pcard->effect_target_owner.erase(this);
for(auto& it : target_effect) {
if(it.second->is_disable_related())
pduel->game_field->add_to_disable_check_list(pcard);
}
auto message = pduel->new_message(MSG_CANCEL_TARGET);
message->write(get_info_location());
message->write(pcard->get_info_location());
}
}
void card::clear_card_target() {
for(auto& pcard : effect_target_owner) {
pcard->effect_target_cards.erase(this);
for(auto& it : pcard->target_effect) {
if(it.second->is_disable_related())
pduel->game_field->add_to_disable_check_list(this);
}
}
for(auto& pcard : effect_target_cards) {
pcard->effect_target_owner.erase(this);
for(auto& it : target_effect) {
if(it.second->is_disable_related())
pduel->game_field->add_to_disable_check_list(pcard);
}
for(auto it = pcard->single_effect.begin(); it != pcard->single_effect.end();) {
auto rm = it++;
effect* peffect = rm->second;
if((peffect->owner == this) && peffect->is_flag(EFFECT_FLAG_OWNER_RELATE))
pcard->remove_effect(peffect, rm);
}
}
effect_target_owner.clear();
effect_target_cards.clear();
}
void card::filter_effect(int32_t code, effect_set* eset, uint8_t sort) {
effect* peffect;
auto rg = single_effect.equal_range(code);
for(auto eit = rg.first; eit != rg.second;) {
peffect = eit->second;
++eit;
if(peffect->is_available() && (!peffect->is_flag(EFFECT_FLAG_SINGLE_RANGE) || is_affect_by_effect(peffect)))
eset->push_back(peffect);
}
for (auto& pcard : equiping_cards) {
rg = pcard->equip_effect.equal_range(code);
for(auto eit = rg.first; eit != rg.second;) {
peffect = eit->second;
++eit;
if (peffect->is_available() && is_affect_by_effect(peffect))
eset->push_back(peffect);
}
}
for(auto& pcard : effect_target_owner) {
rg = pcard->target_effect.equal_range(code);
for(auto eit = rg.first; eit != rg.second;) {
peffect = eit->second;
++eit;
if(peffect->is_available() && peffect->is_target(this) && is_affect_by_effect(peffect))
eset->push_back(peffect);
}
}
for (auto& pcard : xyz_materials) {
rg = pcard->xmaterial_effect.equal_range(code);
for(auto eit = rg.first; eit != rg.second;) {
peffect = eit->second;
++eit;
if (peffect->type & EFFECT_TYPE_FIELD)
continue;
if (peffect->is_available() && is_affect_by_effect(peffect))
eset->push_back(peffect);
}
}
rg = pduel->game_field->effects.aura_effect.equal_range(code);
for(auto eit = rg.first; eit != rg.second;) {
peffect = eit->second;
++eit;
if (!peffect->is_flag(EFFECT_FLAG_PLAYER_TARGET) && peffect->is_available()
&& peffect->is_target(this) && is_affect_by_effect(peffect))
eset->push_back(peffect);
}
if(sort)
std::sort(eset->begin(), eset->end(), effect_sort_id);
}
void card::filter_single_effect(int32_t code, effect_set* eset, uint8_t sort) {
effect* peffect;
auto rg = single_effect.equal_range(code);
for (; rg.first != rg.second; ++rg.first) {
peffect = rg.first->second;
if (peffect->is_available() && !peffect->is_flag(EFFECT_FLAG_SINGLE_RANGE))
eset->push_back(peffect);
}
if(sort)
std::sort(eset->begin(), eset->end(), effect_sort_id);
}
void card::filter_single_continuous_effect(int32_t code, effect_set* eset, uint8_t sort) {
auto rg = single_effect.equal_range(code);
for (; rg.first != rg.second; ++rg.first)
eset->push_back(rg.first->second);
for (auto& pcard : equiping_cards) {
rg = pcard->equip_effect.equal_range(code);
for (; rg.first != rg.second; ++rg.first)
eset->push_back(rg.first->second);
}
for(auto& pcard : effect_target_owner) {
rg = pcard->target_effect.equal_range(code);
for(; rg.first != rg.second; ++rg.first) {
effect* peffect = rg.first->second;
if(peffect->is_target(pcard))
eset->push_back(peffect);
}
}
for (auto& pcard : xyz_materials) {
rg = pcard->xmaterial_effect.equal_range(code);
for (; rg.first != rg.second; ++rg.first) {
effect* peffect = rg.first->second;
if (peffect->type & EFFECT_TYPE_FIELD)
continue;
eset->push_back(peffect);
}
}
if(sort)
std::sort(eset->begin(), eset->end(), effect_sort_id);
}
// refresh this->immune_effect
void card::filter_immune_effect() {
effect* peffect;
immune_effect.clear();
auto rg = single_effect.equal_range(EFFECT_IMMUNE_EFFECT);
for (; rg.first != rg.second; ++rg.first) {
immune_effect.push_back(rg.first->second);
}
for (auto& pcard : equiping_cards) {
rg = pcard->equip_effect.equal_range(EFFECT_IMMUNE_EFFECT);
for (; rg.first != rg.second; ++rg.first) {
immune_effect.push_back(rg.first->second);
}
}
for (auto& pcard : effect_target_owner) {
rg = pcard->target_effect.equal_range(EFFECT_IMMUNE_EFFECT);
for (; rg.first != rg.second; ++rg.first) {
peffect = rg.first->second;
if(peffect->is_target(this))
immune_effect.push_back(peffect);
}
}
for (auto& pcard : xyz_materials) {
rg = pcard->xmaterial_effect.equal_range(EFFECT_IMMUNE_EFFECT);
for (; rg.first != rg.second; ++rg.first) {
peffect = rg.first->second;
if (peffect->type & EFFECT_TYPE_FIELD)
continue;
immune_effect.push_back(peffect);
}
}
rg = pduel->game_field->effects.aura_effect.equal_range(EFFECT_IMMUNE_EFFECT);
for (; rg.first != rg.second; ++rg.first) {
peffect = rg.first->second;
if (peffect->is_target(this))
immune_effect.push_back(peffect);
}
std::sort(immune_effect.begin(), immune_effect.end(), effect_sort_id);
}
// for all disable-related peffect of this,
// 1. put all cards in the target of peffect into effects.disable_check_set, effects.disable_check_list
// 2. add equiping_target of peffect into effects.disable_check_set, effects.disable_check_list
void card::filter_disable_related_cards() {
for (auto& it : indexer) {
effect* peffect = it.first;
if (peffect->is_disable_related()) {
if (peffect->type & EFFECT_TYPE_FIELD)
pduel->game_field->update_disable_check_list(peffect);
else if ((peffect->type & EFFECT_TYPE_EQUIP) && equiping_target)
pduel->game_field->add_to_disable_check_list(equiping_target);
else if((peffect->type & EFFECT_TYPE_TARGET) && !effect_target_cards.empty()) {
for(auto& target : effect_target_cards)
pduel->game_field->add_to_disable_check_list(target);
} else if((peffect->type & EFFECT_TYPE_XMATERIAL) && overlay_target)
pduel->game_field->add_to_disable_check_list(overlay_target);
}
}
}
// put all summon procedures except ordinay summon in peset (see is_can_be_summoned())
// return value:
// -2 = this has a EFFECT_LIMIT_SUMMON_PROC, 0 available
// -1 = this has a EFFECT_LIMIT_SUMMON_PROC, at least 1 available
// 0 = no EFFECT_LIMIT_SUMMON_PROC, and ordinary summon ia not available
// 1 = no EFFECT_LIMIT_SUMMON_PROC, and ordinary summon ia available
int32_t card::filter_summon_procedure(uint8_t playerid, effect_set* peset, uint8_t ignore_count, uint8_t min_tribute, uint32_t zone) {
effect_set eset;
filter_effect(EFFECT_LIMIT_SUMMON_PROC, &eset);
if(eset.size()) {
for(const auto& peffect : eset) {
if(check_summon_procedure(peffect, playerid, ignore_count, min_tribute, zone))
peset->push_back(peffect);
}
if(peset->size())
return -1;
return -2;
}
eset.clear();
filter_effect(EFFECT_SUMMON_PROC, &eset);
for(const auto& peffect : eset) {
if(check_summon_procedure(peffect, playerid, ignore_count, min_tribute, zone))
peset->push_back(peffect);
}
// ordinary summon
if(!pduel->game_field->is_player_can_summon(SUMMON_TYPE_NORMAL, playerid, this, playerid))
return FALSE;
if(pduel->game_field->check_unique_onfield(this, playerid, LOCATION_MZONE))
return FALSE;
int32_t rcount = get_summon_tribute_count();
uint8_t min = rcount & 0xffff;
uint8_t max = (rcount >> 16) & 0xffff;
if(!pduel->game_field->is_player_can_summon(SUMMON_TYPE_ADVANCE, playerid, this, playerid))
max = 0;
if(min < min_tribute)
min = min_tribute;
if(max < min)
return FALSE;
if(!ignore_count && !pduel->game_field->core.extra_summon[playerid]
&& pduel->game_field->core.summon_count[playerid] >= pduel->game_field->get_summon_count_limit(playerid)) {
eset.clear();
filter_effect(EFFECT_EXTRA_SUMMON_COUNT, &eset);
for(const auto& peffect : eset) {
std::vector<lua_Integer> retval;
peffect->get_value(this, 0, retval);
uint8_t new_min = retval.size() > 0 ? static_cast<uint8_t>(retval[0]) : 0;
uint32_t new_zone = retval.size() > 1 ? static_cast<uint32_t>(retval[1]) : 0x1f;
uint32_t releasable = 0xff00ffu;
if(retval.size() > 2) {
if(retval[2] < 0)
releasable += static_cast<int32_t>(retval[2]);
else
releasable = static_cast<uint32_t>(retval[2]);
}
if(new_min < min)
new_min = min;
new_zone &= zone;
if(pduel->game_field->check_tribute(this, new_min, max, nullptr, current.controler, new_zone, releasable))
return TRUE;
}
} else
return pduel->game_field->check_tribute(this, min, max, nullptr, current.controler, zone);
return FALSE;
}
int32_t card::check_summon_procedure(effect* peffect, uint8_t playerid, uint8_t ignore_count, uint8_t min_tribute, uint32_t zone) {
if(!peffect->check_count_limit(playerid))
return FALSE;
uint8_t toplayer = playerid;
if(peffect->is_flag(EFFECT_FLAG_SPSUM_PARAM)) {
if(peffect->o_range)
toplayer = 1 - playerid;
}
if(!pduel->game_field->is_player_can_summon(peffect->get_value(this), playerid, this, toplayer))
return FALSE;
if(pduel->game_field->check_unique_onfield(this, toplayer, LOCATION_MZONE))
return FALSE;
// the script will check min_tribute, Duel.CheckTribute()
if(!ignore_count && !pduel->game_field->core.extra_summon[playerid]
&& pduel->game_field->core.summon_count[playerid] >= pduel->game_field->get_summon_count_limit(playerid)) {
effect_set eset;
filter_effect(EFFECT_EXTRA_SUMMON_COUNT, &eset);
for(const auto& peff : eset) {
std::vector<lua_Integer> retval;
peff->get_value(this, 0, retval);
uint8_t new_min_tribute = retval.size() > 0 ? static_cast<uint8_t>(retval[0]) : 0;
uint32_t new_zone = retval.size() > 1 ? static_cast<uint32_t>(retval[1]) : 0x1f001f;
uint32_t releasable = 0xff00ffu;
if(retval.size() > 2) {
if(retval[2] < 0)
releasable += static_cast<int32_t>(retval[2]);
else
releasable = static_cast<uint32_t>(retval[2]);
}
if(new_min_tribute < min_tribute)
new_min_tribute = min_tribute;
if (peffect->is_flag(EFFECT_FLAG_SPSUM_PARAM) && peffect->o_range)
new_zone = (new_zone >> 16) | (new_zone & 0xffffu << 16);
new_zone &= zone;
if(is_summonable(peffect, new_min_tribute, new_zone, releasable, peffect))
return TRUE;
}
} else
return is_summonable(peffect, min_tribute, zone);
return FALSE;
}
// put all set procedures except ordinay set in peset (see is_can_be_summoned())
int32_t card::filter_set_procedure(uint8_t playerid, effect_set* peset, uint8_t ignore_count, uint8_t min_tribute, uint32_t zone) {
effect_set eset;
filter_effect(EFFECT_LIMIT_SET_PROC, &eset);
if(eset.size()) {
for(const auto& peffect : eset) {
if(check_set_procedure(peffect, playerid, ignore_count, min_tribute, zone))
peset->push_back(peffect);
}
if(peset->size())
return -1;
return -2;
}
eset.clear();
filter_effect(EFFECT_SET_PROC, &eset);
for(const auto& peffect : eset) {
if(check_set_procedure(peffect, playerid, ignore_count, min_tribute, zone))
peset->push_back(peffect);
}
if(!pduel->game_field->is_player_can_mset(SUMMON_TYPE_NORMAL, playerid, this, playerid))
return FALSE;
int32_t rcount = get_set_tribute_count();
int32_t min = rcount & 0xffff;
int32_t max = (rcount >> 16) & 0xffff;
if(!pduel->game_field->is_player_can_mset(SUMMON_TYPE_ADVANCE, playerid, this, playerid))
max = 0;
if(min < min_tribute)
min = min_tribute;
if(max < min)
return FALSE;
if(!ignore_count && !pduel->game_field->core.extra_summon[playerid]
&& pduel->game_field->core.summon_count[playerid] >= pduel->game_field->get_summon_count_limit(playerid)) {
eset.clear();
filter_effect(EFFECT_EXTRA_SET_COUNT, &eset);
for(const auto& peff : eset) {
std::vector<lua_Integer> retval;
peff->get_value(this, 0, retval);
uint8_t new_min = retval.size() > 0 ? static_cast<uint8_t>(retval[0]) : 0;
uint32_t new_zone = retval.size() > 1 ? static_cast<uint32_t>(retval[1]) : 0x1f;
uint32_t releasable = 0xff00ffu;
if(retval.size() > 2) {
if(retval[2] < 0)
releasable += static_cast<int32_t>(retval[2]);
else
releasable = static_cast<uint32_t>(retval[2]);
}
if(new_min < min)
new_min = min;
new_zone &= zone;
if(pduel->game_field->check_tribute(this, new_min, max, nullptr, current.controler, new_zone, releasable, POS_FACEDOWN_DEFENSE))
return TRUE;
}
} else
return pduel->game_field->check_tribute(this, min, max, nullptr, current.controler, zone, 0xff00ff, POS_FACEDOWN_DEFENSE);
return FALSE;
}
int32_t card::check_set_procedure(effect* peffect, uint8_t playerid, uint8_t ignore_count, uint8_t min_tribute, uint32_t zone) {
if(!peffect->check_count_limit(playerid))
return FALSE;
uint8_t toplayer = playerid;
if(peffect->is_flag(EFFECT_FLAG_SPSUM_PARAM)) {
if(peffect->o_range)
toplayer = 1 - playerid;
}
if(!pduel->game_field->is_player_can_mset(peffect->get_value(this), playerid, this, toplayer))
return FALSE;
if(!ignore_count && !pduel->game_field->core.extra_summon[playerid]
&& pduel->game_field->core.summon_count[playerid] >= pduel->game_field->get_summon_count_limit(playerid)) {
effect_set eset;
filter_effect(EFFECT_EXTRA_SET_COUNT, &eset);
for(const auto& peff : eset) {
std::vector<lua_Integer> retval;
peff->get_value(this, 0, retval);
uint8_t new_min_tribute = retval.size() > 0 ? static_cast<uint8_t>(retval[0]) : 0;
uint32_t new_zone = retval.size() > 1 ? static_cast<uint32_t>(retval[1]) : 0x1f001f;
uint32_t releasable = 0xff00ffu;
if(retval.size() > 2) {
if(retval[2] < 0)
releasable += static_cast<int32_t>(retval[2]);
else
releasable = static_cast<uint32_t>(retval[2]);
}
if (peffect->is_flag(EFFECT_FLAG_SPSUM_PARAM) && peffect->o_range)
new_zone = (new_zone >> 16) | (new_zone & 0xffffu << 16);
if(new_min_tribute < (int32_t)min_tribute)
new_min_tribute = min_tribute;
new_zone &= zone;
if(is_summonable(peffect, new_min_tribute, new_zone, releasable, peff))
return TRUE;
}
} else
return is_summonable(peffect, min_tribute, zone);
return FALSE;
}
void card::filter_spsummon_procedure(uint8_t playerid, effect_set* peset, uint32_t summon_type) {
auto pr = field_effect.equal_range(EFFECT_SPSUMMON_PROC);
uint8_t toplayer;
uint8_t topos;
for(auto eit = pr.first; eit != pr.second;) {
effect* peffect = eit->second;
++eit;
if(peffect->is_flag(EFFECT_FLAG_SPSUM_PARAM)) {
topos = (uint8_t)peffect->s_range;
if(peffect->o_range == 0)
toplayer = playerid;
else
toplayer = 1 - playerid;
} else {
topos = POS_FACEUP;
toplayer = playerid;
}
if(!peffect->is_available() || !peffect->check_count_limit(playerid))
continue;
if(is_spsummonable(peffect)
&& ((topos & POS_FACEDOWN) || !pduel->game_field->check_unique_onfield(this, toplayer, LOCATION_MZONE))) {
effect* sumeffect = pduel->game_field->core.reason_effect;
if(!sumeffect)
sumeffect = peffect;
std::vector<lua_Integer> retval;
peffect->get_value(this, 0, retval);
uint32_t sumtype = retval.size() > 0 ? static_cast<uint32_t>(retval[0]) : 0;
uint32_t zone = retval.size() > 1 ? static_cast<uint32_t>(retval[1]) : 0xff;
if(zone != 0xff && pduel->game_field->get_useable_count(this, toplayer, LOCATION_MZONE, playerid, LOCATION_REASON_TOFIELD, zone, nullptr) <= 0)
continue;
if(summon_type != 0 && summon_type != sumtype)
continue;
if(!pduel->game_field->is_player_can_spsummon(sumeffect, sumtype, topos, playerid, toplayer, this, peffect))
continue;
peset->push_back(peffect);
}
}
}
void card::filter_spsummon_procedure_g(uint8_t playerid, effect_set* peset) {
auto pr = field_effect.equal_range(EFFECT_SPSUMMON_PROC_G);
for(auto eit = pr.first; eit != pr.second;) {
effect* peffect = eit->second;
++eit;
if(!peffect->is_available() || !peffect->check_count_limit(playerid))
continue;
if(current.controler != playerid && !peffect->is_flag(EFFECT_FLAG_BOTH_SIDE))
continue;
effect* oreason = pduel->game_field->core.reason_effect;
uint8_t op = pduel->game_field->core.reason_player;
pduel->game_field->core.reason_effect = peffect;
pduel->game_field->core.reason_player = this->current.controler;
pduel->game_field->save_lp_cost();
pduel->lua->add_param<LuaParam::EFFECT>(peffect);
pduel->lua->add_param<LuaParam::CARD>(this);
if(pduel->lua->check_condition(peffect->condition, 2))
peset->push_back(peffect);
pduel->game_field->restore_lp_cost();
pduel->game_field->core.reason_effect = oreason;
pduel->game_field->core.reason_player = op;
}
}
// return: an effect with code which affects this or 0
effect* card::is_affected_by_effect(int32_t code) {
effect* peffect;
auto rg = single_effect.equal_range(code);
for(auto eit = rg.first; eit != rg.second;) {
peffect = eit->second;
++eit;
if (peffect->is_available() && (!peffect->is_flag(EFFECT_FLAG_SINGLE_RANGE) || is_affect_by_effect(peffect)))
return peffect;
}
for (auto& pcard : equiping_cards) {
rg = pcard->equip_effect.equal_range(code);
for(auto eit = rg.first; eit != rg.second;) {
peffect = eit->second;
++eit;
if (peffect->is_available() && is_affect_by_effect(peffect))
return peffect;
}
}
for (auto& pcard : effect_target_owner) {
rg = pcard->target_effect.equal_range(code);
for(auto eit = rg.first; eit != rg.second;) {
peffect = eit->second;
++eit;
if (peffect->is_available() && peffect->is_target(this) && is_affect_by_effect(peffect))
return peffect;
}
}
for (auto& pcard : xyz_materials) {
rg = pcard->xmaterial_effect.equal_range(code);
for(auto eit = rg.first; eit != rg.second;) {
peffect = eit->second;
++eit;
if (peffect->type & EFFECT_TYPE_FIELD)
continue;
if (peffect->is_available() && is_affect_by_effect(peffect))
return peffect;
}
}
rg = pduel->game_field->effects.aura_effect.equal_range(code);
for(auto eit = rg.first; eit != rg.second;) {
peffect = eit->second;
++eit;
if (!peffect->is_flag(EFFECT_FLAG_PLAYER_TARGET) && peffect->is_target(this)
&& peffect->is_available() && is_affect_by_effect(peffect))
return peffect;
}
return nullptr;
}
effect* card::is_affected_by_effect(int32_t code, card* target) {
effect* peffect;
auto rg = single_effect.equal_range(code);
for(auto eit = rg.first; eit != rg.second;) {
peffect = eit->second;
++eit;
if (peffect->is_available() && (!peffect->is_flag(EFFECT_FLAG_SINGLE_RANGE) || is_affect_by_effect(peffect))
&& peffect->get_value(target))
return peffect;
}
for (auto& pcard : equiping_cards) {
rg = pcard->equip_effect.equal_range(code);
for(auto eit = rg.first; eit != rg.second;) {
peffect = eit->second;
++eit;
if (peffect->is_available() && is_affect_by_effect(peffect) && peffect->get_value(target))
return peffect;
}
}
for (auto& pcard : equiping_cards) {
rg = pcard->equip_effect.equal_range(code);
for(auto eit = rg.first; eit != rg.second;) {
peffect = eit->second;
++eit;
if (peffect->type & EFFECT_TYPE_FIELD)
continue;
if (peffect->is_available() && is_affect_by_effect(peffect) && peffect->get_value(target))
return peffect;
}
}
for (auto& pcard : effect_target_owner) {
rg = pcard->target_effect.equal_range(code);
for(auto eit = rg.first; eit != rg.second;) {
peffect = eit->second;
++eit;
if (peffect->is_available() && peffect->is_target(this) && is_affect_by_effect(peffect) && peffect->get_value(target))
return peffect;
}
}
for (auto& pcard : xyz_materials) {
rg = pcard->xmaterial_effect.equal_range(code);
for(auto eit = rg.first; eit != rg.second;) {
peffect = eit->second;
++eit;
if (peffect->type & EFFECT_TYPE_FIELD)
continue;
if (peffect->is_available() && is_affect_by_effect(peffect) && peffect->get_value(target))
return peffect;
}
}
rg = pduel->game_field->effects.aura_effect.equal_range(code);
for(auto eit = rg.first; eit != rg.second;) {
peffect = eit->second;
++eit;
if (!peffect->is_flag(EFFECT_FLAG_PLAYER_TARGET) && peffect->is_available()
&& peffect->is_target(this) && is_affect_by_effect(peffect) && peffect->get_value(target))
return peffect;
}
return nullptr;
}
void card::get_card_effect(uint32_t code, effect_set* eset) {
effect* peffect;
for (auto rg = single_effect.begin(); rg != single_effect.end();) {
peffect = rg->second;
++rg;
if ((code == 0 || peffect->code == code) && peffect->is_available() && (!peffect->is_flag(EFFECT_FLAG_SINGLE_RANGE) || is_affect_by_effect(peffect)))
eset->push_back(peffect);
}
for (auto rg = field_effect.begin(); rg != field_effect.end(); ++rg) {
peffect = rg->second;
if ((code == 0 || peffect->code == code) && is_affect_by_effect(peffect))
eset->push_back(peffect);
}
for (auto cit = equiping_cards.begin(); cit != equiping_cards.end(); ++cit) {
for (auto rg = (*cit)->equip_effect.begin(); rg != (*cit)->equip_effect.end();) {
peffect = rg->second;
++rg;
if ((code == 0 || peffect->code == code) && peffect->is_available() && is_affect_by_effect(peffect))
eset->push_back(peffect);
}
}
for(auto& pcard : effect_target_owner) {
auto rg = pcard->target_effect.equal_range(code);
for(auto eit = rg.first; eit != rg.second;) {
peffect = eit->second;
++eit;
if((code == 0 || peffect->code == code) && peffect->is_available() && peffect->is_target(this) && is_affect_by_effect(peffect))
eset->push_back(peffect);
}
}
for (auto cit = xyz_materials.begin(); cit != xyz_materials.end(); ++cit) {
for (auto rg = (*cit)->xmaterial_effect.begin(); rg != (*cit)->xmaterial_effect.end();) {
peffect = rg->second;
++rg;
if (peffect->type & EFFECT_TYPE_FIELD)
continue;
if ((code == 0 || peffect->code == code) && peffect->is_available() && is_affect_by_effect(peffect))
eset->push_back(peffect);
}
}
for (auto rg = pduel->game_field->effects.aura_effect.begin(); rg != pduel->game_field->effects.aura_effect.end();) {
peffect = rg->second;
++rg;
if ((code == 0 || peffect->code == code) && !peffect->is_flag(EFFECT_FLAG_PLAYER_TARGET) && peffect->is_target(this)
&& peffect->is_available() && is_affect_by_effect(peffect))
eset->push_back(peffect);
}
}
void card::get_own_effects(effect_set* eset) {
std::transform(indexer.begin(), indexer.end(), std::back_inserter(*eset), [](const auto& indexed) {
return indexed.first;
});
std::sort(eset->begin(), eset->end(), effect_sort_id);
}
int32_t card::fusion_check(group* fusion_m, group* cg, uint32_t chkf) {
effect* peffect = nullptr;
auto ecit = single_effect.find(EFFECT_FUSION_MATERIAL);
for (; ecit != single_effect.end(); ++ecit) {
peffect = ecit->second;
if (!peffect->condition || peffect->code != EFFECT_FUSION_MATERIAL)
continue;
pduel->lua->add_param<LuaParam::EFFECT>(peffect);
pduel->lua->add_param<LuaParam::GROUP>(fusion_m);
pduel->lua->add_param<LuaParam::GROUP>(cg);
pduel->lua->add_param<LuaParam::INT>(chkf);
pduel->lua->add_param<LuaParam::EFFECT>(pduel->game_field->core.reason_effect);
effect* oreason = pduel->game_field->core.reason_effect;
uint8_t op = pduel->game_field->core.reason_player;
pduel->game_field->core.reason_effect = peffect;
pduel->game_field->core.reason_player = peffect->get_handler_player();
int32_t res = pduel->lua->check_condition(peffect->condition, 5);
pduel->game_field->core.reason_effect = oreason;
pduel->game_field->core.reason_player = op;
if(res)
return TRUE;
}
return FALSE;
}
void card::fusion_filter_valid(group* fusion_m, group* cg, uint32_t chkf, effect_set* eset) {
effect* peffect = nullptr;
auto ecit = single_effect.find(EFFECT_FUSION_MATERIAL);
for (; ecit != single_effect.end(); ++ecit) {
peffect = ecit->second;
if (!peffect->condition || peffect->code != EFFECT_FUSION_MATERIAL)
continue;
pduel->lua->add_param<LuaParam::EFFECT>(peffect);
pduel->lua->add_param<LuaParam::GROUP>(fusion_m);
pduel->lua->add_param<LuaParam::GROUP>(cg);
pduel->lua->add_param<LuaParam::INT>(chkf);
pduel->lua->add_param<LuaParam::EFFECT>(pduel->game_field->core.reason_effect);
effect* oreason = pduel->game_field->core.reason_effect;
uint8_t op = pduel->game_field->core.reason_player;
pduel->game_field->core.reason_effect = peffect;
pduel->game_field->core.reason_player = peffect->get_handler_player();
int32_t res = pduel->lua->check_condition(peffect->condition, 5);
pduel->game_field->core.reason_effect = oreason;
pduel->game_field->core.reason_player = op;
if (res)
eset->push_back(peffect);
}
}
int32_t card::check_fusion_substitute(card* fcard) {
effect_set eset;
filter_effect(EFFECT_FUSION_SUBSTITUTE, &eset);
if(eset.size() == 0)
return FALSE;
for(const auto& peffect : eset)
if(!peffect->value || peffect->get_value(fcard))
return TRUE;
return FALSE;
}
int32_t card::is_not_tuner(card* scard, uint8_t playerid) {
if(!(get_type(scard, SUMMON_TYPE_SYNCHRO, playerid) & TYPE_TUNER))
return TRUE;
effect_set eset;
filter_effect(EFFECT_NONTUNER, &eset);
for(const auto& peffect : eset)
if(!peffect->value || peffect->get_value(scard))
return TRUE;
return FALSE;
}
int32_t card::check_unique_code(card* pcard) {
if(!unique_code)
return FALSE;
if(unique_code == 1) {
pduel->lua->add_param<LuaParam::CARD>(pcard);
return pduel->lua->get_function_value(unique_function, 1);
}
uint32_t code1 = pcard->get_code();
uint32_t code2 = pcard->get_another_code();
if(code1 == unique_code || (code2 && code2 == unique_code))
return TRUE;
return FALSE;
}
void card::get_unique_target(card_set* cset, uint8_t controler, card* icard) {
cset->clear();
for(uint8_t p = 0; p < 2; ++p) {
if(!unique_pos[p])
continue;
const auto& player = pduel->game_field->player[controler ^ p];
if(unique_location & LOCATION_MZONE) {
for(auto& pcard : player.list_mzone) {
if(pcard && (pcard != icard) && pcard->is_position(POS_FACEUP) && !pcard->get_status(STATUS_SPSUMMON_STEP)
&& check_unique_code(pcard))
cset->insert(pcard);
}
}
if(unique_location & LOCATION_SZONE) {
for(auto& pcard : player.list_szone) {
if(pcard && (pcard != icard) && pcard->is_position(POS_FACEUP) && check_unique_code(pcard))
cset->insert(pcard);
}
}
}
}
int32_t card::check_cost_condition(uint32_t ecode, uint8_t playerid) {
effect_set eset;
pduel->game_field->filter_player_effect(playerid, ecode, &eset, false);
filter_effect(ecode, &eset);
for(const auto& peffect : eset) {
pduel->lua->add_param<LuaParam::EFFECT>(peffect);
pduel->lua->add_param<LuaParam::CARD>(this);
pduel->lua->add_param<LuaParam::INT>(playerid);
if(!pduel->lua->check_condition(peffect->cost, 3))
return FALSE;
}
return TRUE;
}
int32_t card::check_cost_condition(uint32_t ecode, uint8_t playerid, uint32_t sumtype) {
effect_set eset;
pduel->game_field->filter_player_effect(playerid, ecode, &eset, false);
filter_effect(ecode, &eset);
for(const auto& peffect : eset) {
pduel->lua->add_param<LuaParam::EFFECT>(peffect);
pduel->lua->add_param<LuaParam::CARD>(this);
pduel->lua->add_param<LuaParam::INT>(playerid);
pduel->lua->add_param<LuaParam::INT>(sumtype);
if(!pduel->lua->check_condition(peffect->cost, 4))
return FALSE;
}
return TRUE;
}
// check if this is a normal summonable card
int32_t card::is_summonable_card() {
if(!(data.type & TYPE_MONSTER) || (data.type & (TYPE_RITUAL | TYPE_SPSUMMON
| TYPE_FUSION | TYPE_SYNCHRO | TYPE_XYZ | TYPE_LINK
| TYPE_TOKEN | TYPE_TRAPMONSTER)))
return FALSE;
return !is_affected_by_effect(EFFECT_UNSUMMONABLE_CARD);
}
int32_t card::is_fusion_summonable_card(uint32_t summon_type) {
if(!(data.type & TYPE_FUSION))
return FALSE;
summon_type |= SUMMON_TYPE_FUSION;
effect_set eset;
filter_effect(EFFECT_SPSUMMON_CONDITION, &eset);
for(const auto& peffect : eset) {
pduel->lua->add_param<LuaParam::EFFECT>(pduel->game_field->core.reason_effect);
pduel->lua->add_param<LuaParam::INT>(pduel->game_field->core.reason_player);
pduel->lua->add_param<LuaParam::INT>(summon_type);
pduel->lua->add_param<LuaParam::INT>(0);
pduel->lua->add_param<LuaParam::INT>(0);
if(!peffect->check_value_condition(5))
return FALSE;
}
return TRUE;
}
// check the condition of sp_summon procedure peffect
int32_t card::is_spsummonable(effect* peffect) {
effect* oreason = pduel->game_field->core.reason_effect;
uint8_t op = pduel->game_field->core.reason_player;
pduel->game_field->core.reason_effect = peffect;
pduel->game_field->core.reason_player = this->current.controler;
pduel->game_field->save_lp_cost();
pduel->lua->add_param<LuaParam::EFFECT>(peffect);
pduel->lua->add_param<LuaParam::CARD>(this);
pduel->lua->add_param<LuaParam::GROUP>(pduel->game_field->core.must_use_mats);
pduel->lua->add_param<LuaParam::GROUP>(pduel->game_field->core.only_use_mats);
uint32_t result = FALSE;
uint32_t param_count = 4;
if(pduel->game_field->core.forced_summon_minc) {
pduel->lua->add_param<LuaParam::INT>(pduel->game_field->core.forced_summon_minc);
pduel->lua->add_param<LuaParam::INT>(pduel->game_field->core.forced_summon_maxc);
param_count += 2;
}
if (pduel->lua->check_condition(peffect->condition, param_count))
result = TRUE;
pduel->game_field->restore_lp_cost();
pduel->game_field->core.reason_effect = oreason;
pduel->game_field->core.reason_player = op;
return result;
}
// check the condition of summon/set procedure peffect
int32_t card::is_summonable(effect* peffect, uint8_t min_tribute, uint32_t zone, uint32_t releasable, effect* exeffect) {
effect* oreason = pduel->game_field->core.reason_effect;
uint8_t op = pduel->game_field->core.reason_player;
pduel->game_field->core.reason_effect = peffect;
pduel->game_field->core.reason_player = this->current.controler;
pduel->game_field->save_lp_cost();
uint32_t result = FALSE;
pduel->lua->add_param<LuaParam::EFFECT>(peffect);
pduel->lua->add_param<LuaParam::CARD>(this);
pduel->lua->add_param<LuaParam::INT>(min_tribute);
pduel->lua->add_param<LuaParam::INT>(zone);
pduel->lua->add_param<LuaParam::INT>(releasable);
pduel->lua->add_param<LuaParam::EFFECT>(exeffect);
if(pduel->lua->check_condition(peffect->condition, 6))
result = TRUE;
pduel->game_field->restore_lp_cost();
pduel->game_field->core.reason_effect = oreason;
pduel->game_field->core.reason_player = op;
return result;
}
// if this does not have a summon procedure, it will check ordinary summon
// ignore_count: ignore the summon count in this turn or not
// peffect: effects that change the ordinary summon procedure (c80921533)
// min_tribute: the limit of min tribute number by EFFECT_EXTRA_SUMMON_COUNT
// return: whether playerid can summon this or not
int32_t card::is_can_be_summoned(uint8_t playerid, uint8_t ignore_count, effect* peffect, uint8_t min_tribute, uint32_t zone) {
if(!is_summonable_card())
return FALSE;
{
auto reason_effect = pduel->game_field->core.reason_effect;
if(reason_effect && reason_effect->get_handler() == this)
reason_effect->status |= EFFECT_STATUS_SUMMON_SELF;
}
if(!ignore_count && (pduel->game_field->core.extra_summon[playerid] || !is_affected_by_effect(EFFECT_EXTRA_SUMMON_COUNT))
&& pduel->game_field->core.summon_count[playerid] >= pduel->game_field->get_summon_count_limit(playerid))
return FALSE;
if(is_status(STATUS_FORBIDDEN))
return FALSE;
pduel->game_field->save_lp_cost();
if(!check_cost_condition(EFFECT_SUMMON_COST, playerid)) {
pduel->game_field->restore_lp_cost();
return FALSE;
}
if(current.location == LOCATION_MZONE) {
if(is_position(POS_FACEDOWN)
|| !is_affected_by_effect(EFFECT_GEMINI_SUMMONABLE)
|| is_affected_by_effect(EFFECT_GEMINI_STATUS)
|| !pduel->game_field->is_player_can_summon(SUMMON_TYPE_GEMINI, playerid, this, playerid)
|| is_affected_by_effect(EFFECT_CANNOT_SUMMON)) {
pduel->game_field->restore_lp_cost();
return FALSE;
}
} else if(current.location == LOCATION_HAND) {
if(is_affected_by_effect(EFFECT_CANNOT_SUMMON)) {
pduel->game_field->restore_lp_cost();
return FALSE;
}
effect_set proc;
int32_t res = filter_summon_procedure(playerid, &proc, ignore_count, min_tribute, zone);
if(peffect) {
if(res < 0 || !check_summon_procedure(peffect, playerid, ignore_count, min_tribute, zone)) {
pduel->game_field->restore_lp_cost();
return FALSE;
}
} else {
if(!proc.size() && (!res || res == -2)) {
pduel->game_field->restore_lp_cost();
return FALSE;
}
}
}
pduel->game_field->restore_lp_cost();
return TRUE;
}
// return: the min/max number of tribute for an ordinary advance summon of this
int32_t card::get_summon_tribute_count() {
int32_t min = 0, max = 0;
int32_t minul = 0, maxul = 0;
int32_t level = get_level();
if(level < 5)
return 0;
else if(level < 7)
min = max = 1;
else
min = max = 2;
effect_set eset;
filter_effect(EFFECT_DECREASE_TRIBUTE, &eset);
for(const auto& peffect : eset) {
int32_t dec = peffect->get_value(this);
if(!peffect->is_flag(EFFECT_FLAG_COUNT_LIMIT)) {
if(minul < (dec & 0xffff))
minul = dec & 0xffff;
if(maxul < (dec >> 16))
maxul = dec >> 16;
} else if(peffect->count_limit > 0) {
min -= dec & 0xffff;
max -= dec >> 16;
}
}
min -= minul;
max -= maxul;
if(min < 0) min = 0;
if(max < min) max = min;
return min + (max << 16);
}
int32_t card::get_set_tribute_count() {
int32_t min = 0, max = 0;
int32_t level = get_level();
if(level < 5)
return 0;
else if(level < 7)
min = max = 1;
else
min = max = 2;
effect_set eset;
filter_effect(EFFECT_DECREASE_TRIBUTE_SET, &eset);
if(eset.size()) {
int32_t dec = eset.back()->get_value(this);
min -= dec & 0xffff;
max -= dec >> 16;
}
if(min < 0) min = 0;
if(max < min) max = min;
return min + (max << 16);
}
int32_t card::is_can_be_flip_summoned(uint8_t playerid) {
if(is_status(STATUS_FORM_CHANGED))
return FALSE;
if((is_status(STATUS_SUMMON_TURN) || is_status(STATUS_FLIP_SUMMON_TURN) || is_status(STATUS_SPSUMMON_TURN)) &&
(summon.player == current.controler || !pduel->game_field->is_flag(DUEL_CAN_REPOS_IF_NON_SUMPLAYER)))
return FALSE;
if(announce_count > 0)
return FALSE;
if(current.location != LOCATION_MZONE)
return FALSE;
if(!(current.position & POS_FACEDOWN))
return FALSE;
if(pduel->game_field->check_unique_onfield(this, playerid, LOCATION_MZONE))
return FALSE;
if(!pduel->game_field->is_player_can_flipsummon(playerid, this))
return FALSE;
if(is_status(STATUS_FORBIDDEN))
return FALSE;
if(is_affected_by_effect(EFFECT_CANNOT_FLIP_SUMMON))
return FALSE;
if(is_affected_by_effect(EFFECT_CANNOT_CHANGE_POSITION))
return FALSE;
pduel->game_field->save_lp_cost();
if(!check_cost_condition(EFFECT_FLIPSUMMON_COST, playerid)) {
pduel->game_field->restore_lp_cost();
return FALSE;
}
pduel->game_field->restore_lp_cost();
return TRUE;
}
// check if this can be sp_summoned by EFFECT_SPSUMMON_PROC
// call filter_spsummon_procedure()
int32_t card::is_special_summonable(uint8_t playerid, uint32_t summon_type) {
if(!(data.type & TYPE_MONSTER))
return FALSE;
if(is_affected_by_effect(EFFECT_CANNOT_SPECIAL_SUMMON))
return FALSE;
if(is_status(STATUS_FORBIDDEN))
return FALSE;
pduel->game_field->save_lp_cost();
if(!check_cost_condition(EFFECT_SPSUMMON_COST, playerid, summon_type)) {
pduel->game_field->restore_lp_cost();
return FALSE;
}
effect_set eset;
filter_spsummon_procedure(playerid, &eset, summon_type);
pduel->game_field->restore_lp_cost();
return static_cast<uint32_t>(eset.size());
}
int32_t card::is_can_be_special_summoned(effect* reason_effect, uint32_t sumtype, uint8_t sumpos, uint8_t sumplayer, uint8_t toplayer, uint8_t nocheck, uint8_t nolimit, uint32_t zone) {
if(reason_effect->get_handler() == this)
reason_effect->status |= EFFECT_STATUS_SUMMON_SELF;
if(current.location == LOCATION_MZONE)
return FALSE;
if(current.location == LOCATION_REMOVED && (current.position & POS_FACEDOWN))
return FALSE;
if(!nolimit && is_affected_by_effect(EFFECT_REVIVE_LIMIT) && !is_status(STATUS_PROC_COMPLETE)) {
if((current.location & (LOCATION_GRAVE | LOCATION_REMOVED | LOCATION_SZONE))
|| (!nocheck && (current.location & (LOCATION_DECK | LOCATION_HAND))))
return FALSE;
if((data.type & TYPE_PENDULUM) && current.location == LOCATION_EXTRA && (current.position & POS_FACEUP))
return FALSE;
if(current.location == LOCATION_OVERLAY)
return FALSE;
}
if((data.type & TYPE_PENDULUM) && current.location == LOCATION_EXTRA && (current.position & POS_FACEUP)
&& (sumtype == SUMMON_TYPE_FUSION || sumtype == SUMMON_TYPE_SYNCHRO || sumtype == SUMMON_TYPE_XYZ))
return FALSE;
if((sumpos & POS_FACEDOWN) && pduel->game_field->is_player_affected_by_effect(sumplayer, EFFECT_DEVINE_LIGHT))
sumpos = (sumpos & POS_FACEUP) | ((sumpos & POS_FACEDOWN) >> 1);
if((sumpos & POS_FACEUP) && pduel->game_field->check_unique_onfield(this, toplayer, LOCATION_MZONE)) {
if((sumpos &= ~POS_FACEUP) == 0)
return FALSE;
}
sumtype |= SUMMON_TYPE_SPECIAL;
if((sumplayer == 0 || sumplayer == 1) && !pduel->game_field->is_player_can_spsummon(reason_effect, sumtype, sumpos, sumplayer, toplayer, this))
return FALSE;
if(is_affected_by_effect(EFFECT_CANNOT_SPECIAL_SUMMON))
return FALSE;
if(is_status(STATUS_FORBIDDEN))
return FALSE;
if(zone != 0xff) {
if(pduel->game_field->get_useable_count(this, toplayer, LOCATION_MZONE, sumplayer, LOCATION_REASON_TOFIELD, zone, nullptr) <= 0)
return FALSE;
}
pduel->game_field->save_lp_cost();
if(!check_cost_condition(EFFECT_SPSUMMON_COST, sumplayer, sumtype)) {
pduel->game_field->restore_lp_cost();
return FALSE;
}
if(!nocheck) {
effect_set eset;
if(!(data.type & TYPE_MONSTER)) {
pduel->game_field->restore_lp_cost();
return FALSE;
}
filter_effect(EFFECT_SPSUMMON_CONDITION, &eset);
for(const auto& peffect : eset) {
pduel->lua->add_param<LuaParam::EFFECT>(reason_effect);
pduel->lua->add_param<LuaParam::INT>(sumplayer);
pduel->lua->add_param<LuaParam::INT>(sumtype);
pduel->lua->add_param<LuaParam::INT>(sumpos);
pduel->lua->add_param<LuaParam::INT>(toplayer);
if(!peffect->check_value_condition(5)) {
pduel->game_field->restore_lp_cost();
return FALSE;
}
}
}
pduel->game_field->restore_lp_cost();
return TRUE;
}
// if this does not have a set set procedure, it will check ordinary set (see is_can_be_summoned())
int32_t card::is_setable_mzone(uint8_t playerid, uint8_t ignore_count, effect* peffect, uint8_t min_tribute, uint32_t zone) {
if(!is_summonable_card())
return FALSE;
{
auto reason_effect = pduel->game_field->core.reason_effect;
if(reason_effect && reason_effect->get_handler() == this)
reason_effect->status |= EFFECT_STATUS_SUMMON_SELF;
}
if(current.location != LOCATION_HAND)
return FALSE;
if(is_status(STATUS_FORBIDDEN))
return FALSE;
if(is_affected_by_effect(EFFECT_CANNOT_MSET))
return FALSE;
if(!ignore_count && (pduel->game_field->core.extra_summon[playerid] || !is_affected_by_effect(EFFECT_EXTRA_SET_COUNT))
&& pduel->game_field->core.summon_count[playerid] >= pduel->game_field->get_summon_count_limit(playerid))
return FALSE;
pduel->game_field->save_lp_cost();
if(!check_cost_condition(EFFECT_MSET_COST, playerid)) {
pduel->game_field->restore_lp_cost();
return FALSE;
}
effect_set eset;
int32_t res = filter_set_procedure(playerid, &eset, ignore_count, min_tribute, zone);
if(peffect) {
if(res < 0 || !check_set_procedure(peffect, playerid, ignore_count, min_tribute, zone)) {
pduel->game_field->restore_lp_cost();
return FALSE;
}
} else {
if(!eset.size() && (!res || res == -2)) {
pduel->game_field->restore_lp_cost();
return FALSE;
}
}
pduel->game_field->restore_lp_cost();
return TRUE;
}
int32_t card::is_setable_szone(uint8_t playerid, uint8_t ignore_fd) {
if(!(data.type & TYPE_FIELD) && !ignore_fd && pduel->game_field->get_useable_count(this, playerid, LOCATION_SZONE, current.controler, LOCATION_REASON_TOFIELD) <= 0)
return FALSE;
if(data.type & TYPE_MONSTER && !is_affected_by_effect(EFFECT_MONSTER_SSET))
return FALSE;
if(is_status(STATUS_FORBIDDEN))
return FALSE;
if(is_affected_by_effect(EFFECT_CANNOT_SSET))
return FALSE;
if(!pduel->game_field->is_player_can_sset(playerid, this))
return FALSE;
pduel->game_field->save_lp_cost();
if(!check_cost_condition(EFFECT_SSET_COST, playerid)) {
pduel->game_field->restore_lp_cost();
return FALSE;
}
pduel->game_field->restore_lp_cost();
return TRUE;
}
// return: this is affected by peffect or not
int32_t card::is_affect_by_effect(effect* peffect) {
if(is_status(STATUS_SUMMONING) && (peffect && peffect->code != EFFECT_CANNOT_DISABLE_SUMMON && peffect->code != EFFECT_CANNOT_DISABLE_SPSUMMON))
return FALSE;
if(!peffect || peffect->is_flag(EFFECT_FLAG_IGNORE_IMMUNE))
return TRUE;
if(peffect->is_immuned(this))
return FALSE;
return TRUE;
}
int32_t card::is_can_be_disabled_by_effect(effect* reason_effect, bool is_monster_effect) {
if(is_monster_effect && is_status(STATUS_DISABLED))
return FALSE;
if(!is_monster_effect && !(get_type() & TYPE_TRAPMONSTER) && is_status(STATUS_DISABLED))
return FALSE;
if(is_affected_by_effect(EFFECT_CANNOT_DISABLE))
return FALSE;
if(!is_affect_by_effect(reason_effect))
return FALSE;
return TRUE;
}
int32_t card::is_destructable() {
if(overlay_target)
return FALSE;
if(current.location & (LOCATION_GRAVE | LOCATION_REMOVED))
return FALSE;
return TRUE;
}
int32_t card::is_destructable_by_battle(card* pcard) {
if(is_affected_by_effect(EFFECT_INDESTRUCTABLE_BATTLE, pcard))
return FALSE;
return TRUE;
}
effect* card::check_indestructable_by_effect(effect* peffect, uint8_t playerid) {
if(!peffect)
return nullptr;
effect_set eset;
filter_effect(EFFECT_INDESTRUCTABLE_EFFECT, &eset);
for(const auto& peff : eset) {
pduel->lua->add_param<LuaParam::EFFECT>(peffect);
pduel->lua->add_param<LuaParam::INT>(playerid);
pduel->lua->add_param<LuaParam::CARD>(this);
if(peff->check_value_condition(3))
return peff;
}
return nullptr;
}
int32_t card::is_destructable_by_effect(effect* peffect, uint8_t playerid) {
if(!is_affect_by_effect(peffect))
return FALSE;
if(check_indestructable_by_effect(peffect, playerid))
return FALSE;
effect_set eset;
eset.clear();
filter_effect(EFFECT_INDESTRUCTABLE, &eset);
for(const auto& peff : eset) {
pduel->lua->add_param<LuaParam::EFFECT>(peffect);
pduel->lua->add_param<LuaParam::INT>(REASON_EFFECT);
pduel->lua->add_param<LuaParam::INT>(playerid);
if(peff->check_value_condition(3)) {
return FALSE;
}
}
eset.clear();
filter_effect(EFFECT_INDESTRUCTABLE_COUNT, &eset);
for(const auto& peff : eset) {
if(peff->is_flag(EFFECT_FLAG_COUNT_LIMIT)) {
if(peff->count_limit == 0)
continue;
pduel->lua->add_param<LuaParam::EFFECT>(peffect);
pduel->lua->add_param<LuaParam::INT>(REASON_EFFECT);
pduel->lua->add_param<LuaParam::INT>(playerid);
if(peff->check_value_condition(3)) {
return FALSE;
}
} else {
pduel->lua->add_param<LuaParam::EFFECT>(peffect);
pduel->lua->add_param<LuaParam::INT>(REASON_EFFECT);
pduel->lua->add_param<LuaParam::INT>(playerid);
int32_t ct = peff->get_value(3);
if(ct) {
auto it = indestructable_effects.emplace(peff->id, 0);
if(it.first->second + 1 <= ct) {
return FALSE;
}
}
}
}
return TRUE;
}
int32_t card::is_removeable(uint8_t playerid, uint8_t pos, uint32_t reason) {
if(!pduel->game_field->is_player_can_remove(playerid, this, reason))
return FALSE;
if(is_affected_by_effect(EFFECT_CANNOT_REMOVE))
return FALSE;
if((data.type & TYPE_TOKEN) && (pos & POS_FACEDOWN))
return FALSE;
return TRUE;
}
int32_t card::is_removeable_as_cost(uint8_t playerid, uint8_t pos) {
uint32_t redirect = 0;
uint32_t dest = LOCATION_REMOVED;
if(current.location == LOCATION_REMOVED)
return FALSE;
if(is_affected_by_effect(EFFECT_CANNOT_USE_AS_COST))
return FALSE;
if(!is_removeable(playerid, pos, REASON_COST))
return FALSE;
bool redirchk = false;
auto op_param = sendto_param;
sendto_param.location = dest;
if (current.location & LOCATION_ONFIELD)
redirect = leave_field_redirect(REASON_COST) & 0xffff;
if(redirect) {
redirchk = true;
dest = redirect;
}
redirect = destination_redirect(dest, REASON_COST) & 0xffff;
if(redirect) {
redirchk = true;
dest = redirect;
}
sendto_param = op_param;
if(dest != LOCATION_REMOVED || (redirchk && (pos & POS_FACEDOWN)))
return FALSE;
return TRUE;
}
int32_t card::is_releasable_by_summon(uint8_t playerid, card* pcard) {
if(is_status(STATUS_SUMMONING))
return FALSE;
if(overlay_target)
return FALSE;
if(current.location & (LOCATION_GRAVE | LOCATION_REMOVED))
return FALSE;
if(!pduel->game_field->is_player_can_release(playerid, this, REASON_SUMMON))
return FALSE;
if(is_affected_by_effect(EFFECT_UNRELEASABLE_SUM, pcard))
return FALSE;
if(pcard->is_affected_by_effect(EFFECT_TRIBUTE_LIMIT, this))
return FALSE;
return TRUE;
}
int32_t card::is_releasable_by_nonsummon(uint8_t playerid, uint32_t reason) {
if(is_status(STATUS_SUMMONING))
return FALSE;
if(overlay_target)
return FALSE;
if(current.location & (LOCATION_GRAVE | LOCATION_REMOVED))
return FALSE;
if((current.location == LOCATION_HAND) && (data.type & (TYPE_SPELL | TYPE_TRAP)))
return FALSE;
if(!pduel->game_field->is_player_can_release(playerid, this, reason))
return FALSE;
if(is_affected_by_effect(EFFECT_UNRELEASABLE_NONSUM))
return FALSE;
return TRUE;
}
int32_t card::is_releasable_by_effect(uint8_t playerid, effect* peffect) {
if(!peffect)
return TRUE;
effect_set eset;
filter_effect(EFFECT_UNRELEASABLE_EFFECT, &eset);
for(const auto& peff : eset) {
pduel->lua->add_param<LuaParam::EFFECT>(peffect);
pduel->lua->add_param<LuaParam::INT>(playerid);
pduel->lua->add_param<LuaParam::CARD>(this);
if(peff->check_value_condition(3))
return FALSE;
}
return TRUE;
}
int32_t card::is_capable_send_to_grave(uint8_t playerid) {
if(is_affected_by_effect(EFFECT_CANNOT_TO_GRAVE))
return FALSE;
if(!pduel->game_field->is_player_can_send_to_grave(playerid, this))
return FALSE;
return TRUE;
}
int32_t card::is_capable_send_to_hand(uint8_t playerid) {
if(is_status(STATUS_LEAVE_CONFIRMED))
return FALSE;
if((current.location == LOCATION_EXTRA) && is_extra_deck_monster())
return FALSE;
if(is_affected_by_effect(EFFECT_CANNOT_TO_HAND))
return FALSE;
if(is_extra_deck_monster() && !is_capable_send_to_deck(playerid))
return FALSE;
if(!pduel->game_field->is_player_can_send_to_hand(playerid, this))
return FALSE;
return TRUE;
}
int32_t card::is_capable_send_to_deck(uint8_t playerid) {
if(is_status(STATUS_LEAVE_CONFIRMED))
return FALSE;
if((current.location == LOCATION_EXTRA) && is_extra_deck_monster())
return FALSE;
if(is_affected_by_effect(EFFECT_CANNOT_TO_DECK))
return FALSE;
if(!pduel->game_field->is_player_can_send_to_deck(playerid, this))
return FALSE;
return TRUE;
}
int32_t card::is_capable_send_to_extra(uint8_t playerid) {
if(!is_extra_deck_monster() && !(data.type & TYPE_PENDULUM))
return FALSE;
if(is_affected_by_effect(EFFECT_CANNOT_TO_DECK))
return FALSE;
if(!pduel->game_field->is_player_can_send_to_deck(playerid, this))
return FALSE;
return TRUE;
}
int32_t card::is_capable_cost_to_grave(uint8_t playerid) {
uint32_t redirect = 0;
uint32_t dest = LOCATION_GRAVE;
if(data.type & TYPE_TOKEN)
return FALSE;
if((data.type & TYPE_PENDULUM) && (current.location & LOCATION_ONFIELD) && is_capable_send_to_extra(playerid))
return FALSE;
if(current.location == LOCATION_GRAVE)
return FALSE;
if(is_affected_by_effect(EFFECT_CANNOT_USE_AS_COST))
return FALSE;
if(is_affected_by_effect(EFFECT_CANNOT_TO_GRAVE_AS_COST))
return FALSE;
if(!is_capable_send_to_grave(playerid))
return FALSE;
auto op_param = sendto_param;
sendto_param.location = dest;
if(current.location & LOCATION_ONFIELD)
redirect = leave_field_redirect(REASON_COST) & 0xffff;
if(redirect) dest = redirect;
redirect = destination_redirect(dest, REASON_COST) & 0xffff;
if(redirect) dest = redirect;
sendto_param = op_param;
if(dest != LOCATION_GRAVE)
return FALSE;
return TRUE;
}
int32_t card::is_capable_cost_to_hand(uint8_t playerid) {
uint32_t redirect = 0;
uint32_t dest = LOCATION_HAND;
if(data.type & (TYPE_TOKEN) || is_extra_deck_monster())
return FALSE;
if(current.location == LOCATION_HAND)
return FALSE;
if(is_affected_by_effect(EFFECT_CANNOT_USE_AS_COST))
return FALSE;
if(!is_capable_send_to_hand(playerid))
return FALSE;
auto op_param = sendto_param;
sendto_param.location = dest;
if(current.location & LOCATION_ONFIELD)
redirect = leave_field_redirect(REASON_COST) & 0xffff;
if(redirect) dest = redirect;
redirect = destination_redirect(dest, REASON_COST) & 0xffff;
if(redirect) dest = redirect;
sendto_param = op_param;
if(dest != LOCATION_HAND)
return FALSE;
return TRUE;
}
int32_t card::is_capable_cost_to_deck(uint8_t playerid) {
uint32_t redirect = 0;
uint32_t dest = LOCATION_DECK;
if(data.type & (TYPE_TOKEN) || is_extra_deck_monster())
return FALSE;
if(current.location == LOCATION_DECK)
return FALSE;
if(is_affected_by_effect(EFFECT_CANNOT_USE_AS_COST))
return FALSE;
if(!is_capable_send_to_deck(playerid))
return FALSE;
auto op_param = sendto_param;
sendto_param.location = dest;
if(current.location & LOCATION_ONFIELD)
redirect = leave_field_redirect(REASON_COST) & 0xffff;
if(redirect) dest = redirect;
redirect = destination_redirect(dest, REASON_COST) & 0xffff;
if(redirect) dest = redirect;
sendto_param = op_param;
if(dest != LOCATION_DECK)
return FALSE;
return TRUE;
}
int32_t card::is_capable_cost_to_extra(uint8_t playerid) {
uint32_t redirect = 0;
uint32_t dest = LOCATION_EXTRA;
if(!is_extra_deck_monster())
return FALSE;
if(current.location == LOCATION_EXTRA)
return FALSE;
if(is_affected_by_effect(EFFECT_CANNOT_USE_AS_COST))
return FALSE;
if(!is_capable_send_to_deck(playerid))
return FALSE;
auto op_param = sendto_param;
sendto_param.location = dest;
if(current.location & LOCATION_ONFIELD)
redirect = leave_field_redirect(REASON_COST) & 0xffff;
if(redirect) dest = redirect;
redirect = destination_redirect(dest, REASON_COST) & 0xffff;
if(redirect) dest = redirect;
sendto_param = op_param;
if(dest != LOCATION_EXTRA)
return FALSE;
return TRUE;
}
int32_t card::is_capable_attack() {
if(!is_position(POS_FACEUP_ATTACK) && !(is_position(POS_FACEUP_DEFENSE) && is_affected_by_effect(EFFECT_DEFENSE_ATTACK)))
return FALSE;
if(is_affected_by_effect(EFFECT_FORBIDDEN))
return FALSE;
if(is_affected_by_effect(EFFECT_CANNOT_ATTACK) && !is_affected_by_effect(EFFECT_UNSTOPPABLE_ATTACK))
return FALSE;
if(is_affected_by_effect(EFFECT_ATTACK_DISABLED) && !is_affected_by_effect(EFFECT_UNSTOPPABLE_ATTACK))
return FALSE;
if(pduel->game_field->is_player_affected_by_effect(pduel->game_field->infos.turn_player, EFFECT_SKIP_BP))
return FALSE;
return TRUE;
}
int32_t card::is_capable_attack_announce(uint8_t playerid) {
if(!is_capable_attack())
return FALSE;
if(is_affected_by_effect(EFFECT_CANNOT_ATTACK_ANNOUNCE) && !is_affected_by_effect(EFFECT_UNSTOPPABLE_ATTACK))
return FALSE;
pduel->game_field->save_lp_cost();
if(!check_cost_condition(EFFECT_ATTACK_COST, playerid)) {
pduel->game_field->restore_lp_cost();
return FALSE;
}
pduel->game_field->restore_lp_cost();
return TRUE;
}
int32_t card::is_capable_change_position(uint8_t playerid) {
if(is_status(STATUS_FORM_CHANGED))
return FALSE;
if((is_status(STATUS_SUMMON_TURN) || is_status(STATUS_FLIP_SUMMON_TURN) || is_status(STATUS_SPSUMMON_TURN)) &&
(summon.player == current.controler || !pduel->game_field->is_flag(DUEL_CAN_REPOS_IF_NON_SUMPLAYER)))
return FALSE;
if((data.type & TYPE_LINK) && (data.type & TYPE_MONSTER))
return FALSE;
if(announce_count > 0)
return FALSE;
if(is_status(STATUS_FORBIDDEN))
return FALSE;
if(is_affected_by_effect(EFFECT_CANNOT_CHANGE_POSITION))
return FALSE;
if(pduel->game_field->is_player_affected_by_effect(playerid, EFFECT_CANNOT_CHANGE_POSITION))
return FALSE;
return TRUE;
}
int32_t card::is_capable_change_position_by_effect(uint8_t /*playerid*/) {
if((data.type & TYPE_LINK) && (data.type & TYPE_MONSTER))
return FALSE;
return TRUE;
}
int32_t card::is_capable_turn_set(uint8_t playerid) {
if((data.type & (TYPE_TOKEN)) || ((data.type & TYPE_LINK) && (data.type & TYPE_MONSTER)))
return FALSE;
if(is_position(POS_FACEDOWN))
return FALSE;
if(is_affected_by_effect(EFFECT_CANNOT_TURN_SET))
return FALSE;
if(pduel->game_field->is_player_affected_by_effect(playerid, EFFECT_CANNOT_TURN_SET))
return FALSE;
return TRUE;
}
int32_t card::is_capable_change_control() {
if(is_affected_by_effect(EFFECT_CANNOT_CHANGE_CONTROL))
return FALSE;
return TRUE;
}
int32_t card::is_control_can_be_changed(int32_t ignore_mzone, uint32_t zone) {
if(current.controler == PLAYER_NONE)
return FALSE;
if(current.location != LOCATION_MZONE)
return FALSE;
if(!ignore_mzone && pduel->game_field->get_useable_count(this, 1 - current.controler, LOCATION_MZONE, current.controler, LOCATION_REASON_CONTROL, zone) <= 0)
return FALSE;
if(!pduel->game_field->is_flag(DUEL_TRAP_MONSTERS_NOT_USE_ZONE) && ((get_type() & TYPE_TRAPMONSTER)
&& pduel->game_field->get_useable_count(this, 1 - current.controler, LOCATION_SZONE, current.controler, LOCATION_REASON_CONTROL) <= 0))
return FALSE;
if(is_affected_by_effect(EFFECT_CANNOT_CHANGE_CONTROL))
return FALSE;
return TRUE;
}
int32_t card::is_capable_be_battle_target(card* pcard) {
if(is_affected_by_effect(EFFECT_CANNOT_BE_BATTLE_TARGET, pcard))
return FALSE;
if(pcard->is_affected_by_effect(EFFECT_CANNOT_SELECT_BATTLE_TARGET, this))
return FALSE;
if(is_affected_by_effect(EFFECT_IGNORE_BATTLE_TARGET, pcard))
return FALSE;
return TRUE;
}
int32_t card::is_capable_be_effect_target(effect* peffect, uint8_t playerid) {
if(is_status(STATUS_SUMMONING) || is_status(STATUS_BATTLE_DESTROYED))
return FALSE;
if(current.location & (LOCATION_DECK | LOCATION_EXTRA | LOCATION_HAND))
return FALSE;
if((data.type & TYPE_TOKEN) && !(current.location & LOCATION_ONFIELD))
return FALSE;
effect_set eset;
filter_effect(EFFECT_CANNOT_BE_EFFECT_TARGET, &eset);
for(const auto& peff : eset) {
pduel->lua->add_param<LuaParam::INT>(playerid);
if(peff->get_value(peffect, 1))
return FALSE;
}
eset.clear();
peffect->get_handler()->filter_effect(EFFECT_CANNOT_SELECT_EFFECT_TARGET, &eset);
for(const auto& peff : eset) {
pduel->lua->add_param<LuaParam::CARD>(this);
if(peff->get_value(peffect, 1))
return FALSE;
}
return TRUE;
}
int32_t card::is_can_be_fusion_material(card* fcard, uint64_t summon_type, uint8_t playerid) {
if(is_status(STATUS_FORBIDDEN))
return FALSE;
effect_set eset;
filter_effect(EFFECT_CANNOT_BE_FUSION_MATERIAL, &eset);
for(const auto& peffect : eset) {
pduel->lua->add_param<LuaParam::INT>(summon_type);
if(peffect->get_value(fcard, 1))
return FALSE;
}
eset.clear();
filter_effect(EFFECT_CANNOT_BE_MATERIAL, &eset);
for(const auto& peffect : eset) {
pduel->lua->add_param<LuaParam::INT>(summon_type);
pduel->lua->add_param<LuaParam::INT>(playerid);
if(peffect->get_value(fcard, 2))
return FALSE;
}
eset.clear();
if (fcard) {
filter_effect(EFFECT_EXTRA_FUSION_MATERIAL, &eset);
if(eset.size()) {
for(const auto& peffect : eset)
if(peffect->get_value(fcard))
return TRUE;
return FALSE;
}
} else {
if (!(current.location & LOCATION_ONFIELD) && !(data.type & TYPE_MONSTER) && !is_affected_by_effect(EFFECT_EXTRA_FUSION_MATERIAL))
return FALSE;
}
return TRUE;
}
int32_t card::is_can_be_synchro_material(card* scard, uint8_t playerid, card* /*tuner*/) {
if(data.type & (TYPE_XYZ) && !(is_affected_by_effect(EFFECT_RANK_LEVEL) || is_affected_by_effect(EFFECT_RANK_LEVEL_S)))
return FALSE;
if (data.type & (TYPE_LINK) && !(is_affected_by_effect(EFFECT_SYNCHRO_LEVEL)))
return FALSE;
if(!(get_type(scard, SUMMON_TYPE_SYNCHRO, playerid) & TYPE_MONSTER))
return FALSE;
if(scard && current.location == LOCATION_MZONE && current.controler != scard->current.controler && !is_affected_by_effect(EFFECT_SYNCHRO_MATERIAL))
return FALSE;
if(is_status(STATUS_FORBIDDEN))
return FALSE;
effect_set eset;
filter_effect(EFFECT_CANNOT_BE_SYNCHRO_MATERIAL, &eset);
for(const auto& peffect : eset)
if(peffect->get_value(scard))
return FALSE;
eset.clear();
filter_effect(EFFECT_CANNOT_BE_MATERIAL, &eset);
for(const auto& peffect : eset) {
pduel->lua->add_param<LuaParam::INT>(SUMMON_TYPE_SYNCHRO);
pduel->lua->add_param<LuaParam::INT>(playerid);
if(peffect->get_value(scard, 2))
return FALSE;
}
return TRUE;
}
int32_t card::is_can_be_ritual_material(card* scard, uint8_t playerid) {
if(!(get_type() & TYPE_MONSTER))
return FALSE;
effect_set eset;
filter_effect(EFFECT_CANNOT_BE_MATERIAL, &eset);
for(const auto& peffect : eset) {
pduel->lua->add_param<LuaParam::INT>(SUMMON_TYPE_RITUAL);
pduel->lua->add_param<LuaParam::INT>(playerid);
if(peffect->get_value(scard, 2))
return FALSE;
}
if(current.location == LOCATION_GRAVE) {
eset.clear();
filter_effect(EFFECT_EXTRA_RITUAL_MATERIAL, &eset);
for(const auto& peffect : eset)
if(peffect->get_value(scard))
return TRUE;
return FALSE;
}
return TRUE;
}
int32_t card::is_can_be_xyz_material(card* scard, uint8_t playerid, uint32_t reason) {
if(data.type & TYPE_TOKEN)
return FALSE;
if((!(current.location & LOCATION_ONFIELD) || (reason & REASON_MATERIAL)) && is_status(STATUS_FORBIDDEN))
return FALSE;
if(reason & REASON_EFFECT)
return (current.controler == playerid) || is_capable_change_control();
if(!(get_type(scard, SUMMON_TYPE_XYZ, playerid) & TYPE_MONSTER))
return FALSE;
if(reason & REASON_MATERIAL) {
effect_set eset;
filter_effect(EFFECT_CANNOT_BE_XYZ_MATERIAL, &eset);
for(const auto& peffect : eset)
if(peffect->get_value(scard))
return FALSE;
eset.clear();
filter_effect(EFFECT_CANNOT_BE_MATERIAL, &eset);
for(const auto& peffect : eset) {
pduel->lua->add_param<LuaParam::INT>(SUMMON_TYPE_XYZ);
pduel->lua->add_param<LuaParam::INT>(playerid);
if(peffect->get_value(scard, 2))
return FALSE;
}
}
return TRUE;
}
int32_t card::is_can_be_link_material(card* scard, uint8_t playerid) {
if(!(get_type(scard, SUMMON_TYPE_LINK, playerid) & TYPE_MONSTER))
return FALSE;
if(is_status(STATUS_FORBIDDEN))
return FALSE;
effect_set eset;
filter_effect(EFFECT_CANNOT_BE_LINK_MATERIAL, &eset);
for(const auto& peffect : eset)
if(peffect->get_value(scard))
return FALSE;
eset.clear();
filter_effect(EFFECT_CANNOT_BE_MATERIAL, &eset);
for(const auto& peffect : eset) {
pduel->lua->add_param<LuaParam::INT>(SUMMON_TYPE_LINK);
pduel->lua->add_param<LuaParam::INT>(playerid);
if(peffect->get_value(scard, 2))
return FALSE;
}
return TRUE;
}
int32_t card::is_can_be_material(card* scard, uint64_t sumtype, uint8_t playerid) {
if(sumtype & SUMMON_TYPE_FUSION)
return is_can_be_fusion_material(scard, sumtype, playerid);
if(sumtype & SUMMON_TYPE_SYNCHRO)
return is_can_be_synchro_material(scard, playerid);
if(sumtype & SUMMON_TYPE_RITUAL)
return is_can_be_ritual_material(scard, playerid);
if(sumtype & SUMMON_TYPE_XYZ)
return is_can_be_xyz_material(scard, playerid, REASON_XYZ | REASON_MATERIAL);
if(sumtype & SUMMON_TYPE_LINK)
return is_can_be_link_material(scard, playerid);
if(is_status(STATUS_FORBIDDEN))
return FALSE;
effect_set eset;
filter_effect(EFFECT_CANNOT_BE_MATERIAL, &eset);
for(const auto& peffect : eset) {
pduel->lua->add_param<LuaParam::INT>(sumtype);
pduel->lua->add_param<LuaParam::INT>(playerid);
if(peffect->get_value(scard, 2))
return FALSE;
}
return TRUE;
}
bool card::recreate(uint32_t code) {
if(!code)
return false;
data = pduel->read_card(code);
return true;
}