dep-ygopro-core/processor.cpp
2025-11-23 13:36:43 +01:00

5221 lines
188 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::find_if_not, std::find, std::max
#include <array>
#include <iterator> //std::next
#include <set>
#include <utility> //std::make_pair, std::exchange, std::move, std::swap
#include "duel.h"
#include "effect.h"
#include "card.h"
#include "field.h"
#include "group.h"
#include "interpreter.h"
bool field::process(Processors::ExecuteCost& arg) {
auto step = arg.step;
auto triggering_effect = arg.triggering_effect;
auto triggering_player = arg.triggering_player;
if(!triggering_effect->cost) {
core.solving_event.splice(core.solving_event.begin(), core.sub_solving_event);
pduel->lua->params.clear();
core.solving_event.pop_front();
return TRUE;
}
if (step == 0) {
core.solving_event.splice(core.solving_event.begin(), core.sub_solving_event);
const tevent& e = core.solving_event.front();
pduel->lua->add_param<LuaParam::INT>(1, true);
pduel->lua->add_param<LuaParam::INT>(e.reason_player, true);
pduel->lua->add_param<LuaParam::INT>(e.reason, true);
pduel->lua->add_param<LuaParam::EFFECT>(e.reason_effect , true);
pduel->lua->add_param<LuaParam::INT>(e.event_value, true);
pduel->lua->add_param<LuaParam::INT>(e.event_player, true);
pduel->lua->add_param<LuaParam::GROUP>(e.event_cards , true);
pduel->lua->add_param<LuaParam::INT>(triggering_player, true);
pduel->lua->add_param<LuaParam::EFFECT>(triggering_effect, true);
if(core.check_level == 0) {
core.shuffle_deck_check[0] = false;
core.shuffle_deck_check[1] = false;
core.shuffle_hand_check[0] = false;
core.shuffle_hand_check[1] = false;
}
arg.shuffle_check_was_disabled = core.shuffle_check_disabled;
core.shuffle_check_disabled = false;
++core.check_level;
}
core.reason_effect = triggering_effect;
core.reason_player = triggering_player;
uint32_t count = static_cast<uint32_t>(pduel->lua->params.size());
lua_Integer yield_value = 0;
int32_t result = pduel->lua->call_coroutine(triggering_effect->cost, count, &yield_value, step);
returns.set<int32_t>(0, static_cast<int32_t>(yield_value));
if (result != COROUTINE_YIELD) {
core.reason_effect = nullptr;
core.reason_player = PLAYER_NONE;
--core.check_level;
if(core.check_level == 0) {
if(core.shuffle_hand_check[0])
shuffle(0, LOCATION_HAND);
if(core.shuffle_hand_check[1])
shuffle(1, LOCATION_HAND);
if(core.shuffle_deck_check[0])
shuffle(0, LOCATION_DECK);
if(core.shuffle_deck_check[1])
shuffle(1, LOCATION_DECK);
}
core.shuffle_check_disabled = arg.shuffle_check_was_disabled;
core.solving_event.pop_front();
return TRUE;
}
return FALSE;
}
bool field::process(Processors::ExecuteOperation& arg) {
auto step = arg.step;
auto triggering_effect = arg.triggering_effect;
auto triggering_player = arg.triggering_player;
if(!triggering_effect->operation) {
core.solving_event.splice(core.solving_event.begin(), core.sub_solving_event);
pduel->lua->params.clear();
core.solving_event.pop_front();
return TRUE;
}
if (step == 0) {
core.solving_event.splice(core.solving_event.begin(), core.sub_solving_event);
const tevent& e = core.solving_event.front();
pduel->lua->add_param<LuaParam::INT>(e.reason_player, true);
pduel->lua->add_param<LuaParam::INT>(e.reason, true);
pduel->lua->add_param<LuaParam::EFFECT>(e.reason_effect , true);
pduel->lua->add_param<LuaParam::INT>(e.event_value, true);
pduel->lua->add_param<LuaParam::INT>(e.event_player, true);
pduel->lua->add_param<LuaParam::GROUP>(e.event_cards , true);
pduel->lua->add_param<LuaParam::INT>(triggering_player, true);
pduel->lua->add_param<LuaParam::EFFECT>(triggering_effect, true);
if(core.check_level == 0) {
core.shuffle_deck_check[0] = false;
core.shuffle_deck_check[1] = false;
core.shuffle_hand_check[0] = false;
core.shuffle_hand_check[1] = false;
}
arg.shuffle_check_was_disabled = core.shuffle_check_disabled;
core.shuffle_check_disabled = false;
++core.check_level;
}
core.reason_effect = triggering_effect;
core.reason_player = triggering_player;
uint32_t count = static_cast<uint32_t>(pduel->lua->params.size());
lua_Integer yield_value = 0;
int32_t result = pduel->lua->call_coroutine(triggering_effect->operation, count, &yield_value, step);
returns.set<int32_t>(0, static_cast<int32_t>(yield_value));
if (result != COROUTINE_YIELD) {
core.reason_effect = nullptr;
core.reason_player = PLAYER_NONE;
--core.check_level;
if(core.check_level == 0) {
if(core.shuffle_hand_check[0])
shuffle(0, LOCATION_HAND);
if(core.shuffle_hand_check[1])
shuffle(1, LOCATION_HAND);
if(core.shuffle_deck_check[0])
shuffle(0, LOCATION_DECK);
if(core.shuffle_deck_check[1])
shuffle(1, LOCATION_DECK);
//cost[0].count = 0;
//cost[1].count = 0;
//cost[0].amount = 0;
//cost[1].amount = 0;
}
core.shuffle_check_disabled = arg.shuffle_check_was_disabled;
core.solving_event.pop_front();
return TRUE;
}
return FALSE;
}
bool field::process(Processors::ExecuteTarget& arg) {
auto step = arg.step;
auto triggering_effect = arg.triggering_effect;
auto triggering_player = arg.triggering_player;
if(!triggering_effect->target) {
core.solving_event.splice(core.solving_event.begin(), core.sub_solving_event);
pduel->lua->params.clear();
core.solving_event.pop_front();
return TRUE;
}
if (step == 0) {
core.solving_event.splice(core.solving_event.begin(), core.sub_solving_event);
const tevent& e = core.solving_event.front();
pduel->lua->add_param<LuaParam::INT>(1, true);
pduel->lua->add_param<LuaParam::INT>(e.reason_player, true);
pduel->lua->add_param<LuaParam::INT>(e.reason, true);
pduel->lua->add_param<LuaParam::EFFECT>(e.reason_effect , true);
pduel->lua->add_param<LuaParam::INT>(e.event_value, true);
pduel->lua->add_param<LuaParam::INT>(e.event_player, true);
pduel->lua->add_param<LuaParam::GROUP>(e.event_cards , true);
pduel->lua->add_param<LuaParam::INT>(triggering_player, true);
pduel->lua->add_param<LuaParam::EFFECT>(triggering_effect, true);
if(core.check_level == 0) {
core.shuffle_deck_check[0] = false;
core.shuffle_deck_check[1] = false;
core.shuffle_hand_check[0] = false;
core.shuffle_hand_check[1] = false;
}
arg.shuffle_check_was_disabled = core.shuffle_check_disabled;
core.shuffle_check_disabled = false;
++core.check_level;
}
core.reason_effect = triggering_effect;
core.reason_player = triggering_player;
uint32_t count = static_cast<uint32_t>(pduel->lua->params.size());
lua_Integer yield_value = 0;
int32_t result = pduel->lua->call_coroutine(triggering_effect->target, count, &yield_value, step);
returns.set<int32_t>(0, static_cast<int32_t>(yield_value));
if (result != COROUTINE_YIELD) {
core.reason_effect = nullptr;
core.reason_player = PLAYER_NONE;
--core.check_level;
if(core.check_level == 0) {
if(core.shuffle_hand_check[0])
shuffle(0, LOCATION_HAND);
if(core.shuffle_hand_check[1])
shuffle(1, LOCATION_HAND);
if(core.shuffle_deck_check[0])
shuffle(0, LOCATION_DECK);
if(core.shuffle_deck_check[1])
shuffle(1, LOCATION_DECK);
}
core.shuffle_check_disabled = arg.shuffle_check_was_disabled;
core.solving_event.pop_front();
return TRUE;
}
return FALSE;
}
void field::raise_event(card* event_card, uint32_t event_code, effect* reason_effect, uint32_t reason, uint8_t reason_player, uint8_t event_player, uint32_t event_value) {
auto& new_event = core.queue_event.emplace_back();
new_event.trigger_card = nullptr;
if (event_card) {
auto pgroup = pduel->new_group(event_card);
pgroup->is_readonly = true;
new_event.event_cards = pgroup;
} else
new_event.event_cards = nullptr;
new_event.event_code = event_code;
new_event.reason_effect = reason_effect;
new_event.reason = reason;
new_event.reason_player = reason_player;
new_event.event_player = event_player;
new_event.event_value = event_value;
new_event.global_id = infos.event_id;
}
void field::raise_event(card_set event_cards, uint32_t event_code, effect* reason_effect, uint32_t reason, uint8_t reason_player, uint8_t event_player, uint32_t event_value) {
auto& new_event = core.queue_event.emplace_back();
new_event.trigger_card = nullptr;
auto pgroup = pduel->new_group(std::move(event_cards));
pgroup->is_readonly = true;
new_event.event_cards = pgroup;
new_event.event_code = event_code;
new_event.reason_effect = reason_effect;
new_event.reason = reason;
new_event.reason_player = reason_player;
new_event.event_player = event_player;
new_event.event_value = event_value;
new_event.global_id = infos.event_id;
}
void field::raise_single_event(card* trigger_card, card_set* event_cards, uint32_t event_code, effect* reason_effect, uint32_t reason, uint8_t reason_player, uint8_t event_player, uint32_t event_value) {
auto& new_event = core.single_event.emplace_back();
new_event.trigger_card = trigger_card;
if (event_cards) {
auto pgroup = pduel->new_group(*event_cards);
pgroup->is_readonly = true;
new_event.event_cards = pgroup;
} else
new_event.event_cards = nullptr;
new_event.event_code = event_code;
new_event.reason_effect = reason_effect;
new_event.reason = reason;
new_event.reason_player = reason_player;
new_event.event_player = event_player;
new_event.event_value = event_value;
new_event.global_id = infos.event_id;
}
int32_t field::check_event(uint32_t code, tevent* pe) {
for(const auto& ev : core.point_event) {
if(ev.event_code == code) {
if(pe)
*pe = ev;
return TRUE;
}
}
for(const auto& ev : core.instant_event) {
if(ev.event_code == code) {
if(pe)
*pe = ev;
return TRUE;
}
}
return FALSE;
}
int32_t field::check_event_c(effect* peffect, uint8_t playerid, int32_t neglect_con, int32_t neglect_cost, int32_t copy_info, tevent* pe) {
if(peffect->code == EVENT_FREE_CHAIN) {
return peffect->is_activate_ready(core.reason_effect, playerid, nil_event, neglect_con, neglect_cost, FALSE);
}
for(const auto& ev : core.point_event) {
if(ev.event_code == peffect->code &&
peffect->is_activate_ready(core.reason_effect, playerid, ev, neglect_con, neglect_cost, FALSE)) {
if(pe)
*pe = ev;
if(copy_info && !pduel->lua->no_action && core.current_chain.size()) {
core.current_chain.back().evt = ev;
}
return TRUE;
}
}
for(const auto& ev : core.instant_event) {
if(ev.event_code == peffect->code &&
peffect->is_activate_ready(core.reason_effect, playerid, ev, neglect_con, neglect_cost, FALSE)) {
if(pe)
*pe = ev;
if(copy_info && !pduel->lua->no_action && core.current_chain.size()) {
core.current_chain.back().evt = ev;
}
return TRUE;
}
}
return FALSE;
}
int32_t field::check_hint_timing(effect* peffect) {
int32_t p = peffect->get_handler_player();
if(p == 0)
return (peffect->hint_timing[0] & core.hint_timing[0]) || (peffect->hint_timing[1] & core.hint_timing[1]);
else
return (peffect->hint_timing[0] & core.hint_timing[1]) || (peffect->hint_timing[1] & core.hint_timing[0]);
}
// core.spe_effect[p]: # of optional trigger effects, activate/quick effect with hints
bool field::process(Processors::PhaseEvent& arg) {
auto phase = arg.phase;
switch(arg.step) {
case 0: {
if((phase == PHASE_DRAW && (core.force_turn_end || is_player_affected_by_effect(infos.turn_player, EFFECT_SKIP_DP)))
|| (phase == PHASE_STANDBY && (core.force_turn_end || is_player_affected_by_effect(infos.turn_player, EFFECT_SKIP_SP)))
|| (phase == PHASE_BATTLE_START && (core.force_turn_end || is_player_affected_by_effect(infos.turn_player, EFFECT_SKIP_BP)))
|| (phase == PHASE_BATTLE && (core.force_turn_end || is_player_affected_by_effect(infos.turn_player, EFFECT_SKIP_BP)))
|| (phase == PHASE_END && (core.force_turn_end || is_player_affected_by_effect(infos.turn_player, EFFECT_SKIP_EP)))) {
arg.step = 24;
return FALSE;
}
int32_t phase_event = EVENT_PHASE + phase;
nil_event.event_code = phase_event;
nil_event.event_player = infos.turn_player;
int32_t check_player = infos.turn_player;
if(arg.is_opponent)
check_player = 1 - infos.turn_player;
core.select_chains.clear();
int32_t tf_count = 0, to_count = 0, fc_count = 0, cn_count = 0;
auto pr = effects.trigger_f_effect.equal_range(phase_event);
for(auto eit = pr.first; eit != pr.second;) {
effect* peffect = eit->second;
++eit;
peffect->set_activate_location();
if(!peffect->is_activateable(check_player, nil_event))
continue;
peffect->id = infos.field_id++;
core.select_chains.emplace_back().triggering_effect = peffect;
++tf_count;
}
pr = effects.continuous_effect.equal_range(phase_event);
for(auto eit = pr.first; eit != pr.second;) {
effect* peffect = eit->second;
++eit;
//effects.continuous_effect may be changed in is_activateable (e.g. Rescue Cat)
if(peffect->get_handler_player() != check_player || !peffect->is_activateable(check_player, nil_event))
continue;
peffect->id = infos.field_id++;
core.select_chains.emplace_back().triggering_effect = peffect;
++cn_count;
}
//all effects taking control non-permanently are only until End Phase, not until Turn end
for(auto* peffect : effects.pheff) {
if(peffect->code != EFFECT_SET_CONTROL)
continue;
if(!(peffect->reset_flag & phase))
continue;
uint8_t pid = peffect->get_handler_player();
if(pid != check_player)
continue;
uint8_t tp = infos.turn_player;
if(!(((peffect->reset_flag & RESET_SELF_TURN) && pid == tp) || ((peffect->reset_flag & RESET_OPPO_TURN) && pid != tp)))
continue;
if(peffect->reset_count != 1)
continue;
card* phandler = peffect->get_handler();
if(peffect->get_value(phandler) != phandler->current.controler)
continue;
core.select_chains.emplace_back().triggering_effect = peffect;
++cn_count;
}
core.spe_effect[check_player] = 0;
if(!core.hand_adjusted) {
pr = effects.trigger_o_effect.equal_range(phase_event);
for(auto eit = pr.first; eit != pr.second;) {
effect* peffect = eit->second;
++eit;
peffect->set_activate_location();
if(!peffect->is_activateable(check_player, nil_event))
continue;
peffect->id = infos.field_id++;
core.select_chains.emplace_back().triggering_effect = peffect;
++to_count;
++core.spe_effect[check_player];
}
if(phase == PHASE_DRAW)
core.hint_timing[infos.turn_player] = TIMING_DRAW_PHASE;
else if(phase == PHASE_STANDBY)
core.hint_timing[infos.turn_player] = TIMING_STANDBY_PHASE;
else if(phase == PHASE_BATTLE_START)
core.hint_timing[infos.turn_player] = TIMING_BATTLE_START;
else if(phase == PHASE_BATTLE)
core.hint_timing[infos.turn_player] = TIMING_BATTLE_END;
else
core.hint_timing[infos.turn_player] = TIMING_END_PHASE;
pr = effects.activate_effect.equal_range(EVENT_FREE_CHAIN);
for(auto eit = pr.first; eit != pr.second;) {
effect* peffect = eit->second;
++eit;
peffect->set_activate_location();
if(!peffect->is_chainable(check_player) || !peffect->is_activateable(check_player, nil_event))
continue;
peffect->id = infos.field_id++;
core.select_chains.emplace_back().triggering_effect = peffect;
if(check_hint_timing(peffect) || check_cteffect_hint(peffect, check_player))
++core.spe_effect[check_player];
++fc_count;
}
pr = effects.quick_o_effect.equal_range(EVENT_FREE_CHAIN);
for(auto eit = pr.first; eit != pr.second;) {
effect* peffect = eit->second;
++eit;
peffect->set_activate_location();
if(!peffect->is_chainable(check_player) || !peffect->is_activateable(check_player, nil_event))
continue;
peffect->id = infos.field_id++;
core.select_chains.emplace_back().triggering_effect = peffect;
if(check_hint_timing(peffect))
++core.spe_effect[check_player];
++fc_count;
}
pr = effects.continuous_effect.equal_range(EVENT_FREE_CHAIN);
for(auto eit = pr.first; eit != pr.second;) {
effect* peffect = eit->second;
++eit;
if(peffect->get_handler_player() != check_player || !peffect->is_activateable(check_player, nil_event))
continue;
peffect->id = infos.field_id++;
core.select_chains.emplace_back().triggering_effect = peffect;
++fc_count;
}
}
if(core.select_chains.size() == 0) {
returns.set<int32_t>(0, -1);
arg.step = 1;
return FALSE;
} else if(tf_count == 0 && cn_count == 1 && to_count == 0 && fc_count == 0) {
returns.set<int32_t>(0, 0);
arg.step = 1;
return FALSE;
} else {
auto message = pduel->new_message(MSG_HINT);
message->write<uint8_t>(HINT_EVENT);
message->write<uint8_t>(check_player);
if(infos.phase == PHASE_DRAW)
message->write<uint64_t>(20);
else if(infos.phase == PHASE_STANDBY)
message->write<uint64_t>(21);
else if(infos.phase == PHASE_BATTLE_START)
message->write<uint64_t>(28);
else if(infos.phase == PHASE_BATTLE)
message->write<uint64_t>(25);
else
message->write<uint64_t>(26);
if(tf_count == 0 && to_count == 1 && fc_count == 0 && cn_count == 0) {
emplace_process<Processors::SelectEffectYesNo>(check_player, 0, core.select_chains.front().triggering_effect->get_handler());
return FALSE;
} else {
emplace_process<Processors::SelectChain>(check_player, core.spe_effect[check_player], (tf_count + cn_count) > 0);
arg.step = 1;
return FALSE;
}
}
return FALSE;
}
case 1: {
returns.set<int32_t>(0, returns.at<int32_t>(0) - 1);
return FALSE;
}
case 2: {
if(returns.at<int32_t>(0) == -1) {
if(arg.priority_passed)
arg.step = 19;
else {
arg.priority_passed = true;
arg.is_opponent = !arg.is_opponent;
arg.step = Processors::restart;
}
return FALSE;
}
arg.priority_passed = false;
auto selected_chain = std::next(core.select_chains.begin(), returns.at<int32_t>(0));
effect* peffect = selected_chain->triggering_effect;
card* phandler = peffect->get_handler();
if(!(peffect->type & EFFECT_TYPE_ACTIONS)) {
if(peffect->is_flag(EFFECT_FLAG_FIELD_ONLY))
remove_effect(peffect);
else
peffect->handler->remove_effect(peffect);
adjust_all();
arg.step = 3;
} else if(!(peffect->type & EFFECT_TYPE_CONTINUOUS)) {
int32_t check_player = infos.turn_player;
if(arg.is_opponent)
check_player = 1 - infos.turn_player;
core.new_chains.splice(core.new_chains.end(), core.select_chains, selected_chain);
selected_chain->flag = 0;
selected_chain->chain_id = infos.field_id++;
selected_chain->evt = nil_event;
selected_chain->set_triggering_state(phandler);
selected_chain->triggering_player = check_player;
phandler->set_status(STATUS_CHAINING, TRUE);
peffect->dec_count(check_player);
core.select_chains.clear();
emplace_process<Processors::AddChain>();
if(is_flag(DUEL_INVERTED_QUICK_PRIORITY))
emplace_process<Processors::QuickEffect>(false, check_player);
else
emplace_process<Processors::QuickEffect>(false, 1 - check_player);
infos.priorities[0] = 0;
infos.priorities[1] = 0;
} else {
core.select_chains.clear();
solve_continuous(peffect->get_handler_player(), peffect, nil_event);
arg.step = 3;
}
return FALSE;
}
case 3: {
for(auto& ch_lim : core.chain_limit)
ensure_luaL_stack(luaL_unref, pduel->lua->lua_state, LUA_REGISTRYINDEX, ch_lim.function);
core.chain_limit.clear();
for(auto& ch : core.current_chain)
ch.triggering_effect->get_handler()->set_status(STATUS_CHAINING, FALSE);
emplace_process<Processors::SolveChain>(false, false, false);
arg.step = Processors::restart;
return FALSE;
}
case 4: {
adjust_instant();
emplace_process<Processors::PointEvent>(false, false, false);
arg.step = Processors::restart;
return FALSE;
}
case 20: {
if(phase != PHASE_END) {
arg.step = 24;
return FALSE;
}
int32_t limit = 6;
effect_set eset;
filter_player_effect(infos.turn_player, EFFECT_HAND_LIMIT, &eset);
if(eset.size())
limit = eset.back()->get_value();
int32_t hd = static_cast<int32_t>(player[infos.turn_player].list_hand.size());
if(hd <= limit || is_flag(DUEL_NO_HAND_LIMIT)) {
arg.step = 24;
return FALSE;
}
core.select_cards.clear();
for(auto& pcard : player[infos.turn_player].list_hand)
core.select_cards.push_back(pcard);
auto message = pduel->new_message(MSG_HINT);
message->write<uint8_t>(HINT_SELECTMSG);
message->write<uint8_t>(infos.turn_player);
message->write<uint64_t>(501);
auto to_discard = hd - limit;
emplace_process<Processors::SelectCard>(infos.turn_player, false, to_discard, to_discard);
return FALSE;
}
case 21: {
if(return_cards.list.size())
send_to(card_set{ return_cards.list.begin(), return_cards.list.end() }, nullptr, REASON_RULE + REASON_DISCARD + REASON_ADJUST, infos.turn_player, PLAYER_NONE, LOCATION_GRAVE, 0, POS_FACEUP);
return FALSE;
}
case 22: {
core.hand_adjusted = true;
emplace_process<Processors::PointEvent>(false, false, false);
arg.step = Processors::restart;
arg.is_opponent = false;
arg.priority_passed = false;
return FALSE;
}
case 25: {
core.hint_timing[infos.turn_player] = 0;
reset_phase(phase);
adjust_all();
return FALSE;
}
case 26: {
core.quick_f_chain.clear();
core.instant_event.clear();
core.point_event.clear();
core.delayed_activate_event.clear();
core.full_event.clear();
/*if(core.set_forced_attack) {
core.set_forced_attack = false;
emplace_process<Processors::ForcedBattle>();
}*/
return TRUE;
}
}
/*if(core.set_forced_attack) {
core.set_forced_attack = false;
emplace_process<Processors::ForcedBattle>();
}*/
return TRUE;
}
// core.ignition_priority_chains: used in step 8 (obsolete ignition effect ruling)
bool field::process(Processors::PointEvent& arg) {
auto skip_trigger = arg.skip_trigger;
auto skip_freechain = arg.skip_freechain;
auto skip_new = arg.skip_new;
switch(arg.step) {
case 0: {
core.select_chains.clear();
core.point_event.splice(core.point_event.end(), core.instant_event);
if(skip_trigger) {
arg.step = 7;
return FALSE;
}
core.new_fchain_s.splice(core.new_fchain_s.begin(), core.new_fchain);
core.new_ochain_s.splice(core.new_ochain_s.begin(), core.new_ochain);
core.full_event.splice(core.full_event.end(), core.delayed_activate_event);
core.delayed_quick.clear();
core.delayed_quick_tmp.swap(core.delayed_quick);
core.current_player = infos.turn_player;
arg.step = 1;
return FALSE;
}
case 1: {
return FALSE;
}
case 2: {
//forced trigger
core.select_chains.clear();
for (auto clit = core.new_fchain_s.begin(); clit != core.new_fchain_s.end(); ) {
effect* peffect = clit->triggering_effect;
card* phandler = peffect->get_handler();
auto update_triggering_state = [&, updated_state = false]() mutable {
if(std::exchange(updated_state, true))
return;
clit->set_triggering_state(phandler);
};
if(phandler->is_has_relation(*clit)) //work around: position and control should be refreshed before raising event
update_triggering_state();
if(clit->triggering_player != phandler->current.controler && !peffect->is_flag(EFFECT_FLAG_EVENT_PLAYER)) {
clit->triggering_player = phandler->current.controler;
update_triggering_state();
}
uint8_t tp = clit->triggering_player;
if((!clit->was_just_sent || phandler->current.location == LOCATION_HAND)
&& check_trigger_effect(*clit)
&& peffect->is_chainable(tp)
&& peffect->is_activateable(tp, clit->evt, TRUE, FALSE, FALSE, is_flag(DUEL_TRIGGER_WHEN_PRIVATE_KNOWLEDGE), is_flag(DUEL_TRIGGER_WHEN_PRIVATE_KNOWLEDGE))) {
if(tp == core.current_player)
core.select_chains.push_back(*clit);
} else {
peffect->active_type = 0;
core.new_fchain_s.erase(clit++);
continue;
}
++clit;
}
//TCG SEGOC
if(core.select_chains.size() == 0) {
returns.set<int32_t>(0, -1);
} else {
if(is_flag(DUEL_TCG_SEGOC_FIRSTTRIGGER)) { //tcg segoc
core.select_chains.sort([](const chain& c1, const chain& c2) {return c1.event_id < c2.event_id; });
auto pred = [id = core.select_chains.front().event_id](const chain& ch) {
return ch.event_id == id;
};
auto endit = std::find_if_not(core.select_chains.begin(), core.select_chains.end(), pred);
core.select_chains.erase(endit, core.select_chains.end());
}
if(core.select_chains.size() == 1) {
returns.set<int32_t>(0, 0);
} else {
emplace_process<Processors::SelectChain>(core.current_player, 0x7f, true);
}
}
return FALSE;
}
case 3: {
if(returns.at<int32_t>(0) == -1) {
if(core.new_fchain_s.size()) {
core.current_player = 1 - infos.turn_player;
arg.step = 1;
} else {
core.current_player = infos.turn_player;
}
return FALSE;
}
auto newchain = std::next(core.select_chains.begin(), returns.at<int32_t>(0));
effect* peffect = newchain->triggering_effect;
uint8_t tp = newchain->triggering_player;
peffect->get_handler()->set_status(STATUS_CHAINING, TRUE);
peffect->dec_count(tp);
auto chain_id = newchain->chain_id;
core.new_chains.splice(core.new_chains.end(), core.select_chains, newchain);
emplace_process<Processors::AddChain>();
core.new_fchain_s.remove_if([chain_id](const chain& ch) { return ch.chain_id == chain_id; });
arg.step = 1;
return FALSE;
}
case 4: {
//optional trigger
core.select_chains.clear();
for (auto clit = core.new_ochain_s.begin(); clit != core.new_ochain_s.end(); ) {
effect* peffect = clit->triggering_effect;
card* phandler = peffect->get_handler();
auto update_triggering_state = [&, updated_state = false]() mutable {
if(std::exchange(updated_state, true))
return;
clit->set_triggering_state(phandler);
};
if(phandler->is_has_relation(*clit)) //work around: position and control should be refreshed before raising event
update_triggering_state();
if(!peffect->is_flag(EFFECT_FLAG_FIELD_ONLY) && (peffect->type & EFFECT_TYPE_FIELD)
&& (peffect->range & LOCATION_HAND) && phandler->current.location == LOCATION_HAND) {
if(!phandler->is_has_relation(*clit) && peffect->is_condition_check(phandler->current.controler, clit->evt))
phandler->create_relation(*clit);
peffect->set_activate_location();
clit->triggering_player = phandler->current.controler;
update_triggering_state();
}
if(clit->triggering_player != phandler->current.controler && !peffect->is_flag(EFFECT_FLAG_EVENT_PLAYER)) {
clit->triggering_player = phandler->current.controler;
update_triggering_state();
}
uint8_t tp = clit->triggering_player;
if((!clit->was_just_sent || phandler->current.location == LOCATION_HAND)
&& (is_flag(DUEL_TRIGGER_WHEN_PRIVATE_KNOWLEDGE) || check_nonpublic_trigger(*clit))
&& check_trigger_effect(*clit)
&& peffect->is_chainable(tp)
&& peffect->is_activateable(tp, clit->evt, TRUE, FALSE, FALSE, is_flag(DUEL_TRIGGER_WHEN_PRIVATE_KNOWLEDGE), is_flag(DUEL_TRIGGER_WHEN_PRIVATE_KNOWLEDGE))
&& check_spself_from_hand_trigger(*clit)) {
if(tp == core.current_player)
core.select_chains.push_back(*clit);
} else {
peffect->active_type = 0;
core.new_ochain_s.erase(clit++);
continue;
}
++clit;
}
//TCG SEGOC
if(core.select_chains.size() == 0) {
returns.set<int32_t>(0, -2);
arg.step = 5;
return FALSE;
} else {
if(is_flag(DUEL_TCG_SEGOC_NONPUBLIC))
core.new_ochain_h.clear();
//tcg segoc
if(is_flag(DUEL_TCG_SEGOC_FIRSTTRIGGER)) {
core.select_chains.sort([](const chain& c1, const chain& c2) {return c1.event_id < c2.event_id; });
auto pred = [id = core.select_chains.front().event_id](const chain& ch) {
return ch.event_id == id;
};
auto endit = std::find_if_not(core.select_chains.begin(), core.select_chains.end(), pred);
core.select_chains.erase(endit, core.select_chains.end());
}
if(core.select_chains.size() == 1 && !core.current_chain.size()) {
emplace_process<Processors::SelectEffectYesNo>(core.current_player, 221, core.select_chains.front().triggering_effect->get_handler());
return FALSE;
} else {
emplace_process<Processors::SelectChain>(core.current_player, 0x7f, false);
arg.step = 5;
return FALSE;
}
}
return FALSE;
}
case 5: {
returns.set<int32_t>(0, returns.at<int32_t>(0) - 1);
return FALSE;
}
case 6: {
const auto ret = returns.at<int32_t>(0);
if(ret == -2 || (ret == -1 && !is_flag(DUEL_TCG_SEGOC_FIRSTTRIGGER))) {
for(const auto& ch : core.select_chains) {
ch.triggering_effect->active_type = 0;
core.new_ochain_s.remove_if([chain_id = ch.chain_id](const chain& ch) { return ch.chain_id == chain_id; });
}
if(core.new_ochain_s.size()) {
core.current_player = 1 - infos.turn_player;
arg.step = 3;
} else {
core.current_player = infos.turn_player;
arg.step = 6;
}
return FALSE;
} else if(ret == -1) {
//TCG SEGOC
auto discardedid = core.select_chains.front().event_id;
core.new_ochain_s.remove_if([&](const chain& ch) { return ch.event_id == discardedid && ch.triggering_player == core.current_player; });
arg.step = 3;
return FALSE;
}
auto newchain = std::next(core.select_chains.begin(), returns.at<int32_t>(0));
effect* peffect = newchain->triggering_effect;
uint8_t tp = newchain->triggering_player;
peffect->get_handler()->set_status(STATUS_CHAINING, TRUE);
peffect->dec_count(tp);
auto chain_id = newchain->chain_id;
core.new_chains.splice(core.new_chains.end(), core.select_chains, newchain);
emplace_process<Processors::AddChain>();
core.new_ochain_s.remove_if([chain_id](const chain& ch) { return ch.chain_id == chain_id; });
core.new_ochain_h.remove_if([chain_id](const chain& ch) { return ch.chain_id == chain_id; });
arg.step = 3;
return FALSE;
}
case 7: {
core.select_chains.clear();
return FALSE;
}
case 8: {
if(skip_freechain || !(is_flag(DUEL_OCG_OBSOLETE_IGNITION) || is_flag(DUEL_TCG_FAST_EFFECT_IGNITION)) || (infos.phase != PHASE_MAIN1 && infos.phase != PHASE_MAIN2))
return FALSE;
// Obsolete ignition effect ruling
auto check_events_ocg = [&] {
if(check_event(EVENT_CHAIN_END))
return true;
tevent _e;
if(!check_event(EVENT_SUMMON_SUCCESS, &_e) && !check_event(EVENT_SPSUMMON_SUCCESS, &_e) &&
!check_event(EVENT_FLIP_SUMMON_SUCCESS, &_e))
return false;
return _e.reason_player == infos.turn_player;
};
if(core.current_chain.size() == 0 && (is_flag(DUEL_TCG_FAST_EFFECT_IGNITION) || check_events_ocg())) {
chain newchain;
{
newchain.evt.event_cards = nullptr;
newchain.evt.event_value = 0;
newchain.evt.event_player = PLAYER_NONE;
newchain.evt.reason_effect = nullptr;
newchain.evt.reason = 0;
newchain.evt.reason_player = PLAYER_NONE;
}
newchain.flag = 0;
newchain.triggering_player = infos.turn_player;
for(auto eit = effects.ignition_effect.begin(); eit != effects.ignition_effect.end();) {
effect* peffect = eit->second;
++eit;
card* phandler = peffect->get_handler();
newchain.evt.event_code = peffect->code;
if((is_flag(DUEL_TCG_FAST_EFFECT_IGNITION) || phandler->current.location == LOCATION_MZONE)
&& peffect->is_chainable(infos.turn_player)
&& peffect->is_activateable(infos.turn_player, newchain.evt)) {
newchain.chain_id = infos.field_id++;
newchain.triggering_effect = peffect;
newchain.set_triggering_state(phandler);
core.ignition_priority_chains.push_back(newchain);
}
}
}
return FALSE;
}
case 9: {
infos.priorities[0] = 0;
infos.priorities[1] = 0;
if(core.current_chain.size() == 0) {
if(!core.hand_adjusted) {
if(is_flag(DUEL_INVERTED_QUICK_PRIORITY))
emplace_process<Processors::QuickEffect>(skip_freechain, 1 - infos.turn_player);
else
emplace_process<Processors::QuickEffect>(skip_freechain, infos.turn_player);
}
} else
emplace_process<Processors::QuickEffect>(skip_freechain, 1 - core.current_chain.back().triggering_player);
return FALSE;
}
case 10: {
core.new_ochain_h.clear();
core.full_event.clear();
core.delayed_quick.clear();
for(auto& ch_lim : core.chain_limit)
ensure_luaL_stack(luaL_unref, pduel->lua->lua_state, LUA_REGISTRYINDEX, ch_lim.function);
core.chain_limit.clear();
if(core.current_chain.size()) {
for(auto& ch : core.current_chain)
ch.triggering_effect->get_handler()->set_status(STATUS_CHAINING, FALSE);
emplace_process<Processors::SolveChain>(skip_trigger, skip_freechain, skip_new);
} else {
core.used_event.splice(core.used_event.end(), core.point_event);
reset_chain();
returns.set<int32_t>(0, FALSE);
}
if(core.set_forced_attack) {
core.set_forced_attack = false;
emplace_process<Processors::ForcedBattle>();
}
return TRUE;
}
case 30: {
int32_t check_player = infos.turn_player;
nil_event.event_code = EVENT_FREE_CHAIN;
core.select_chains.clear();
core.spe_effect[check_player] = 0;
auto pr = effects.continuous_effect.equal_range(EVENT_FREE_CHAIN);
for(auto eit = pr.first; eit != pr.second;) {
effect* peffect = eit->second;
++eit;
if(peffect->get_handler_player() == check_player && peffect->is_activateable(check_player, nil_event)) {
core.select_chains.emplace_back().triggering_effect = peffect;
++core.spe_effect[check_player];
}
}
if(!core.select_chains.empty())
emplace_process<Processors::SelectChain>(check_player, core.spe_effect[check_player], false);
else
arg.step = 31;
return FALSE;
}
case 31: {
if(returns.at<int32_t>(0) == -1)
return FALSE;
auto newchain = std::next(core.select_chains.begin(), returns.at<int32_t>(0));
effect* peffect = newchain->triggering_effect;
core.select_chains.clear();
solve_continuous(peffect->get_handler_player(), peffect, nil_event);
arg.step = 29;
return FALSE;
}
case 32: {
int32_t check_player = 1 - infos.turn_player;
nil_event.event_code = EVENT_FREE_CHAIN;
core.select_chains.clear();
core.spe_effect[check_player] = 0;
auto pr = effects.continuous_effect.equal_range(EVENT_FREE_CHAIN);
for(auto eit = pr.first; eit != pr.second;) {
effect* peffect = eit->second;
++eit;
if(peffect->get_handler_player() == check_player && peffect->is_activateable(check_player, nil_event)) {
core.select_chains.emplace_back().triggering_effect = peffect;
++core.spe_effect[check_player];
}
}
if(!core.select_chains.empty())
emplace_process<Processors::SelectChain>(check_player, core.spe_effect[check_player], false);
else
arg.step = Processors::restart;
return FALSE;
}
case 33: {
if(returns.at<int32_t>(0) == -1) {
arg.step = Processors::restart;
return FALSE;
}
auto newchain = std::next(core.select_chains.begin(), returns.at<int32_t>(0));
effect* peffect = newchain->triggering_effect;
core.select_chains.clear();
solve_continuous(peffect->get_handler_player(), peffect, nil_event);
arg.step = 31;
return FALSE;
}
}
if(core.set_forced_attack) {
core.set_forced_attack = false;
emplace_process<Processors::ForcedBattle>();
}
return TRUE;
}
bool field::process(Processors::QuickEffect& arg) {
auto skip_freechain = arg.skip_freechain;
auto priority = arg.priority_player;
switch(arg.step) {
case 0: {
uint8_t check_player = infos.turn_player;
if(arg.is_opponent)
check_player = 1 - infos.turn_player;
core.select_chains.clear();
for(auto ifit = core.quick_f_chain.begin(); ifit != core.quick_f_chain.end(); ) {
effect* peffect = ifit->first;
card* phandler = peffect->get_handler();
if(peffect->is_chainable(ifit->second.triggering_player) && peffect->check_count_limit(ifit->second.triggering_player)
&& phandler->is_has_relation(ifit->second)) {
if(ifit->second.triggering_player == check_player)
core.select_chains.push_back(ifit->second);
} else {
core.quick_f_chain.erase(ifit++);
continue;
}
++ifit;
}
if(core.select_chains.size() == 0)
returns.set<int32_t>(0, -1);
else if(core.select_chains.size() == 1)
returns.set<int32_t>(0, 0);
else
emplace_process<Processors::SelectChain>(check_player, 0, true);
return FALSE;
}
case 1: {
if(returns.at<int32_t>(0) == -1) {
if(core.quick_f_chain.size()) {
arg.is_opponent = true;
arg.step = Processors::restart;
} else if(core.new_chains.size()) {
emplace_process<Processors::AddChain>();
emplace_process<Processors::QuickEffect>(false, 1 - core.new_chains.back().triggering_player);
infos.priorities[0] = 0;
infos.priorities[1] = 0;
/*if(core.set_forced_attack) {
core.set_forced_attack = false;
emplace_process<Processors::ForcedBattle>();
}*/
return TRUE;
}
return FALSE;
}
auto newchain = std::next(core.select_chains.begin(), returns.at<int32_t>(0));
effect* peffect = newchain->triggering_effect;
uint8_t tp = newchain->triggering_player;
peffect->get_handler()->set_status(STATUS_CHAINING, TRUE);
peffect->dec_count(tp);
core.new_chains.splice(core.new_chains.end(), core.select_chains, newchain);
core.quick_f_chain.erase(peffect);
arg.step = Processors::restart;
return FALSE;
}
case 2: {
if(core.ignition_priority_chains.size())
core.select_chains.swap(core.ignition_priority_chains);
for(auto& event : { &core.point_event , &core.instant_event }) {
for(auto evit = event->begin(); evit != event->end(); ++evit) {
auto pr = effects.activate_effect.equal_range(evit->event_code);
for(auto eit = pr.first; eit != pr.second;) {
effect* peffect = eit->second;
++eit;
peffect->set_activate_location();
if(!peffect->is_flag(EFFECT_FLAG_DELAY) && peffect->is_chainable(priority) && peffect->is_activateable(priority, *evit)) {
card* phandler = peffect->get_handler();
auto& newchain = core.select_chains.emplace_back();
newchain.flag = 0;
newchain.chain_id = infos.field_id++;
newchain.evt = *evit;
newchain.triggering_effect = peffect;
newchain.set_triggering_state(phandler);
newchain.triggering_player = priority;
}
}
pr = effects.quick_o_effect.equal_range(evit->event_code);
for(auto eit = pr.first; eit != pr.second;) {
effect* peffect = eit->second;
++eit;
peffect->set_activate_location();
if(!peffect->is_flag(EFFECT_FLAG_DELAY) && peffect->is_chainable(priority) && peffect->is_activateable(priority, *evit)) {
card* phandler = peffect->get_handler();
auto& newchain = core.select_chains.emplace_back();
newchain.flag = 0;
newchain.chain_id = infos.field_id++;
newchain.evt = *evit;
newchain.triggering_effect = peffect;
newchain.set_triggering_state(phandler);
newchain.triggering_player = priority;
}
}
}
}
for(auto& ch : core.new_ochain_h) {
effect* peffect = ch.triggering_effect;
card* phandler = peffect->get_handler();
if(!peffect->is_flag(EFFECT_FLAG_FIELD_ONLY) && (peffect->type & EFFECT_TYPE_FIELD)
&& (peffect->range & LOCATION_HAND) && phandler->current.location == LOCATION_HAND) {
if(!phandler->is_has_relation(ch) && peffect->is_condition_check(phandler->current.controler, ch.evt))
phandler->create_relation(ch);
peffect->set_activate_location();
ch.triggering_player = phandler->current.controler;
ch.set_triggering_state(phandler);
}
if(ch.triggering_player == priority && !phandler->is_status(STATUS_CHAINING)
&& ((ch.triggering_location == LOCATION_HAND && phandler->is_position(POS_FACEDOWN)) || ch.triggering_location == LOCATION_DECK)
&& phandler->is_has_relation(ch) && peffect->is_chainable(priority) && peffect->is_activateable(priority, ch.evt, TRUE)
&& check_spself_from_hand_trigger(ch))
core.select_chains.push_back(ch);
}
//delayed activate
for(const auto& ev : core.full_event) {
auto pr = effects.activate_effect.equal_range(ev.event_code);
for(auto eit = pr.first; eit != pr.second;) {
effect* peffect = eit->second;
++eit;
peffect->set_activate_location();
if(peffect->is_flag(EFFECT_FLAG_DELAY) && peffect->is_chainable(priority) && peffect->is_activateable(priority, ev)) {
card* phandler = peffect->get_handler();
auto& newchain = core.select_chains.emplace_back();
newchain.flag = 0;
newchain.chain_id = infos.field_id++;
newchain.evt = ev;
newchain.triggering_effect = peffect;
newchain.set_triggering_state(phandler);
newchain.triggering_player = priority;
}
}
}
// delayed quick
for(auto eit = core.delayed_quick.begin(); eit != core.delayed_quick.end();) {
effect* peffect = eit->first;
const tevent& evt = eit->second;
++eit;
peffect->set_activate_location();
if(peffect->is_chainable(priority) && peffect->is_activateable(priority, evt, TRUE, FALSE, FALSE)) {
card* phandler = peffect->get_handler();
auto& newchain = core.select_chains.emplace_back();
newchain.flag = 0;
newchain.chain_id = infos.field_id++;
newchain.evt = evt;
newchain.triggering_effect = peffect;
newchain.set_triggering_state(phandler);
newchain.triggering_player = priority;
}
}
core.spe_effect[priority] = static_cast<int32_t>(core.select_chains.size());
if(!skip_freechain) {
nil_event.event_code = EVENT_FREE_CHAIN;
auto pr = effects.activate_effect.equal_range(EVENT_FREE_CHAIN);
for(auto eit = pr.first; eit != pr.second;) {
effect* peffect = eit->second;
++eit;
peffect->set_activate_location();
if(peffect->is_chainable(priority) && peffect->is_activateable(priority, nil_event)) {
card* phandler = peffect->get_handler();
auto& newchain = core.select_chains.emplace_back();
newchain.flag = 0;
newchain.chain_id = infos.field_id++;
newchain.evt = nil_event;
newchain.triggering_effect = peffect;
newchain.set_triggering_state(phandler);
newchain.triggering_player = priority;
if(check_hint_timing(peffect) || check_cteffect_hint(peffect, priority))
++core.spe_effect[priority];
}
}
pr = effects.quick_o_effect.equal_range(EVENT_FREE_CHAIN);
for(auto eit = pr.first; eit != pr.second;) {
effect* peffect = eit->second;
++eit;
peffect->set_activate_location();
if(peffect->is_chainable(priority) && peffect->is_activateable(priority, nil_event)) {
card* phandler = peffect->get_handler();
auto& newchain = core.select_chains.emplace_back();
newchain.flag = 0;
newchain.chain_id = infos.field_id++;
newchain.evt = nil_event;
newchain.triggering_effect = peffect;
newchain.set_triggering_state(phandler);
newchain.triggering_player = priority;
if(check_hint_timing(peffect))
++core.spe_effect[priority];
}
}
}
if(core.current_chain.size() || (core.hint_timing[0] & TIMING_ATTACK) || (core.hint_timing[1] & TIMING_ATTACK))
core.spe_effect[priority] = static_cast<int32_t>(core.select_chains.size());
emplace_process<Processors::SelectChain>(priority, core.spe_effect[priority], false);
return FALSE;
}
case 3: {
if(core.select_chains.size() && returns.at<int32_t>(0) != -1) {
auto newchain = std::next(core.select_chains.begin(), returns.at<int32_t>(0));
effect* peffect = newchain->triggering_effect;
core.delayed_quick.erase(std::make_pair(peffect, newchain->evt));
core.new_chains.splice(core.new_chains.end(), core.select_chains, newchain);
peffect->get_handler()->set_status(STATUS_CHAINING, TRUE);
peffect->dec_count(priority);
emplace_process<Processors::AddChain>();
emplace_process<Processors::QuickEffect>(false, 1 - priority);
infos.priorities[0] = 0;
infos.priorities[1] = 0;
} else {
infos.priorities[priority] = 1;
if(!infos.priorities[0] || !infos.priorities[1])
emplace_process<Processors::QuickEffect>(Step{ 1 }, skip_freechain, 1 - priority);
else {
core.hint_timing[0] &= TIMING_DAMAGE_STEP | TIMING_DAMAGE_CAL;
core.hint_timing[1] &= TIMING_DAMAGE_STEP | TIMING_DAMAGE_CAL;
}
}
core.select_chains.clear();
/*if(core.set_forced_attack) {
core.set_forced_attack = false;
emplace_process<Processors::ForcedBattle>();
}*/
return TRUE;
}
}
/*if(core.set_forced_attack) {
core.set_forced_attack = false;
emplace_process<Processors::ForcedBattle>();
}*/
return TRUE;
}
int32_t field::process_instant_event() {
auto check_simul = [&just_sent=core.just_sent_cards](effect* peffect, card* phandler) {
return (peffect->flag[1] & EFFECT_FLAG2_CHECK_SIMULTANEOUS) != 0 && just_sent.find(phandler) != just_sent.end();
};
if(core.queue_event.size() == 0) {
/*if(core.set_forced_attack)
emplace_process<Processors::ForcedBattle>();*/
return TRUE;
}
chain_list tp;
chain_list ntp;
for(const auto& ev : core.queue_event) {
//continuous events
auto pr = effects.continuous_effect.equal_range(ev.event_code);
for(auto eit = pr.first; eit != pr.second;) {
effect* peffect = eit->second;
++eit;
uint8_t owner_player = peffect->get_handler_player();
if(peffect->is_flag(EFFECT_FLAG_EVENT_PLAYER) && (ev.event_player == 0 || ev.event_player == 1))
owner_player = ev.event_player;
if(peffect->is_activateable(owner_player, ev)) {
auto& chain_set = [&]() -> chain_list& {
if(peffect->is_flag(EFFECT_FLAG_DELAY) && (core.chain_solving || core.conti_solving)) {
if(owner_player == infos.turn_player)
return core.delayed_continuous_tp;
return core.delayed_continuous_ntp;
}
if(owner_player == infos.turn_player)
return tp;
return ntp;
}();
auto& newchain = chain_set.emplace_back();
newchain.chain_id = 0;
newchain.chain_count = 0;
newchain.triggering_effect = peffect;
newchain.triggering_player = owner_player;
newchain.evt = ev;
newchain.target_cards = nullptr;
newchain.target_player = PLAYER_NONE;
newchain.target_param = 0;
newchain.disable_player = PLAYER_NONE;
newchain.disable_reason = nullptr;
newchain.flag = 0;
}
}
if(ev.event_code == EVENT_ADJUST || ev.event_code == EVENT_BREAK_EFFECT || ((ev.event_code & 0xfffff000u) == EVENT_PHASE_START))
continue;
//triggers
pr = effects.trigger_f_effect.equal_range(ev.event_code);
for(auto eit = pr.first; eit != pr.second;) {
effect* peffect = eit->second;
++eit;
card* phandler = peffect->get_handler();
if(!phandler->is_status(STATUS_EFFECT_ENABLED) || !peffect->is_condition_check(phandler->current.controler, ev))
continue;
bool was_just_sent = false;
if((was_just_sent = check_simul(peffect, phandler)) == true && (peffect->range & LOCATION_HAND) == 0)
continue;
peffect->set_activate_location();
auto& newchain = core.new_fchain.emplace_back();
newchain.was_just_sent = was_just_sent;
newchain.flag = 0;
newchain.chain_id = infos.field_id++;
newchain.event_id = ev.global_id;
newchain.evt = ev;
newchain.triggering_effect = peffect;
newchain.set_triggering_state(phandler);
if(peffect->is_flag(EFFECT_FLAG_EVENT_PLAYER) && (ev.event_player == 0 || ev.event_player == 1))
newchain.triggering_player = ev.event_player;
else
newchain.triggering_player = phandler->current.controler;
phandler->create_relation(newchain);
}
pr = effects.trigger_o_effect.equal_range(ev.event_code);
for(auto eit = pr.first; eit != pr.second;) {
effect* peffect = eit->second;
++eit;
card* phandler = peffect->get_handler();
bool act = phandler->is_status(STATUS_EFFECT_ENABLED) && peffect->is_condition_check(phandler->current.controler, ev);
bool was_just_sent = false;
if((was_just_sent = check_simul(peffect, phandler)) == true && (peffect->range & LOCATION_HAND) == 0)
continue;
if((peffect->range & LOCATION_HAND) == 0 && !act)
continue;
peffect->set_activate_location();
auto& newchain = core.new_ochain.emplace_back();
newchain.was_just_sent = was_just_sent;
newchain.flag = 0;
newchain.chain_id = infos.field_id++;
newchain.event_id = ev.global_id;
newchain.evt = ev;
newchain.triggering_effect = peffect;
newchain.set_triggering_state(phandler);
if(peffect->is_flag(EFFECT_FLAG_EVENT_PLAYER) && (ev.event_player == 0 || ev.event_player == 1))
newchain.triggering_player = ev.event_player;
else
newchain.triggering_player = phandler->current.controler;
if(peffect->is_flag(EFFECT_FLAG_FIELD_ONLY)
|| !(peffect->range & LOCATION_HAND)
|| ((peffect->range & phandler->current.location) && act))
phandler->create_relation(newchain);
}
//instant_f
pr = effects.quick_f_effect.equal_range(ev.event_code);
for(auto eit = pr.first; eit != pr.second;) {
effect* peffect = eit->second;
++eit;
card* phandler = peffect->get_handler();
peffect->set_activate_location();
if(peffect->is_activateable(phandler->current.controler, ev)) {
chain& newchain = core.quick_f_chain[peffect];
newchain.flag = 0;
newchain.chain_id = infos.field_id++;
newchain.evt = ev;
newchain.triggering_effect = peffect;
newchain.set_triggering_state(phandler);
if(peffect->is_flag(EFFECT_FLAG_EVENT_PLAYER) && (ev.event_player == 0 || ev.event_player == 1))
newchain.triggering_player = ev.event_player;
else
newchain.triggering_player = phandler->current.controler;
phandler->create_relation(newchain);
}
}
// delayed activate effect
core.delayed_activate_event.push_back(ev);
// delayed quick effect
pr = effects.quick_o_effect.equal_range(ev.event_code);
for(auto eit = pr.first; eit != pr.second;) {
effect* peffect = eit->second;
++eit;
if(peffect->is_flag(EFFECT_FLAG_DELAY) && peffect->is_condition_check(peffect->get_handler()->current.controler, ev))
core.delayed_quick_tmp.emplace(peffect, ev);
}
}
while(tp.size()) {
core.sub_solving_continuous.splice(core.sub_solving_continuous.end(), tp, tp.begin());
emplace_process<Processors::SolveContinuous>();
}
while(ntp.size()) {
core.sub_solving_continuous.splice(core.sub_solving_continuous.end(), ntp, ntp.begin());
emplace_process<Processors::SolveContinuous>();
}
core.instant_event.splice(core.instant_event.end(), core.queue_event);
/*if(core.set_forced_attack) {
core.set_forced_attack = false;
emplace_process<Processors::ForcedBattle>();
}*/
return TRUE;
}
int32_t field::process_single_event() {
if(core.single_event.size() == 0) {
/*if(core.set_forced_attack) {
core.set_forced_attack = false;
emplace_process<Processors::ForcedBattle>();
}*/
return TRUE;
}
chain_list tp;
chain_list ntp;
for(const auto& ev : core.single_event) {
card* starget = ev.trigger_card;
auto pr = starget->single_effect.equal_range(ev.event_code);
for(auto eit = pr.first; eit != pr.second;) {
effect* peffect = eit->second;
++eit;
process_single_event(peffect, ev, tp, ntp);
}
for(auto& pcard : starget->xyz_materials) {
pr = pcard->xmaterial_effect.equal_range(ev.event_code);
for(auto eit = pr.first; eit != pr.second;) {
effect* peffect = eit->second;
++eit;
if(peffect->type & EFFECT_TYPE_FIELD)
continue;
process_single_event(peffect, ev, tp, ntp);
}
}
}
while(tp.size()) {
core.sub_solving_continuous.splice(core.sub_solving_continuous.end(), tp, tp.begin());
emplace_process<Processors::SolveContinuous>();
}
while(ntp.size()) {
core.sub_solving_continuous.splice(core.sub_solving_continuous.end(), ntp, ntp.begin());
emplace_process<Processors::SolveContinuous>();
}
core.single_event.clear();
/*if(core.set_forced_attack) {
core.set_forced_attack = false;
emplace_process<Processors::ForcedBattle>();
}*/
return TRUE;
}
int32_t field::process_single_event(effect* peffect, const tevent& e, chain_list& tp, chain_list& ntp) {
if(!(peffect->type & EFFECT_TYPE_ACTIONS))
return FALSE;
if((peffect->type & EFFECT_TYPE_FLIP) && (e.event_value & (NO_FLIP_EFFECT >> 16)))
return FALSE;
//continuous & trigger (single)
if(peffect->type & EFFECT_TYPE_CONTINUOUS) {
uint8_t owner_player = peffect->get_handler_player();
if(peffect->is_flag(EFFECT_FLAG_EVENT_PLAYER) && (e.event_player == 0 || e.event_player == 1))
owner_player = e.event_player;
if(peffect->is_activateable(owner_player, e)) {
auto& chain_set = [&]() -> chain_list& {
if(peffect->is_flag(EFFECT_FLAG_DELAY) && (core.chain_solving || core.conti_solving)) {
if(owner_player == infos.turn_player)
return core.delayed_continuous_tp;
return core.delayed_continuous_ntp;
}
if(owner_player == infos.turn_player)
return tp;
return ntp;
}();
auto& newchain = chain_set.emplace_back();
newchain.chain_id = 0;
newchain.chain_count = 0;
newchain.triggering_effect = peffect;
newchain.triggering_player = owner_player;
newchain.evt = e;
newchain.target_cards = nullptr;
newchain.target_player = PLAYER_NONE;
newchain.target_param = 0;
newchain.disable_player = PLAYER_NONE;
newchain.disable_reason = nullptr;
newchain.flag = 0;
}
} else {
card* phandler = peffect->get_handler();
if(!peffect->is_condition_check(phandler->current.controler, e))
return FALSE;
peffect->set_activate_location();
auto& chain_set = [&]() -> chain_list& {
if(core.flip_delayed && e.event_code == EVENT_FLIP) {
if(peffect->type & EFFECT_TYPE_TRIGGER_O)
return core.new_ochain_b;
return core.new_fchain_b;
}
if(peffect->type & EFFECT_TYPE_TRIGGER_O)
return core.new_ochain;
return core.new_fchain;
}();
auto& newchain = chain_set.emplace_back();
newchain.flag = 0;
newchain.chain_id = infos.field_id++;
newchain.event_id = e.global_id;
newchain.evt = e;
newchain.triggering_effect = peffect;
newchain.set_triggering_state(phandler);
if(peffect->is_flag(EFFECT_FLAG_EVENT_PLAYER) && (e.event_player == 0 || e.event_player == 1))
newchain.triggering_player = e.event_player;
else {
if(phandler->current.reason & REASON_TEMPORARY)
newchain.triggering_player = phandler->previous.controler;
else
newchain.triggering_player = newchain.triggering_controler;
}
peffect->set_active_type();
phandler->create_relation(newchain);
effect* deffect;
if((deffect = phandler->is_affected_by_effect(EFFECT_DISABLE_EFFECT)) != nullptr) {
effect* negeff = pduel->new_effect();
negeff->owner = deffect->owner;
negeff->type = EFFECT_TYPE_SINGLE;
negeff->code = EFFECT_DISABLE_CHAIN;
negeff->value = newchain.chain_id;
negeff->reset_flag = RESET_EVENT | deffect->get_value();
phandler->add_effect(negeff);
}
}
/*if(core.set_forced_attack) {
core.set_forced_attack = false;
emplace_process<Processors::ForcedBattle>();
}*/
return TRUE;
}
bool field::process(Processors::IdleCommand& arg) {
switch(arg.step) {
case 0: {
bool must_attack = false;
core.select_chains.clear();
nil_event.event_code = EVENT_FREE_CHAIN;
if(core.set_forced_attack) {
core.set_forced_attack = false;
arg.step = Processors::restart;
emplace_process<Processors::ForcedBattle>();
return FALSE;
}
core.to_bp = true;
core.to_ep = true;
if((!is_flag(DUEL_ATTACK_FIRST_TURN) && infos.turn_id == 1 && !(is_player_affected_by_effect(infos.turn_player, EFFECT_BP_FIRST_TURN))) || infos.phase == PHASE_MAIN2 || is_player_affected_by_effect(infos.turn_player, EFFECT_CANNOT_BP) || core.force_turn_end)
core.to_bp = false;
if(infos.phase == PHASE_MAIN1) {
for(auto& pcard : player[infos.turn_player].list_mzone) {
if(pcard && pcard->is_capable_attack() && pcard->is_affected_by_effect(EFFECT_MUST_ATTACK)) {
must_attack = true;
break;
}
}
if(core.to_bp && (must_attack || is_player_affected_by_effect(infos.turn_player, EFFECT_CANNOT_EP)))
core.to_ep = false;
}
if((infos.phase == PHASE_MAIN1 && is_player_affected_by_effect(infos.turn_player, EFFECT_SKIP_M1))
|| (infos.phase == PHASE_MAIN2 && is_player_affected_by_effect(infos.turn_player, EFFECT_SKIP_M2)) || core.force_turn_end) {
if(core.to_bp && core.to_ep) {
core.select_options.clear();
core.select_options.push_back(80);
core.select_options.push_back(81);
emplace_process<Processors::SelectOption>(infos.turn_player);
arg.step = 11;
} else if(core.to_bp) {
arg.phase_to_change_to = 6;
arg.step = 10;
reset_phase(infos.phase);
adjust_all();
} else {
arg.phase_to_change_to = 7;
arg.step = 10;
reset_phase(infos.phase);
adjust_all();
}
return FALSE;
}
if((infos.phase == PHASE_MAIN2) && core.skip_m2) {
core.skip_m2 = false;
returns.set<int32_t>(0, 7);
return FALSE;
}
auto pr = effects.activate_effect.equal_range(EVENT_FREE_CHAIN);
for(auto eit = pr.first; eit != pr.second; eit++) {
effect* peffect = eit->second;
peffect->set_activate_location();
if(peffect->is_activateable(infos.turn_player, nil_event)) {
core.select_chains.emplace_back().triggering_effect = peffect;
}
}
pr = effects.quick_o_effect.equal_range(EVENT_FREE_CHAIN);
for(auto eit = pr.first; eit != pr.second; eit++) {
effect* peffect = eit->second;
peffect->set_activate_location();
if(peffect->is_activateable(infos.turn_player, nil_event)) {
core.select_chains.emplace_back().triggering_effect = peffect;
}
}
pr = effects.continuous_effect.equal_range(EVENT_FREE_CHAIN);
for(auto eit = pr.first; eit != pr.second; eit++) {
effect* peffect = eit->second;
if(peffect->get_handler_player() == infos.turn_player && peffect->is_activateable(infos.turn_player, nil_event)) {
core.select_chains.emplace_back().triggering_effect = peffect;
}
}
for(auto& eit : effects.ignition_effect) {
effect* peffect = eit.second;
peffect->set_activate_location();
if(peffect->is_activateable(infos.turn_player, nil_event)) {
core.select_chains.emplace_back().triggering_effect = peffect;
}
}
core.summonable_cards.clear();
for(auto& pcard : player[infos.turn_player].list_hand)
if(pcard->is_can_be_summoned(infos.turn_player, FALSE, nullptr, 0))
core.summonable_cards.push_back(pcard);
for(auto& pcard : player[infos.turn_player].list_mzone) {
if(pcard && pcard->is_can_be_summoned(infos.turn_player, FALSE, nullptr, 0))
core.summonable_cards.push_back(pcard);
}
core.spsummonable_cards.clear();
effect_set eset;
filter_field_effect(EFFECT_SPSUMMON_PROC, &eset);
for(const auto& peff : eset) {
card* pcard = peff->get_handler();
if(!peff->check_count_limit(pcard->current.controler))
continue;
if(pcard->current.controler == infos.turn_player && pcard->is_special_summonable(infos.turn_player, 0))
core.spsummonable_cards.push_back(pcard);
}
eset.clear();
filter_field_effect(EFFECT_SPSUMMON_PROC_G, &eset);
for(const auto& peff : eset) {
card* pcard = peff->get_handler();
if(!peff->check_count_limit(infos.turn_player))
continue;
if(pcard->current.controler != infos.turn_player && !peff->is_flag(EFFECT_FLAG_BOTH_SIDE))
continue;
effect* oreason = core.reason_effect;
uint8_t op = core.reason_player;
core.reason_effect = peff;
core.reason_player = pcard->current.controler;
save_lp_cost();
pduel->lua->add_param<LuaParam::EFFECT>(peff);
pduel->lua->add_param<LuaParam::CARD>(pcard);
if(pduel->lua->check_condition(peff->condition, 2))
core.spsummonable_cards.push_back(pcard);
restore_lp_cost();
core.reason_effect = oreason;
core.reason_player = op;
}
core.repositionable_cards.clear();
for(auto& pcard : player[infos.turn_player].list_mzone) {
if(pcard && ((pcard->is_position(POS_FACEUP | POS_FACEDOWN_ATTACK) && pcard->is_capable_change_position(infos.turn_player))
|| (pcard->is_position(POS_FACEDOWN) && pcard->is_can_be_flip_summoned(infos.turn_player))))
core.repositionable_cards.push_back(pcard);
}
core.msetable_cards.clear();
core.ssetable_cards.clear();
for(auto& pcard : player[infos.turn_player].list_hand) {
if(pcard->is_setable_mzone(infos.turn_player, FALSE, nullptr, 0))
core.msetable_cards.push_back(pcard);
if(pcard->is_setable_szone(infos.turn_player))
core.ssetable_cards.push_back(pcard);
}
emplace_process<Processors::SelectIdleCmd>(infos.turn_player);
return FALSE;
}
case 1: {
uint32_t ctype = returns.at<int32_t>(0) & 0xffff;
uint32_t sel = returns.at<int32_t>(0) >> 16;
if(ctype == 5) {
auto newchain = std::next(core.select_chains.begin(), sel);
effect* peffect = newchain->triggering_effect;
if(peffect->type & EFFECT_TYPE_CONTINUOUS) {
core.select_chains.clear();
solve_continuous(peffect->get_handler_player(), peffect, nil_event);
arg.step = 2;
return FALSE;
}
card* phandler = peffect->get_handler();
newchain->flag = 0;
newchain->chain_id = infos.field_id++;
newchain->evt.event_code = peffect->code;
newchain->evt.event_player = PLAYER_NONE;
newchain->evt.event_value = 0;
newchain->evt.event_cards = nullptr;
newchain->evt.reason = 0;
newchain->evt.reason_effect = nullptr;
newchain->evt.reason_player = PLAYER_NONE;
newchain->set_triggering_state(phandler);
newchain->triggering_player = infos.turn_player;
core.new_chains.splice(core.new_chains.end(), core.select_chains, newchain);
phandler->set_status(STATUS_CHAINING, TRUE);
peffect->dec_count(infos.turn_player);
core.select_chains.clear();
emplace_process<Processors::AddChain>();
emplace_process<Processors::QuickEffect>(false, 1 - infos.turn_player);
infos.priorities[0] = 0;
infos.priorities[1] = 0;
core.select_chains.clear();
return FALSE;
} else if(ctype == 0) {
arg.step = 4;
return FALSE;
} else if(ctype == 1) {
arg.step = 5;
return FALSE;
} else if(ctype == 2) {
arg.step = 6;
return FALSE;
} else if(ctype == 3) {
arg.step = 7;
return FALSE;
} else if(ctype == 4) {
arg.step = 8;
return FALSE;
} else if (ctype == 8) {
arg.step = Processors::restart;
shuffle(infos.turn_player, LOCATION_HAND);
infos.can_shuffle = false;
return FALSE;
} else {
arg.step = 9;
auto message = pduel->new_message(MSG_HINT);
message->write<uint8_t>(HINT_EVENT);
message->write<uint8_t>(1 - infos.turn_player);
message->write<uint64_t>(23);
core.select_chains.clear();
core.hint_timing[infos.turn_player] = TIMING_MAIN_END;
emplace_process<Processors::QuickEffect>(false, 1 - infos.turn_player);
infos.priorities[infos.turn_player] = 1;
infos.priorities[1 - infos.turn_player] = 0;
arg.phase_to_change_to = ctype;
return FALSE;
}
/*if(core.set_forced_attack) {
core.set_forced_attack = false;
emplace_process<Processors::ForcedBattle>();
}*/
return TRUE;
}
case 2: {
for(auto& ch_lim : core.chain_limit)
ensure_luaL_stack(luaL_unref, pduel->lua->lua_state, LUA_REGISTRYINDEX, ch_lim.function);
core.chain_limit.clear();
for(auto& ch : core.current_chain)
ch.triggering_effect->get_handler()->set_status(STATUS_CHAINING, FALSE);
emplace_process<Processors::SolveChain>(false, false, false);
arg.step = Processors::restart;
return FALSE;
}
case 3: {
adjust_instant();
emplace_process<Processors::PointEvent>(false, false, false);
arg.step = Processors::restart;
return FALSE;
}
case 5: {
card* target = core.summonable_cards[returns.at<int32_t>(0) >> 16];
core.summon_cancelable = TRUE;
summon(infos.turn_player, target, nullptr, FALSE, 0);
arg.step = Processors::restart;
return FALSE;
}
case 6: {
card* target = core.spsummonable_cards[returns.at<int32_t>(0) >> 16];
core.summon_cancelable = TRUE;
special_summon_rule(infos.turn_player, target, 0);
arg.step = Processors::restart;
return FALSE;
}
case 7: {
card* target = core.repositionable_cards[returns.at<int32_t>(0) >> 16];
if(target->is_position(POS_FACEUP_ATTACK)) {
core.phase_action = true;
change_position(target, nullptr, infos.turn_player, POS_FACEUP_DEFENSE, FALSE);
adjust_all();
emplace_process<Processors::PointEvent>(false, false, false);
} else if(target->is_position(POS_FACEUP_DEFENSE)) {
core.phase_action = true;
change_position(target, nullptr, infos.turn_player, POS_FACEUP_ATTACK, FALSE);
adjust_all();
emplace_process<Processors::PointEvent>(false, false, false);
} else if(target->is_position(POS_FACEDOWN_ATTACK)) {
arg.card_to_reposition = target;
int32_t positions = 0;
if(target->is_capable_change_position(infos.turn_player))
positions |= POS_FACEDOWN_DEFENSE;
if(target->is_can_be_flip_summoned(infos.turn_player))
positions |= POS_FACEUP_ATTACK;
emplace_process<Processors::SelectPosition>(infos.turn_player, target->data.code, positions);
arg.step = 12;
return FALSE;
} else
emplace_process<Processors::FlipSummon>(target->current.controler, target);
target->set_status(STATUS_FORM_CHANGED, TRUE);
arg.step = Processors::restart;
return FALSE;
}
case 8: {
card* target = core.msetable_cards[returns.at<int32_t>(0) >> 16];
core.summon_cancelable = TRUE;
mset(target->current.controler, target, nullptr, FALSE, 0);
arg.step = Processors::restart;
return FALSE;
}
case 9: {
card* target = core.ssetable_cards[returns.at<int32_t>(0) >> 16];
emplace_process<Processors::SpellSet>(target->current.controler, target->current.controler, target, nullptr);
arg.step = Processors::restart;
return FALSE;
}
case 10: {
//end announce
for(auto& ch_lim : core.chain_limit)
ensure_luaL_stack(luaL_unref, pduel->lua->lua_state, LUA_REGISTRYINDEX, ch_lim.function);
core.chain_limit.clear();
if(core.current_chain.size()) {
for(auto& ch : core.current_chain)
ch.triggering_effect->get_handler()->set_status(STATUS_CHAINING, FALSE);
emplace_process<Processors::SolveChain>(false, false, false);
arg.step = Processors::restart;
return FALSE;
}
reset_phase(infos.phase);
adjust_all();
return FALSE;
}
case 11: {
returns.set<int32_t>(0, arg.phase_to_change_to);
infos.can_shuffle = true;
/*if(core.set_forced_attack) {
core.set_forced_attack = false;
emplace_process<Processors::ForcedBattle>();
}*/
return TRUE;
}
case 12: {
if(returns.at<int32_t>(0) == 0)
arg.phase_to_change_to = 6;
else
arg.phase_to_change_to = 7;
reset_phase(infos.phase);
adjust_all();
arg.step = 10;
return FALSE;
}
case 13: {
card* target = arg.card_to_reposition;
if(returns.at<int32_t>(0) == POS_FACEUP_ATTACK)
emplace_process<Processors::FlipSummon>(target->current.controler, target);
else {
core.phase_action = true;
change_position(target, nullptr, infos.turn_player, POS_FACEDOWN_DEFENSE, FALSE);
adjust_all();
emplace_process<Processors::PointEvent>(false, false, false);
}
target->set_status(STATUS_FORM_CHANGED, TRUE);
arg.step = Processors::restart;
return FALSE;
}
}
/*if(core.set_forced_attack) {
core.set_forced_attack = false;
emplace_process<Processors::ForcedBattle>();
}*/
return TRUE;
}
bool field::process(Processors::BattleCommand& arg) {
switch(arg.step) {
case 0: {
core.select_chains.clear();
nil_event.event_code = EVENT_FREE_CHAIN;
if(!core.chain_attack) {
core.chain_attacker_id = 0;
core.chain_attack_target = nullptr;
}
core.attack_player = FALSE;
core.attacker = nullptr;
core.attack_target = nullptr;
if(auto peffect = is_player_affected_by_effect(infos.turn_player, EFFECT_SKIP_BP); peffect != nullptr || core.force_turn_end) {
arg.step = 41;
arg.phase_to_change_to = 2;
arg.repeat_battle_phase = static_cast<bool>(is_player_affected_by_effect(infos.turn_player, EFFECT_BP_TWICE));
if(core.force_turn_end || !peffect->value) {
reset_phase(PHASE_BATTLE_STEP);
adjust_all();
infos.phase = PHASE_BATTLE;
emplace_process<Processors::PhaseEvent>(PHASE_BATTLE);
} else {
core.hint_timing[infos.turn_player] = 0;
reset_phase(PHASE_BATTLE);
adjust_all();
}
return FALSE;
}
auto pr = effects.activate_effect.equal_range(EVENT_FREE_CHAIN);
for(auto eit = pr.first; eit != pr.second; eit++) {
auto peffect = eit->second;
peffect->set_activate_location();
if(peffect->is_activateable(infos.turn_player, nil_event) && peffect->get_speed() > 1) {
core.select_chains.emplace_back().triggering_effect = peffect;
}
}
pr = effects.quick_o_effect.equal_range(EVENT_FREE_CHAIN);
for(auto eit = pr.first; eit != pr.second; eit++) {
auto peffect = eit->second;
peffect->set_activate_location();
if(peffect->is_activateable(infos.turn_player, nil_event)) {
core.select_chains.emplace_back().triggering_effect = peffect;
}
}
pr = effects.continuous_effect.equal_range(EVENT_FREE_CHAIN);
for(auto eit = pr.first; eit != pr.second; eit++) {
auto peffect = eit->second;
if(peffect->get_handler_player() == infos.turn_player && peffect->is_activateable(infos.turn_player, nil_event)) {
core.select_chains.emplace_back().triggering_effect = peffect;
}
}
core.attackable_cards.clear();
card_vector first_attack;
card_vector must_attack;
if(!is_player_affected_by_effect(infos.turn_player, EFFECT_CANNOT_ATTACK_ANNOUNCE)) {
for(auto& pcard : player[infos.turn_player].list_mzone) {
if(!pcard)
continue;
if(!pcard->is_capable_attack_announce(infos.turn_player))
continue;
uint8_t chain_attack = FALSE;
if(core.chain_attack && core.chain_attacker_id == pcard->fieldid)
chain_attack = TRUE;
card_vector cv;
get_attack_target(pcard, &cv, chain_attack);
if(cv.size() == 0 && pcard->direct_attackable == 0)
continue;
core.attackable_cards.push_back(pcard);
if(pcard->is_affected_by_effect(EFFECT_FIRST_ATTACK))
first_attack.push_back(pcard);
if(pcard->is_affected_by_effect(EFFECT_MUST_ATTACK))
must_attack.push_back(pcard);
}
if(first_attack.size())
core.attackable_cards = first_attack;
}
core.to_m2 = !is_flag(DUEL_NO_MAIN_PHASE_2);
core.to_ep = true;
if(must_attack.size() || is_player_affected_by_effect(infos.turn_player, EFFECT_CANNOT_M2) || core.force_turn_end)
core.to_m2 = false;
if(must_attack.size())
core.to_ep = false;
core.attack_cancelable = true;
core.attack_cost_paid = FALSE;
emplace_process<Processors::SelectBattleCmd>(infos.turn_player);
return FALSE;
}
case 1: {
int32_t ctype = returns.at<int32_t>(0) & 0xffff;
int32_t sel = returns.at<int32_t>(0) >> 16;
if(arg.forced_attack_done || (ctype != 0 && ctype != 1)) {
arg.step = 39;
// ignored when arg.forced_attack_done, so can be anything
arg.phase_to_change_to = ctype;
auto message = pduel->new_message(MSG_HINT);
message->write<uint8_t>(HINT_EVENT);
message->write<uint8_t>(1 - infos.turn_player);
message->write<uint64_t>(29);
core.select_chains.clear();
core.hint_timing[infos.turn_player] = TIMING_BATTLE_STEP_END;
emplace_process<Processors::QuickEffect>(false, 1 - infos.turn_player);
infos.priorities[infos.turn_player] = 1;
infos.priorities[1 - infos.turn_player] = 0;
return FALSE;
} else if(ctype == 0) {
auto newchain = std::next(core.select_chains.begin(), sel);
effect* peffect = newchain->triggering_effect;
if(peffect->type & EFFECT_TYPE_CONTINUOUS) {
core.select_chains.clear();
solve_continuous(peffect->get_handler_player(), peffect, nil_event);
arg.step = 14;
return FALSE;
}
card* phandler = peffect->get_handler();
newchain->flag = 0;
newchain->chain_id = infos.field_id++;
newchain->evt.event_code = peffect->code;
newchain->evt.event_player = PLAYER_NONE;
newchain->evt.event_value = 0;
newchain->evt.event_cards = nullptr;
newchain->evt.reason = 0;
newchain->evt.reason_effect = nullptr;
newchain->evt.reason_player = PLAYER_NONE;
newchain->set_triggering_state(phandler);
newchain->triggering_player = infos.turn_player;
core.new_chains.splice(core.new_chains.end(), core.select_chains, newchain);
phandler->set_status(STATUS_CHAINING, TRUE);
peffect->dec_count(infos.turn_player);
core.select_chains.clear();
emplace_process<Processors::AddChain>();
emplace_process<Processors::QuickEffect>(false, 1 - infos.turn_player);
infos.priorities[0] = 0;
infos.priorities[1] = 0;
core.select_chains.clear();
return FALSE;
} else if(ctype == 1) {
arg.step = 2;
arg.attack_announce_failed = false;
if(!arg.forced_attack)
core.attacker = core.attackable_cards[sel];
core.attacker->set_status(STATUS_ATTACK_CANCELED, FALSE);
core.attacker->attack_controler = core.attacker->current.controler;
core.pre_field[0] = core.attacker->fieldid_r;
if(core.chain_attack && core.chain_attacker_id != core.attacker->fieldid) {
core.chain_attack = false;
core.chain_attacker_id = 0;
}
effect_set eset;
core.tpchain.clear();
filter_player_effect(infos.turn_player, EFFECT_ATTACK_COST, &eset, false);
core.attacker->filter_effect(EFFECT_ATTACK_COST, &eset);
for(const auto& peff : eset) {
if(peff->operation) {
core.tpchain.emplace_back().triggering_effect = peff;
core.attack_cancelable = false;
}
}
if(core.tpchain.size() > 1) {
emplace_process<Processors::SortChain>(infos.turn_player);
arg.step = 13;
}
else if(core.tpchain.size() == 1){
core.sub_solving_event.push_back(nil_event);
emplace_process<Processors::ExecuteOperation>(core.tpchain.front().triggering_effect, infos.turn_player);
adjust_all();
}
return FALSE;
}
/*if(core.set_forced_attack) {
core.set_forced_attack = false;
emplace_process<Processors::ForcedBattle>();
}*/
return TRUE;
}
case 2: {
for(auto& ch_lim : core.chain_limit)
ensure_luaL_stack(luaL_unref, pduel->lua->lua_state, LUA_REGISTRYINDEX, ch_lim.function);
core.chain_limit.clear();
for(auto& ch : core.current_chain)
ch.triggering_effect->get_handler()->set_status(STATUS_CHAINING, FALSE);
emplace_process<Processors::SolveChain>(false, false, false);
arg.step = Processors::restart;
return FALSE;
}
case 3: {
arg.is_replaying_attack = false;
if(core.attacker->is_status(STATUS_ATTACK_CANCELED)) {
arg.attack_announce_failed = true;
arg.step = 6;
return FALSE;
}
//Cancel the attack cost
if(core.attack_cost_paid != 1 && !core.attack_cancelable) {
if(arg.forced_attack) {
returns.set<int32_t>(0, 2);
arg.step = 0;
} else
arg.step = Processors::restart;
return FALSE;
}
if(arg.forced_attack)
arg.step = 6;
return FALSE;
}
case 4: {
// select attack target(replay start point)
core.attack_player = FALSE;
core.select_cards.clear();
return_cards.clear();
arg.must_attack_map.clear();
auto atype = get_attack_target(core.attacker, &core.select_cards, core.chain_attack, true, &arg.must_attack_map);
// direct attack
if(core.attacker->direct_attackable) {
if(core.select_cards.size() == 0) {
returns.set<int32_t>(0, -2);
arg.step = 5;
return FALSE;
}
if(is_player_affected_by_effect(infos.turn_player, EFFECT_PATRICIAN_OF_DARKNESS))
emplace_process<Processors::SelectEffectYesNo>(1 - infos.turn_player, 31, core.attacker);
else
emplace_process<Processors::SelectYesNo>(infos.turn_player, 31);
return FALSE;
}
// no target and not direct attackable
if(core.select_cards.size() == 0) {
arg.attack_announce_failed = true;
arg.step = 6;
return FALSE;
}
auto differentMustAttackMonsterEffects = [&] {
size_t count = 0;
const auto& must = arg.must_attack_map;
for(auto it = must.begin(), end = must.end(); it != end; it = must.upper_bound(it->first))
++count;
return count;
}();
auto patrician = is_player_affected_by_effect(infos.turn_player, EFFECT_PATRICIAN_OF_DARKNESS) != nullptr;
if(patrician || (atype == 3 && differentMustAttackMonsterEffects != 1)) {
if(core.select_cards.size() == 1)
return_cards.list.push_back(core.select_cards.front());
else {
auto message = pduel->new_message(MSG_CARD_SELECTED);
message->write<uint32_t>(1);
message->write(core.attacker->get_info_location());
message = pduel->new_message(MSG_HINT);
message->write<uint8_t>(HINT_SELECTMSG);
message->write<uint8_t>(1 - infos.turn_player);
message->write<uint64_t>(549);
emplace_process<Processors::SelectCard>(1 - infos.turn_player, false, 1, 1);
if(!patrician && atype == 3 && arg.must_attack_map.size() != differentMustAttackMonsterEffects) {
arg.step = 15;
return FALSE;
}
}
} else {
auto message = pduel->new_message(MSG_HINT);
message->write<uint8_t>(HINT_SELECTMSG);
message->write<uint8_t>(infos.turn_player);
message->write<uint64_t>(549);
emplace_process<Processors::SelectCard>(infos.turn_player, core.attack_cancelable, 1, 1);
}
arg.step = 5;
return FALSE;
}
case 5: {
// the answer of "direct attack or not"
if(returns.at<int32_t>(0)) {
returns.set<int32_t>(0, -2);
} else {
if(core.select_cards.size()) {
auto opposel = !!is_player_affected_by_effect(infos.turn_player, EFFECT_PATRICIAN_OF_DARKNESS);
const auto sel_player = opposel ? 1 - infos.turn_player : infos.turn_player;
const auto cancelable = (core.attack_cancelable && !opposel);
auto message = pduel->new_message(MSG_HINT);
message->write<uint8_t>(HINT_SELECTMSG);
message->write<uint8_t>(opposel ? 1 - infos.turn_player : infos.turn_player);
message->write<uint64_t>(549);
emplace_process<Processors::SelectCard>(sel_player, cancelable, 1, 1);
} else {
arg.attack_announce_failed = true;
arg.step = 6;
}
}
return FALSE;
}
case 6: {
if(return_cards.canceled) {//cancel attack manually
if(arg.is_replaying_attack) {
arg.step = 12;
return FALSE;
}
if(arg.forced_attack) {
returns.set<int32_t>(0, 2);
arg.step = 0;
} else
arg.step = Processors::restart;
return FALSE;
}
if(returns.at<int32_t>(0) == -2)
core.attack_target = nullptr;
else
core.attack_target = return_cards.list[0];
if(core.attack_target)
core.pre_field[1] = core.attack_target->fieldid_r;
else
core.pre_field[1] = 0;
return FALSE;
}
case 7: {
if(!arg.is_replaying_attack) {
core.phase_action = true;
++core.attack_state_count[infos.turn_player];
check_card_counter(core.attacker, ACTIVITY_ATTACK, infos.turn_player);
++core.attacker->attack_announce_count;
}
if(arg.attack_announce_failed) {
++core.attacker->announce_count;
core.chain_attack = false;
if(arg.forced_attack) {
returns.set<int32_t>(0, 2);
arg.step = 0;
} else
arg.step = Processors::restart;
}
return FALSE;
}
case 8: {
core.attack_cancelable = true;
auto message = pduel->new_message(MSG_ATTACK);
message->write(core.attacker->get_info_location());
if(core.attack_target) {
raise_single_event(core.attack_target, nullptr, EVENT_BE_BATTLE_TARGET, nullptr, 0, 0, 1 - infos.turn_player, 0);
raise_event(core.attack_target, EVENT_BE_BATTLE_TARGET, nullptr, 0, 0, 1 - infos.turn_player, 0);
message->write(core.attack_target->get_info_location());
} else {
message->write(loc_info{});
}
core.attack_rollback = false;
core.opp_mzone.clear();
for(auto& pcard : player[1 - infos.turn_player].list_mzone) {
if(pcard)
core.opp_mzone.insert(pcard->fieldid_r);
}
if(!arg.is_replaying_attack) {
raise_single_event(core.attacker, nullptr, EVENT_ATTACK_ANNOUNCE, nullptr, 0, 0, infos.turn_player, 0);
raise_event(core.attacker, EVENT_ATTACK_ANNOUNCE, nullptr, 0, 0, infos.turn_player, 0);
}
core.attacker->attack_controler = core.attacker->current.controler;
core.pre_field[0] = core.attacker->fieldid_r;
process_single_event();
process_instant_event();
core.hint_timing[infos.turn_player] = TIMING_ATTACK;
emplace_process<Processors::PointEvent>(false, false, false);
return FALSE;
}
case 9: {
if(is_player_affected_by_effect(infos.turn_player, EFFECT_SKIP_BP)
|| core.attacker->is_status(STATUS_ATTACK_CANCELED) || core.attack_rollback) {
arg.step = 10;
return FALSE;
}
auto message = pduel->new_message(MSG_HINT);
message->write<uint8_t>(HINT_EVENT);
message->write<uint8_t>(0);
message->write<uint64_t>(24);
message = pduel->new_message(MSG_HINT);
message->write<uint8_t>(HINT_EVENT);
message->write<uint8_t>(1);
message->write<uint64_t>(24);
core.hint_timing[0] = TIMING_BATTLE_PHASE;
core.hint_timing[1] = TIMING_BATTLE_PHASE;
emplace_process<Processors::PointEvent>(Step{ 30 }, false, false, false);
return FALSE;
}
case 10: {
if(returns.at<int32_t>(0))
arg.step = 8;
else
adjust_all();
return FALSE;
}
case 11: {
if(core.attacker->is_affected_by_effect(EFFECT_ATTACK_DISABLED)) {
core.attacker->reset(EFFECT_ATTACK_DISABLED, RESET_CODE);
pduel->new_message(MSG_ATTACK_DISABLED);
core.attacker->set_status(STATUS_ATTACK_CANCELED, TRUE);
}
if(is_player_affected_by_effect(infos.turn_player, EFFECT_SKIP_BP)
|| core.attacker->is_status(STATUS_ATTACK_CANCELED)) {
arg.step = 12;
return FALSE;
}
// go to damage step
if(!core.attack_rollback) {
++core.attacker->announce_count;
core.attacker->announced_cards.addcard(core.attack_target);
attack_all_target_check();
arg.step = 18;
return FALSE;
}
card_vector cv;
get_attack_target(core.attacker, &cv, core.chain_attack);
if(!cv.size() && !core.attacker->direct_attackable) {
arg.step = 12;
return FALSE;
}
// replay
if(is_flag(DUEL_STORE_ATTACK_REPLAYS) && !core.chain_attack) {
returns.set<int32_t>(0, FALSE);
} else if(!core.attacker->is_affected_by_effect(EFFECT_MUST_ATTACK))
emplace_process<Processors::SelectYesNo>(infos.turn_player, 30);
else {
returns.set<int32_t>(0, TRUE);
core.attack_cancelable = false;
}
return FALSE;
}
case 12: {
// answer of "replay or not"
if(returns.at<int32_t>(0)) {
arg.is_replaying_attack = true;
arg.attack_announce_failed = false;
arg.step = 3;
}
return FALSE;
}
case 13: {
if(core.attacker->fieldid_r == core.pre_field[0] && (!(is_flag(DUEL_STORE_ATTACK_REPLAYS) && !core.chain_attack) || core.attacker->is_status(STATUS_ATTACK_CANCELED))) {
++core.attacker->announce_count;
core.attacker->announced_cards.addcard(core.attack_target);
attack_all_target_check();
}
core.chain_attack = false;
if(arg.forced_attack) {
returns.set<int32_t>(0, 2);
arg.step = 0;
} else
arg.step = Processors::restart;
reset_phase(PHASE_DAMAGE);
adjust_all();
return FALSE;
}
case 14: {
for(auto clit = core.tpchain.begin(); clit != core.tpchain.end(); ++clit) {
core.sub_solving_event.push_back(nil_event);
emplace_process<Processors::ExecuteOperation>(clit->triggering_effect, infos.turn_player);
adjust_all();
}
core.tpchain.clear();
arg.step = 2;
return FALSE;
}
case 15: {
adjust_instant();
emplace_process<Processors::PointEvent>(false, false, false);
arg.step = Processors::restart;
return FALSE;
}
// EFFECT_MUST_ATTACK_MONSTER where an effect affects more than 1 monster
case 16: {
auto selected_card = return_cards.list.front();
const auto range = arg.must_attack_map.equal_range(
std::find_if(arg.must_attack_map.begin(), arg.must_attack_map.end(),
[selected_card](const auto& must_pair) {
return must_pair.second == selected_card;
})->first);
if(std::distance(range.first, range.second) < 2) {
core.attack_target = selected_card;
core.pre_field[1] = core.attack_target->fieldid_r;
arg.step = 5;
return FALSE;
}
core.select_cards.clear();
std::transform(range.first, range.second, std::back_inserter(core.select_cards), [](const auto& pair) {
return pair.second;
});
auto message = pduel->new_message(MSG_HINT);
message->write<uint8_t>(HINT_SELECTMSG);
message->write<uint8_t>(infos.turn_player);
message->write<uint64_t>(549);
emplace_process<Processors::SelectCard>(infos.turn_player, core.attack_cancelable, 1, 1);
arg.step = 5;
return FALSE;
}
case 19: {
infos.phase = PHASE_DAMAGE;
core.chain_attack = false;
arg.is_replaying_attack = false;
core.damage_calculated = false;
core.selfdes_disabled = true;
core.flip_delayed = true;
core.attacker->attack_controler = core.attacker->current.controler;
core.pre_field[0] = core.attacker->fieldid_r;
if(core.attack_target) {
core.attack_target->attack_controler = core.attack_target->current.controler;
core.pre_field[1] = core.attack_target->fieldid_r;
} else
core.pre_field[1] = 0;
++core.attacker->attacked_count;
core.attacker->attacked_cards.addcard(core.attack_target);
++core.battled_count[infos.turn_player];
adjust_all();
return FALSE;
}
case 20: {
// start of PHASE_DAMAGE;
(void)pduel->new_message(MSG_DAMAGE_STEP_START);
raise_single_event(core.attacker, nullptr, EVENT_BATTLE_START, nullptr, 0, 0, 0, 0);
if(core.attack_target) {
raise_single_event(core.attack_target, nullptr, EVENT_BATTLE_START, nullptr, 0, 0, 0, 1);
}
raise_event(nullptr, EVENT_BATTLE_START, nullptr, 0, 0, 0, 0);
process_single_event();
process_instant_event();
arg.previous_point_event_had_any_trigger_to_resolve = false;
if(!is_flag(DUEL_6_STEP_BATLLE_STEP) || (core.new_fchain.size() || core.new_ochain.size())) {
arg.previous_point_event_had_any_trigger_to_resolve = core.new_fchain.size() || core.new_ochain.size();
auto message = pduel->new_message(MSG_HINT);
message->write<uint8_t>(HINT_EVENT);
message->write<uint8_t>(0);
message->write<uint64_t>(40);
message = pduel->new_message(MSG_HINT);
message->write<uint8_t>(HINT_EVENT);
message->write<uint8_t>(1);
message->write<uint64_t>(40);
emplace_process<Processors::PointEvent>(false, false, is_flag(DUEL_SINGLE_CHAIN_IN_DAMAGE_SUBSTEP));
}
return FALSE;
}
case 21: {
if(core.attacker->is_status(STATUS_ATTACK_CANCELED)) {
arg.step = 33;
return FALSE;
}
if(!core.attack_target) {
return FALSE;
}
core.attacker->temp.position = core.attacker->current.position;
core.attack_target->temp.position = core.attack_target->current.position;
if(core.attack_target->is_position(POS_FACEDOWN)) {
change_position(core.attack_target, nullptr, PLAYER_NONE, core.attack_target->current.position >> 1, 0, TRUE);
adjust_all();
}
return FALSE;
}
case 22: {
raise_single_event(core.attacker, nullptr, EVENT_BATTLE_CONFIRM, nullptr, 0, 0, 0, 0);
if(core.attack_target) {
if(core.attack_target->temp.position & POS_FACEDOWN)
core.pre_field[1] = core.attack_target->fieldid_r;
raise_single_event(core.attack_target, nullptr, EVENT_BATTLE_CONFIRM, nullptr, 0, 0, 0, 1);
}
raise_event(nullptr, EVENT_BATTLE_CONFIRM, nullptr, 0, 0, 0, 0);
process_single_event();
process_instant_event();
if(!is_flag(DUEL_6_STEP_BATLLE_STEP) || !arg.previous_point_event_had_any_trigger_to_resolve
|| core.new_fchain.size() || core.new_ochain.size()) {
auto message = pduel->new_message(MSG_HINT);
message->write<uint8_t>(HINT_EVENT);
message->write<uint8_t>(0);
message->write<uint64_t>(41);
message = pduel->new_message(MSG_HINT);
message->write<uint8_t>(HINT_EVENT);
message->write<uint8_t>(1);
message->write<uint64_t>(41);
core.hint_timing[infos.turn_player] = TIMING_DAMAGE_STEP;
emplace_process<Processors::PointEvent>(false, false, is_flag(DUEL_SINGLE_CHAIN_IN_DAMAGE_SUBSTEP));
}
return FALSE;
}
case 23: {
if(core.attacker->is_status(STATUS_ATTACK_CANCELED)) {
arg.step = 33;
return FALSE;
}
infos.phase = PHASE_DAMAGE_CAL;
adjust_all();
return FALSE;
}
case 24: {
// PHASE_DAMAGE_CAL;
calculate_battle_damage(nullptr, nullptr, nullptr);
raise_single_event(core.attacker, nullptr, EVENT_PRE_DAMAGE_CALCULATE, nullptr, 0, 0, 0, 0);
if(core.attack_target)
raise_single_event(core.attack_target, nullptr, EVENT_PRE_DAMAGE_CALCULATE, nullptr, 0, 0, 0, 1);
raise_event(nullptr, EVENT_PRE_DAMAGE_CALCULATE, nullptr, 0, 0, 0, 0);
process_single_event();
process_instant_event();
auto message = pduel->new_message(MSG_HINT);
message->write<uint8_t>(HINT_EVENT);
message->write<uint8_t>(0);
message->write<uint64_t>(42);
message = pduel->new_message(MSG_HINT);
message->write<uint8_t>(HINT_EVENT);
message->write<uint8_t>(1);
message->write<uint64_t>(42);
core.hint_timing[infos.turn_player] = TIMING_DAMAGE_CAL;
emplace_process<Processors::PointEvent>(false, false, is_flag(DUEL_SINGLE_CHAIN_IN_DAMAGE_SUBSTEP));
return FALSE;
}
case 25: {
if(core.attacker->is_status(STATUS_ATTACK_CANCELED)) {
reset_phase(PHASE_DAMAGE_CAL);
adjust_all();
infos.phase = PHASE_DAMAGE;
arg.step = 33;
return FALSE;
}
return FALSE;
}
case 26: {
// Duel.CalculateDamage() goes here
uint32_t aa = core.attacker->get_attack(), ad = core.attacker->get_defense();
uint32_t da = 0, dd = 0;
uint8_t pa = core.attacker->current.controler, pd;
core.attacker->set_status(STATUS_BATTLE_RESULT, FALSE);
core.attacker->set_status(STATUS_BATTLE_DESTROYED, FALSE);
if(core.attack_target) {
da = core.attack_target->get_attack();
dd = core.attack_target->get_defense();
core.attack_target->set_status(STATUS_BATTLE_RESULT, FALSE);
core.attack_target->set_status(STATUS_BATTLE_DESTROYED, FALSE);
pd = core.attack_target->current.controler;
if(pa != pd) {
core.attacker->set_status(STATUS_OPPO_BATTLE, TRUE);
core.attack_target->set_status(STATUS_OPPO_BATTLE, TRUE);
}
}
effect* damchange = nullptr;
card* reason_card = nullptr;
std::array<bool, 2> bd;
calculate_battle_damage(&damchange, &reason_card, &bd);
if(bd[0]) {
effect* indestructable_effect = core.attacker->is_affected_by_effect(EFFECT_INDESTRUCTABLE_BATTLE, core.attack_target);
if(indestructable_effect) {
auto message = pduel->new_message(MSG_HINT);
message->write<uint8_t>(HINT_CARD);
message->write<uint8_t>(0);
message->write<uint64_t>(indestructable_effect->owner->data.code);
bd[0] = false;
} else
core.attacker->set_status(STATUS_BATTLE_RESULT, TRUE);
}
if(bd[1]) {
effect* indestructable_effect = core.attack_target->is_affected_by_effect(EFFECT_INDESTRUCTABLE_BATTLE, core.attacker);
if(indestructable_effect) {
auto message = pduel->new_message(MSG_HINT);
message->write<uint8_t>(HINT_CARD);
message->write<uint8_t>(0);
message->write<uint64_t>(indestructable_effect->owner->data.code);
bd[1] = false;
} else
core.attack_target->set_status(STATUS_BATTLE_RESULT, TRUE);
}
auto message = pduel->new_message(MSG_BATTLE);
message->write(core.attacker->get_info_location());
message->write<uint32_t>(aa);
message->write<uint32_t>(ad);
message->write<uint8_t>(bd[0]);
if(core.attack_target) {
message->write(core.attack_target->get_info_location());
message->write<uint32_t>(da);
message->write<uint32_t>(dd);
message->write<uint8_t>(bd[1]);
} else {
message->write(loc_info{});
message->write<uint32_t>(0);
message->write<uint32_t>(0);
message->write<uint8_t>(0);
}
arg.damage_change_effect = damchange;
arg.reason_card = reason_card;
if(reason_card)
arg.reason_player = reason_card->current.controler;
if(!damchange) {
if(core.battle_damage[infos.turn_player]) {
raise_single_event(core.attacker, nullptr, EVENT_PRE_BATTLE_DAMAGE, nullptr, 0, reason_card->current.controler, infos.turn_player, core.battle_damage[infos.turn_player]);
if(core.attack_target)
raise_single_event(core.attack_target, nullptr, EVENT_PRE_BATTLE_DAMAGE, nullptr, 0, reason_card->current.controler, infos.turn_player, core.battle_damage[infos.turn_player]);
raise_event(reason_card, EVENT_PRE_BATTLE_DAMAGE, nullptr, 0, reason_card->current.controler, infos.turn_player, core.battle_damage[infos.turn_player]);
}
if(core.battle_damage[1 - infos.turn_player]) {
raise_single_event(core.attacker, nullptr, EVENT_PRE_BATTLE_DAMAGE, nullptr, 0, reason_card->current.controler, 1 - infos.turn_player, core.battle_damage[1 - infos.turn_player]);
if(core.attack_target)
raise_single_event(core.attack_target, nullptr, EVENT_PRE_BATTLE_DAMAGE, nullptr, 0, reason_card->current.controler, 1 - infos.turn_player, core.battle_damage[1 - infos.turn_player]);
raise_event(reason_card, EVENT_PRE_BATTLE_DAMAGE, nullptr, 0, reason_card->current.controler, 1 - infos.turn_player, core.battle_damage[1 - infos.turn_player]);
}
}
process_single_event();
process_instant_event();
core.damage_calculated = true;
return FALSE;
}
case 27: {
infos.phase = PHASE_DAMAGE;
core.hint_timing[infos.turn_player] = 0;
core.chain_attack = false;
core.attacker->battled_cards.addcard(core.attack_target);
if(core.attack_target)
core.attack_target->battled_cards.addcard(core.attacker);
auto* reason_card = arg.reason_card;
effect* damchange = std::exchange(arg.damage_change_effect, nullptr);
if(!damchange) {
if(core.battle_damage[0])
damage(nullptr, REASON_BATTLE, arg.reason_player, reason_card, 0, core.battle_damage[0]);
if(core.battle_damage[1])
damage(nullptr, REASON_BATTLE, arg.reason_player, reason_card, 1, core.battle_damage[1]);
} else {
if(core.battle_damage[0])
damage(damchange, REASON_EFFECT, arg.reason_player, reason_card, 0, core.battle_damage[0]);
if(core.battle_damage[1])
damage(damchange, REASON_EFFECT, arg.reason_player, reason_card, 1, core.battle_damage[1]);
}
reset_phase(PHASE_DAMAGE_CAL);
adjust_all();
return FALSE;
}
case 28: {
card_set des;
effect* peffect;
if(core.attacker->is_status(STATUS_BATTLE_RESULT)
&& core.attacker->current.location == LOCATION_MZONE && core.attacker->fieldid_r == core.pre_field[0]) {
des.insert(core.attacker);
core.attacker->temp.reason = core.attacker->current.reason;
core.attacker->temp.reason_card = core.attacker->current.reason_card;
core.attacker->temp.reason_effect = core.attacker->current.reason_effect;
core.attacker->temp.reason_player = core.attacker->current.reason_player;
core.attacker->current.reason_effect = nullptr;
core.attacker->current.reason = REASON_BATTLE;
core.attacker->current.reason_card = core.attack_target;
core.attacker->current.reason_player = core.attack_target->current.controler;
uint32_t dest = LOCATION_GRAVE;
uint32_t seq = 0;
if((peffect = core.attack_target->is_affected_by_effect(EFFECT_BATTLE_DESTROY_REDIRECT)) != nullptr && (core.attacker->data.type & TYPE_MONSTER)) {
dest = peffect->get_value(core.attacker);
seq = dest >> 16;
dest &= 0xffff;
}
core.attacker->sendto_param.set(core.attacker->owner, POS_FACEUP, dest, seq);
core.attacker->set_status(STATUS_DESTROY_CONFIRMED, TRUE);
}
if(core.attack_target && core.attack_target->is_status(STATUS_BATTLE_RESULT)
&& core.attack_target->current.location == LOCATION_MZONE && core.attack_target->fieldid_r == core.pre_field[1]) {
des.insert(core.attack_target);
core.attack_target->temp.reason = core.attack_target->current.reason;
core.attack_target->temp.reason_card = core.attack_target->current.reason_card;
core.attack_target->temp.reason_effect = core.attack_target->current.reason_effect;
core.attack_target->temp.reason_player = core.attack_target->current.reason_player;
core.attack_target->current.reason_effect = nullptr;
core.attack_target->current.reason = REASON_BATTLE;
core.attack_target->current.reason_card = core.attacker;
core.attack_target->current.reason_player = core.attacker->current.controler;
uint32_t dest = LOCATION_GRAVE;
uint32_t seq = 0;
if((peffect = core.attacker->is_affected_by_effect(EFFECT_BATTLE_DESTROY_REDIRECT)) != nullptr && (core.attack_target->data.type & TYPE_MONSTER)) {
dest = peffect->get_value(core.attack_target);
seq = dest >> 16;
dest &= 0xffff;
}
core.attack_target->sendto_param.set(core.attack_target->owner, POS_FACEUP, dest, seq);
core.attack_target->set_status(STATUS_DESTROY_CONFIRMED, TRUE);
}
core.attacker->set_status(STATUS_BATTLE_RESULT, FALSE);
if(core.attack_target)
core.attack_target->set_status(STATUS_BATTLE_RESULT, FALSE);
core.battle_destroy_rep.clear();
core.desrep_chain.clear();
if(des.size()) {
auto ng = pduel->new_group();
ng->container.swap(des);
ng->is_readonly = true;
emplace_process<Processors::Destroy>(Step{ 10 }, ng, nullptr, REASON_BATTLE, PLAYER_NONE);
arg.cards_destroyed_by_battle = ng;
}
return FALSE;
}
case 29: {
if(core.battle_destroy_rep.size())
destroy(core.battle_destroy_rep, nullptr, REASON_EFFECT | REASON_REPLACE, PLAYER_NONE);
if(core.desrep_chain.size())
emplace_process<Processors::OperationReplace>(Step{ 15 }, nullptr, nullptr, nullptr, false);
adjust_all();
return FALSE;
}
case 30: {
auto des = arg.cards_destroyed_by_battle;
if(des && des->container.size()) {
for(auto& pcard : des->container) {
pcard->set_status(STATUS_BATTLE_DESTROYED, TRUE);
pcard->set_status(STATUS_DESTROY_CONFIRMED, FALSE);
pcard->filter_disable_related_cards();
}
}
core.selfdes_disabled = false;
adjust_all();
if(is_flag(DUEL_6_STEP_BATLLE_STEP)) {
//EVENT_BATTLE_END was here, but this timing does not exist in Master Rule 3+
if(!core.effect_damage_step) {
auto message = pduel->new_message(MSG_HINT);
message->write<uint8_t>(HINT_EVENT);
message->write<uint8_t>(0);
message->write<uint64_t>(45);
message = pduel->new_message(MSG_HINT);
message->write<uint8_t>(HINT_EVENT);
message->write<uint8_t>(1);
message->write<uint64_t>(45);
core.hint_timing[infos.turn_player] = TIMING_DAMAGE_CAL;
emplace_process<Processors::PointEvent>(false, false, is_flag(DUEL_SINGLE_CHAIN_IN_DAMAGE_SUBSTEP));
} else {
break_effect();
}
}
return FALSE;
}
case 31: {
core.flip_delayed = false;
core.new_fchain.splice(core.new_fchain.begin(), core.new_fchain_b);
core.new_ochain.splice(core.new_ochain.begin(), core.new_ochain_b);
raise_single_event(core.attacker, nullptr, EVENT_BATTLED, nullptr, 0, PLAYER_NONE, 0, 0);
card_set battled_cards{ core.attacker };
if(core.attack_target) {
battled_cards.insert(core.attack_target);
raise_single_event(core.attack_target, nullptr, EVENT_BATTLED, nullptr, 0, PLAYER_NONE, 0, 1);
}
raise_event(std::move(battled_cards), EVENT_BATTLED, nullptr, 0, PLAYER_NONE, 0, 0);
process_single_event();
process_instant_event();
if(core.effect_damage_step) {
if(core.reserved) {
auto* damage_step = Processors::get_opt_variant<Processors::DamageStep>(*core.reserved);
if(damage_step)
damage_step->cards_destroyed_by_battle = arg.cards_destroyed_by_battle;
}
return TRUE;
}
arg.step = 32;
[[fallthrough]];
}
case 32: {
auto message = pduel->new_message(MSG_HINT);
message->write<uint8_t>(HINT_EVENT);
message->write<uint8_t>(0);
message->write<uint64_t>(43);
message = pduel->new_message(MSG_HINT);
message->write<uint8_t>(HINT_EVENT);
message->write<uint8_t>(1);
message->write<uint64_t>(43);
core.hint_timing[0] |= TIMING_BATTLED;
core.hint_timing[1] |= TIMING_BATTLED;
emplace_process<Processors::PointEvent>(false, false, is_flag(DUEL_SINGLE_CHAIN_IN_DAMAGE_SUBSTEP));
return FALSE;
}
case 33: {
auto des = arg.cards_destroyed_by_battle;
if(des) {
for(auto cit = des->container.begin(); cit != des->container.end();) {
auto rm = cit++;
if((*rm)->current.location != LOCATION_MZONE || ((*rm)->fieldid_r != core.pre_field[0] && (*rm)->fieldid_r != core.pre_field[1]))
des->container.erase(rm);
}
emplace_process<Processors::Destroy>(Step{ 3 }, des, nullptr, REASON_BATTLE, PLAYER_NONE);
}
adjust_all();
return FALSE;
}
case 34: {
arg.cards_destroyed_by_battle = nullptr;
// for unexpected end of damage step
core.damage_calculated = true;
core.selfdes_disabled = false;
core.flip_delayed = false;
core.new_fchain.splice(core.new_fchain.begin(), core.new_fchain_b);
core.new_ochain.splice(core.new_ochain.begin(), core.new_ochain_b);
card_set ing;
card_set ed;
if(core.attacker->is_status(STATUS_BATTLE_DESTROYED) && (core.attacker->current.reason & REASON_BATTLE)) {
raise_single_event(core.attack_target, nullptr, EVENT_BATTLE_DESTROYING, nullptr, core.attacker->current.reason, core.attack_target->current.controler, 0, 1);
raise_single_event(core.attacker, nullptr, EVENT_BATTLE_DESTROYED, nullptr, core.attacker->current.reason, core.attack_target->current.controler, 0, 0);
raise_single_event(core.attacker, nullptr, EVENT_DESTROYED, nullptr, core.attacker->current.reason, core.attack_target->current.controler, 0, 0);
ing.insert(core.attack_target);
ed.insert(core.attacker);
}
if(core.attack_target && core.attack_target->is_status(STATUS_BATTLE_DESTROYED) && (core.attack_target->current.reason & REASON_BATTLE)) {
raise_single_event(core.attacker, nullptr, EVENT_BATTLE_DESTROYING, nullptr, core.attack_target->current.reason, core.attacker->current.controler, 0, 0);
raise_single_event(core.attack_target, nullptr, EVENT_BATTLE_DESTROYED, nullptr, core.attack_target->current.reason, core.attacker->current.controler, 0, 1);
raise_single_event(core.attack_target, nullptr, EVENT_DESTROYED, nullptr, core.attack_target->current.reason, core.attacker->current.controler, 0, 1);
ing.insert(core.attacker);
ed.insert(core.attack_target);
}
if(ing.size())
raise_event(std::move(ing), EVENT_BATTLE_DESTROYING, nullptr, 0, 0, 0, 0);
if(ed.size()) {
raise_event(ed, EVENT_BATTLE_DESTROYED, nullptr, 0, 0, 0, 0);
raise_event(std::move(ed), EVENT_DESTROYED, nullptr, 0, 0, 0, 0);
}
raise_single_event(core.attacker, nullptr, EVENT_DAMAGE_STEP_END, nullptr, 0, 0, 0, 0);
if(core.attack_target)
raise_single_event(core.attack_target, nullptr, EVENT_DAMAGE_STEP_END, nullptr, 0, 0, 0, 1);
raise_event(nullptr, EVENT_DAMAGE_STEP_END, nullptr, 0, 0, 0, 0);
core.attacker->set_status(STATUS_BATTLE_DESTROYED, FALSE);
if(core.attack_target)
core.attack_target->set_status(STATUS_BATTLE_DESTROYED, FALSE);
process_single_event();
process_instant_event();
auto message = pduel->new_message(MSG_HINT);
message->write<uint8_t>(HINT_EVENT);
message->write<uint8_t>(0);
message->write<uint64_t>(44);
message = pduel->new_message(MSG_HINT);
message->write<uint8_t>(HINT_EVENT);
message->write<uint8_t>(1);
message->write<uint64_t>(44);
emplace_process<Processors::PointEvent>(false, false, true);
arg.step = 38;
return FALSE;
}
case 38: {
return FALSE;
}
case 39: {
//end of damage step
core.attacker->set_status(STATUS_OPPO_BATTLE, FALSE);
if(core.attack_target)
core.attack_target->set_status(STATUS_OPPO_BATTLE, FALSE);
if(arg.forced_attack) {
arg.forced_attack_done = true;
arg.step = 0;
} else
arg.step = Processors::restart;
infos.phase = PHASE_BATTLE_STEP;
pduel->new_message(MSG_DAMAGE_STEP_END);
reset_phase(PHASE_DAMAGE);
adjust_all();
if(core.effect_damage_step)
return TRUE;
return FALSE;
}
case 40: {
for(auto& ch_lim : core.chain_limit)
ensure_luaL_stack(luaL_unref, pduel->lua->lua_state, LUA_REGISTRYINDEX, ch_lim.function);
core.chain_limit.clear();
if(core.current_chain.size()) {
for(auto& ch : core.current_chain)
ch.triggering_effect->get_handler()->set_status(STATUS_CHAINING, FALSE);
emplace_process<Processors::SolveChain>(false, false, false);
if(!arg.forced_attack)
arg.step = Processors::restart;
return FALSE;
}
reset_phase(PHASE_BATTLE_STEP);
adjust_all();
return FALSE;
}
case 41: {
// normal end of battle step
arg.second_battle_phase_is_optional = true;
effect_set eset;
filter_player_effect(infos.turn_player, EFFECT_BP_TWICE, &eset, false);
for(const auto& peff : eset) {
if(!peff->value || peff->get_value() != 1) {
arg.second_battle_phase_is_optional = false;
break;
}
}
arg.repeat_battle_phase = !eset.empty();
infos.phase = PHASE_BATTLE;
emplace_process<Processors::PhaseEvent>(PHASE_BATTLE);
adjust_all();
return FALSE;
}
case 42: {
core.attacker = nullptr;
core.attack_target = nullptr;
if(arg.repeat_battle_phase && arg.second_battle_phase_is_optional) {
emplace_process<Processors::SelectYesNo>(infos.turn_player, 32);
return FALSE;
}
returns.set<int32_t>(0, arg.phase_to_change_to);
returns.set<int32_t>(1, arg.repeat_battle_phase);
return TRUE;
}
case 43: {
auto bp_twice = returns.at<int32_t>(0);
returns.set<int32_t>(0, arg.phase_to_change_to);
returns.set<int32_t>(1, bp_twice);
return TRUE;
}
}
return TRUE;
}
bool field::process(Processors::ForcedBattle& arg) {
switch(arg.step) {
case 0: {
if (is_player_affected_by_effect(infos.turn_player, EFFECT_CANNOT_BP))
return TRUE;
++core.battle_phase_count[infos.turn_player];
if (is_player_affected_by_effect(infos.turn_player, EFFECT_SKIP_BP) || core.force_turn_end) {
auto message = pduel->new_message(MSG_NEW_PHASE);
message->write<uint16_t>(PHASE_BATTLE_START);
reset_phase(PHASE_BATTLE_START);
reset_phase(PHASE_BATTLE_STEP);
reset_phase(PHASE_BATTLE);
adjust_all();
message = pduel->new_message(MSG_NEW_PHASE);
message->write<uint16_t>(infos.phase);
return TRUE;
}
arg.backup_phase = infos.phase;
auto tmp_attacker = core.forced_attacker;
auto tmp_attack_target = core.forced_attack_target;
if(!tmp_attacker->is_capable_attack_announce(infos.turn_player))
return TRUE;
card_vector cv;
get_attack_target(tmp_attacker, &cv);
if((cv.size() == 0 && tmp_attacker->direct_attackable == 0) || (tmp_attack_target && std::find(cv.begin(), cv.end(), tmp_attack_target)==cv.end()))
return TRUE;
core.attacker = tmp_attacker;
core.attack_target = tmp_attack_target;
for(uint8_t p = 0; p < 2; ++p) {
for(auto& pcard : player[p].list_mzone) {
if(!pcard)
continue;
pcard->attack_announce_count = 0;
pcard->announce_count = 0;
pcard->attacked_count = 0;
pcard->announced_cards.clear();
pcard->attacked_cards.clear();
pcard->battled_cards.clear();
}
}
core.attack_cancelable = true;
core.attack_cost_paid = FALSE;
core.chain_attacker_id = 0;
core.chain_attack_target = nullptr;
returns.set<int32_t>(0, 1);
reset_phase(infos.phase);
reset_phase(PHASE_BATTLE_START);
infos.phase = PHASE_BATTLE_STEP;
core.new_fchain.clear();
core.new_ochain.clear();
core.quick_f_chain.clear();
core.delayed_quick_tmp.clear();
auto message = pduel->new_message(MSG_NEW_PHASE);
message->write<uint16_t>(PHASE_BATTLE_START);
emplace_process<Processors::BattleCommand>(Step{ 1 }, nullptr, true);
return FALSE;
}
case 1: {
reset_phase(infos.phase);
infos.phase = arg.backup_phase;
core.new_fchain.clear();
core.new_ochain.clear();
core.quick_f_chain.clear();
core.delayed_quick_tmp.clear();
for(uint8_t p = 0; p < 2; ++p) {
for(auto& pcard : player[p].list_mzone) {
if(!pcard)
continue;
pcard->attack_announce_count = 0;
pcard->announce_count = 0;
pcard->attacked_count = 0;
pcard->announced_cards.clear();
pcard->attacked_cards.clear();
pcard->battled_cards.clear();
}
}
core.attacker = nullptr;
core.attack_target = nullptr;
auto message = pduel->new_message(MSG_NEW_PHASE);
message->write<uint16_t>(infos.phase);
return TRUE;
}
}
return TRUE;
}
bool field::process(Processors::DamageStep& arg) {
auto new_attack = arg.new_attack;
switch(arg.step) {
case 0: {
if(core.effect_damage_step && !new_attack)
return TRUE;
core.effect_damage_step = 1;
std::swap(core.attacker, arg.attacker);
std::swap(core.attack_target, arg.attack_target);
arg.backup_phase = infos.phase;
if(core.attacker->current.location != LOCATION_MZONE || (core.attack_target && core.attack_target->current.location != LOCATION_MZONE)) {
arg.step = 2;
return FALSE;
}
if(new_attack) {
++core.attack_state_count[infos.turn_player];
++core.battled_count[infos.turn_player];
check_card_counter(core.attacker, ACTIVITY_ATTACK, infos.turn_player);
}
core.attacker->announced_cards.addcard(core.attack_target);
attack_all_target_check();
auto message = pduel->new_message(MSG_ATTACK);
message->write(core.attacker->get_info_location());
if(core.attack_target) {
message->write(core.attack_target->get_info_location());
} else {
message->write(loc_info{});
}
infos.phase = PHASE_DAMAGE;
(void)pduel->new_message(MSG_DAMAGE_STEP_START);
core.pre_field[0] = core.attacker->fieldid_r;
++core.attacker->attacked_count;
if(core.attack_target) {
core.pre_field[1] = core.attack_target->fieldid_r;
if(core.attack_target->is_position(POS_FACEDOWN)) {
change_position(core.attack_target, nullptr, PLAYER_NONE, core.attack_target->current.position >> 1, 0, TRUE);
adjust_all();
}
} else
core.pre_field[1] = 0;
return FALSE;
}
case 1: {
infos.phase = PHASE_DAMAGE_CAL;
emplace_process<Processors::BattleCommand>(Step{ 26 });
arg.step = 2;
core.reserved = std::move(arg);
return TRUE;
}
case 2: {
core.effect_damage_step = 2;
emplace_process<Processors::BattleCommand>(Step{ 32 }, arg.cards_destroyed_by_battle);
return FALSE;
}
case 3: {
std::swap(core.attacker, arg.attacker);
std::swap(core.attack_target, arg.attack_target);
if(core.attacker)
core.attacker->set_status(STATUS_ATTACK_CANCELED, TRUE);
if(core.attack_target)
core.attack_target->set_status(STATUS_ATTACK_CANCELED, TRUE);
core.effect_damage_step = 0;
infos.phase = arg.backup_phase;
return TRUE;
}
}
return TRUE;
}
void field::calculate_battle_damage(effect** pdamchange, card** preason_card, std::array<bool, 2>* battle_destroyed) {
uint32_t aa = core.attacker->get_attack(), ad = core.attacker->get_defense();
uint32_t da = 0, dd = 0, a = aa, d;
uint8_t pa = core.attacker->current.controler, pd;
uint8_t damp = 0;
effect* damchange = nullptr;
card* reason_card = nullptr;
std::array<bool, 2> bd{};
bool pierce = false;
core.battle_damage[0] = core.battle_damage[1] = 0;
if(core.attacker->is_position(POS_FACEUP_DEFENSE)) {
effect* defattack = core.attacker->is_affected_by_effect(EFFECT_DEFENSE_ATTACK);
if(defattack && defattack->get_value(core.attacker))
a = ad;
}
effect* battstat = core.attacker->is_affected_by_effect(EFFECT_CHANGE_BATTLE_STAT);
if(battstat)
a = battstat->get_value(core.attacker);
if(core.attack_target) {
da = core.attack_target->get_attack();
dd = core.attack_target->get_defense();
pd = core.attack_target->current.controler;
battstat = core.attack_target->is_affected_by_effect(EFFECT_CHANGE_BATTLE_STAT);
if(battstat)
d = battstat->get_value(core.attack_target);
else if (core.attack_target->is_position(POS_ATTACK))
d = da;
else
d = dd;
if(core.attack_target->is_position(POS_ATTACK)) {
if(a > d) {
damp = pd;
core.battle_damage[damp] = a - d;
reason_card = core.attacker;
bd[1] = true;
} else if(a < d) {
damp = pa;
core.battle_damage[damp] = d - a;
reason_card = core.attack_target;
bd[0] = true;
} else {
if(a != 0 || is_flag(DUEL_0_ATK_DESTROYED)) {
bd[0] = bd[1] = true;
}
}
} else {
if(a > d) {
effect_set eset;
core.attacker->filter_effect(EFFECT_PIERCE, &eset);
if(eset.size()) {
pierce = true;
uint8_t dp[2] = {};
for(const auto& peff : eset)
dp[1 - peff->get_handler_player()] = 1;
if(dp[0])
core.battle_damage[0] = a - d;
if(dp[1])
core.battle_damage[1] = a - d;
bool double_damage = false;
//bool half_damage = false;
for(const auto& peff : eset) {
if(peff->get_value() == DOUBLE_DAMAGE)
double_damage = true;
//if(peff->get_value() == HALF_DAMAGE)
// half_damage = true;
}
//if(double_damage && half_damage) {
// double_damage = false;
// half_damage = false;
//}
if(double_damage) {
if(dp[0])
core.battle_damage[0] *= 2;
if(dp[1])
core.battle_damage[1] *= 2;
}
//if(half_damage) {
// if(dp[0])
// core.battle_damage[0] /= 2;
// if(dp[1])
// core.battle_damage[1] /= 2;
//}
bool both = dp[0] && dp[1];
if(!both) {
damp = dp[0] ? 0 : 1;
if(core.attacker->is_affected_by_effect(EFFECT_BOTH_BATTLE_DAMAGE)
|| core.attack_target->is_affected_by_effect(EFFECT_BOTH_BATTLE_DAMAGE)) {
core.battle_damage[1 - damp] = core.battle_damage[damp];
both = true;
}
}
effect* reflect[2] = {};
if((reflect[pd] = core.attack_target->is_affected_by_effect(EFFECT_REFLECT_BATTLE_DAMAGE, core.attacker)) == nullptr)
reflect[pd] = is_player_affected_by_effect(pd, EFFECT_REFLECT_BATTLE_DAMAGE);
if((reflect[1 - pd] = core.attacker->is_affected_by_effect(EFFECT_REFLECT_BATTLE_DAMAGE, core.attack_target)) == nullptr)
reflect[1 - pd] = is_player_affected_by_effect(1 - pd, EFFECT_REFLECT_BATTLE_DAMAGE);
bool also[2] = { false, false };
if(!both
&& (core.attack_target->is_affected_by_effect(EFFECT_ALSO_BATTLE_DAMAGE)
|| is_player_affected_by_effect(pd, EFFECT_ALSO_BATTLE_DAMAGE)))
also[pd] = true;
if(!both
&& (core.attacker->is_affected_by_effect(EFFECT_ALSO_BATTLE_DAMAGE)
|| is_player_affected_by_effect(1 - pd, EFFECT_ALSO_BATTLE_DAMAGE)))
also[1 - pd] = true;
if(both) {
//turn player's effect applies first
if(reflect[pa] && reflect[pa]->get_handler_player() == pa) {
core.battle_damage[1 - pa] += core.battle_damage[pa];
core.battle_damage[pa] = 0;
} else if(reflect[1 - pa] && reflect[1 - pa]->get_handler_player() == pa) {
core.battle_damage[pa] += core.battle_damage[1 - pa];
core.battle_damage[1 - pa] = 0;
} else if(reflect[pa] && reflect[pa]->get_handler_player() == 1 - pa) {
core.battle_damage[1 - pa] += core.battle_damage[pa];
core.battle_damage[pa] = 0;
} else if(reflect[1 - pa] && reflect[1 - pa]->get_handler_player() == 1 - pa) {
core.battle_damage[pa] += core.battle_damage[1 - pa];
core.battle_damage[1 - pa] = 0;
}
} else {
if(reflect[damp]) {
if(!also[1 - damp]) {
core.battle_damage[1 - damp] += core.battle_damage[damp];
core.battle_damage[damp] = 0;
} else {
core.battle_damage[1 - damp] += core.battle_damage[damp];
core.battle_damage[damp] = core.battle_damage[1 - damp];
}
} else if(also[damp]) {
if(!reflect[1 - damp]) {
core.battle_damage[1 - damp] += core.battle_damage[damp];
} else {
core.battle_damage[1 - damp] += core.battle_damage[damp];
core.battle_damage[damp] += core.battle_damage[1 - damp];
core.battle_damage[1 - damp] = 0;
}
}
}
eset.clear();
core.attacker->filter_effect(EFFECT_CHANGE_BATTLE_DAMAGE, &eset, FALSE);
core.attack_target->filter_effect(EFFECT_CHANGE_BATTLE_DAMAGE, &eset, FALSE);
filter_player_effect(pa, EFFECT_CHANGE_BATTLE_DAMAGE, &eset, false);
filter_player_effect(1 - pa, EFFECT_CHANGE_BATTLE_DAMAGE, &eset, false);
std::sort(eset.begin(), eset.end(), effect_sort_id);
for(uint8_t p = 0; p < 2; ++p) {
bool double_dam = false;
bool half_dam = false;
int32_t dam_value = -1;
for(const auto& peff : eset) {
lua_Integer val = -1;
if(!peff->is_flag(EFFECT_FLAG_PLAYER_TARGET)) {
pduel->lua->add_param<LuaParam::INT>(p);
pduel->lua->add_param<LuaParam::CARD>(core.attacker);
val = peff->get_value(2);
} else if(peff->is_target_player(p)) {
pduel->lua->add_param<LuaParam::CARD>(core.attacker);
val = peff->get_value(1);
}
if(val == DOUBLE_DAMAGE) {
double_dam = true;
} else if(val == HALF_DAMAGE) {
half_dam = true;
} else if(val > 0) {
dam_value = static_cast<int32_t>(val);
} else if(val == 0) {
dam_value = 0;
break;
}
}
if(double_dam && half_dam) {
double_dam = false;
half_dam = false;
}
if(double_dam)
core.battle_damage[p] *= 2;
if(half_dam)
core.battle_damage[p] /= 2;
if(dam_value >= 0 && core.battle_damage[p] > 0)
core.battle_damage[p] = dam_value;
}
if(core.attacker->is_affected_by_effect(EFFECT_NO_BATTLE_DAMAGE)
|| core.attack_target->is_affected_by_effect(EFFECT_AVOID_BATTLE_DAMAGE, core.attacker)
|| is_player_affected_by_effect(pd, EFFECT_AVOID_BATTLE_DAMAGE))
core.battle_damage[pd] = 0;
if(core.attack_target->is_affected_by_effect(EFFECT_NO_BATTLE_DAMAGE)
|| core.attacker->is_affected_by_effect(EFFECT_AVOID_BATTLE_DAMAGE, core.attack_target)
|| is_player_affected_by_effect(1 - pd, EFFECT_AVOID_BATTLE_DAMAGE))
core.battle_damage[1 - pd] = 0;
reason_card = core.attacker;
}
bd[1] = true;
} else if(a < d) {
damp = pa;
core.battle_damage[damp] = d - a;
reason_card = core.attack_target;
}
}
} else {
if(a != 0) {
damp = 1 - pa;
core.battle_damage[damp] = a;
reason_card = core.attacker;
}
}
if(reason_card && !pierce
&& (damchange = reason_card->is_affected_by_effect(EFFECT_BATTLE_DAMAGE_TO_EFFECT)) == nullptr) {
card* dam_card = (reason_card == core.attacker) ? core.attack_target : core.attacker;
bool both = false;
if(reason_card->is_affected_by_effect(EFFECT_BOTH_BATTLE_DAMAGE)
|| (dam_card && dam_card->is_affected_by_effect(EFFECT_BOTH_BATTLE_DAMAGE))) {
core.battle_damage[1 - damp] = core.battle_damage[damp];
both = true;
}
effect* reflect[2] = {};
if(!dam_card || (reflect[damp] = dam_card->is_affected_by_effect(EFFECT_REFLECT_BATTLE_DAMAGE, reason_card)) == nullptr)
reflect[damp] = is_player_affected_by_effect(damp, EFFECT_REFLECT_BATTLE_DAMAGE);
if((reflect[1 - damp] = reason_card->is_affected_by_effect(EFFECT_REFLECT_BATTLE_DAMAGE, dam_card)) == nullptr)
reflect[1 - damp] = is_player_affected_by_effect(1 - damp, EFFECT_REFLECT_BATTLE_DAMAGE);
bool also[2] = { false, false };
if(!both
&& ((dam_card && dam_card->is_affected_by_effect(EFFECT_ALSO_BATTLE_DAMAGE))
|| is_player_affected_by_effect(damp, EFFECT_ALSO_BATTLE_DAMAGE)))
also[damp] = true;
if(!both
&& (reason_card->is_affected_by_effect(EFFECT_ALSO_BATTLE_DAMAGE)
|| is_player_affected_by_effect(1 - damp, EFFECT_ALSO_BATTLE_DAMAGE)))
also[1 - damp] = true;
if(both) {
//turn player's effect applies first
if(reflect[pa] && reflect[pa]->get_handler_player() == pa) {
core.battle_damage[1 - pa] += core.battle_damage[pa];
core.battle_damage[pa] = 0;
} else if(reflect[1 - pa] && reflect[1 - pa]->get_handler_player() == pa) {
core.battle_damage[pa] += core.battle_damage[1 - pa];
core.battle_damage[1 - pa] = 0;
} else if(reflect[pa] && reflect[pa]->get_handler_player() == 1 - pa) {
core.battle_damage[1 - pa] += core.battle_damage[pa];
core.battle_damage[pa] = 0;
} else if(reflect[1 - pa] && reflect[1 - pa]->get_handler_player() == 1 - pa) {
core.battle_damage[pa] += core.battle_damage[1 - pa];
core.battle_damage[1 - pa] = 0;
}
} else {
if(reflect[damp]) {
if(!also[1 - damp]) {
core.battle_damage[1 - damp] += core.battle_damage[damp];
core.battle_damage[damp] = 0;
} else {
core.battle_damage[1 - damp] += core.battle_damage[damp];
core.battle_damage[damp] = core.battle_damage[1 - damp];
}
} else if(also[damp]) {
if(!reflect[1 - damp]) {
core.battle_damage[1 - damp] += core.battle_damage[damp];
} else {
core.battle_damage[1 - damp] += core.battle_damage[damp];
core.battle_damage[damp] += core.battle_damage[1 - damp];
core.battle_damage[1 - damp] = 0;
}
}
}
effect_set eset;
reason_card->filter_effect(EFFECT_CHANGE_BATTLE_DAMAGE, &eset, FALSE);
if(dam_card)
dam_card->filter_effect(EFFECT_CHANGE_BATTLE_DAMAGE, &eset, FALSE);
filter_player_effect(damp, EFFECT_CHANGE_BATTLE_DAMAGE, &eset, false);
filter_player_effect(1 - damp, EFFECT_CHANGE_BATTLE_DAMAGE, &eset, false);
std::sort(eset.begin(), eset.end(), effect_sort_id);
for(uint8_t p = 0; p < 2; ++p) {
bool double_dam = false;
bool half_dam = false;
int32_t dam_value = -1;
for(const auto& peff : eset) {
lua_Integer val = -1;
if(!peff->is_flag(EFFECT_FLAG_PLAYER_TARGET)) {
pduel->lua->add_param<LuaParam::INT>(p);
pduel->lua->add_param<LuaParam::CARD>(reason_card);
val = peff->get_value(2);
} else if(peff->is_target_player(p)) {
pduel->lua->add_param<LuaParam::CARD>(reason_card);
val = peff->get_value(1);
}
if(val == DOUBLE_DAMAGE) {
double_dam = true;
} else if(val == HALF_DAMAGE) {
half_dam = true;
} else if(val > 0) {
dam_value = static_cast<int32_t>(val);
} else if(val == 0) {
dam_value = 0;
break;
}
}
if(double_dam && half_dam) {
double_dam = false;
half_dam = false;
}
if(double_dam)
core.battle_damage[p] *= 2;
if(half_dam)
core.battle_damage[p] /= 2;
if(dam_value >= 0 && core.battle_damage[p] > 0)
core.battle_damage[p] = dam_value;
}
if(reason_card->is_affected_by_effect(EFFECT_NO_BATTLE_DAMAGE)
|| (dam_card && dam_card->is_affected_by_effect(EFFECT_AVOID_BATTLE_DAMAGE, reason_card))
|| is_player_affected_by_effect(damp, EFFECT_AVOID_BATTLE_DAMAGE))
core.battle_damage[damp] = 0;
if((dam_card && dam_card->is_affected_by_effect(EFFECT_NO_BATTLE_DAMAGE))
|| reason_card->is_affected_by_effect(EFFECT_AVOID_BATTLE_DAMAGE, dam_card)
|| is_player_affected_by_effect(1 - damp, EFFECT_AVOID_BATTLE_DAMAGE))
core.battle_damage[1 - damp] = 0;
}
if(!core.battle_damage[damp] && !core.battle_damage[1 - damp])
reason_card = nullptr;
if(pdamchange)
*pdamchange = damchange;
if(preason_card)
*preason_card = reason_card;
if(battle_destroyed) {
*battle_destroyed = bd;
}
}
bool field::process(Processors::Turn& arg) {
const auto& turn_player = arg.turn_player;
switch(arg.step) {
case 0: {
//Pre Draw
core.used_event.clear();
for(auto& peffect : core.reseted_effects) {
pduel->delete_effect(peffect);
}
core.reseted_effects.clear();
core.effect_count_code.clear();
for(uint8_t p = 0; p < 2; ++p) {
for(auto& pcard : player[p].list_mzone) {
if(!pcard)
continue;
pcard->set_status(STATUS_SUMMON_TURN, FALSE);
pcard->set_status(STATUS_FLIP_SUMMON_TURN, FALSE);
pcard->set_status(STATUS_SPSUMMON_TURN, FALSE);
pcard->set_status(STATUS_SET_TURN, FALSE);
pcard->set_status(STATUS_FORM_CHANGED, FALSE);
pcard->indestructable_effects.clear();
pcard->attack_announce_count = 0;
pcard->announce_count = 0;
pcard->attacked_count = 0;
pcard->announced_cards.clear();
pcard->attacked_cards.clear();
pcard->battled_cards.clear();
pcard->attack_all_target = TRUE;
}
for(auto& pcard : player[p].list_szone) {
if(!pcard)
continue;
pcard->set_status(STATUS_SET_TURN, FALSE);
pcard->indestructable_effects.clear();
}
core.summon_state_count[p] = 0;
core.normalsummon_state_count[p] = 0;
core.flipsummon_state_count[p] = 0;
core.spsummon_state_count[p] = 0;
core.spsummon_state_count_rst[p] = 0;
core.attack_state_count[p] = 0;
core.battle_phase_count[p] = 0;
core.battled_count[p] = 0;
core.summon_count[p] = 0;
core.extra_summon[p] = 0;
core.spsummon_once_map[p].clear();
core.spsummon_once_map_rst[p].clear();
}
emplace_process<Processors::RefreshRelay>();
return FALSE;
}
case 1: {
core.force_turn_end = false;
core.spsummon_rst = false;
for(auto& peffect : effects.rechargeable)
if(!peffect->is_flag(EFFECT_FLAG_NO_TURN_RESET))
peffect->recharge();
auto clear_counter = [](processor::action_counter_t& counter) {
for(auto& iter : counter)
iter.second.player_amount[0] = iter.second.player_amount[1] = 0;
};
clear_counter(core.summon_counter);
clear_counter(core.normalsummon_counter);
clear_counter(core.spsummon_counter);
clear_counter(core.flipsummon_counter);
clear_counter(core.attack_counter);
clear_counter(core.chain_counter);
for(auto& peffect : effects.spsummon_count_eff) {
card* pcard = peffect->get_handler();
if(!peffect->is_flag(EFFECT_FLAG_NO_TURN_RESET)) {
pcard->spsummon_counter[0] = pcard->spsummon_counter[1] = 0;
pcard->spsummon_counter_rst[0] = pcard->spsummon_counter_rst[1] = 0;
}
}
++infos.turn_id;
++infos.turn_id_by_player[turn_player];
infos.turn_player = turn_player;
auto message = pduel->new_message(MSG_NEW_TURN);
message->write<uint8_t>(turn_player);
if(!is_flag(DUEL_RELAY) && infos.turn_id != 1)
tag_swap(turn_player);
if(is_player_affected_by_effect(infos.turn_player, EFFECT_SKIP_TURN)) {
arg.step = 18;
reset_phase(PHASE_DRAW);
reset_phase(PHASE_STANDBY);
reset_phase(PHASE_END);
adjust_all();
return FALSE;
}
infos.phase = PHASE_DRAW;
core.phase_action = false;
core.hand_adjusted = false;
raise_event(nullptr, EVENT_PHASE_START + PHASE_DRAW, nullptr, 0, 0, turn_player, 0);
process_instant_event();
adjust_all();
return FALSE;
}
case 2: {
core.new_fchain.clear();
core.new_ochain.clear();
core.quick_f_chain.clear();
core.delayed_quick_tmp.clear();
if(is_player_affected_by_effect(infos.turn_player, EFFECT_SKIP_DP) || core.force_turn_end) {
arg.step = 3;
reset_phase(PHASE_DRAW);
adjust_all();
return FALSE;
}
auto message = pduel->new_message(MSG_NEW_PHASE);
message->write<uint16_t>(infos.phase);
raise_event(nullptr, EVENT_PREDRAW, nullptr, 0, 0, turn_player, 0);
process_instant_event();
message = pduel->new_message(MSG_HINT);
message->write<uint8_t>(HINT_EVENT);
message->write<uint8_t>(turn_player);
message->write<uint64_t>(27);
if(core.new_fchain.size() || core.new_ochain.size())
emplace_process<Processors::PointEvent>(false, true, false);
/*if(core.set_forced_attack)
emplace_process<Processors::ForcedBattle>();*/
return FALSE;
}
case 3: {
// Draw, new ruling
if(is_flag(DUEL_1ST_TURN_DRAW) || (infos.turn_id > 1)) {
int32_t count = get_draw_count(infos.turn_player);
if(count > 0 && is_flag(DUEL_DRAW_UNTIL_5)) {
count = std::max<int32_t>(static_cast<int32_t>(5 - player[turn_player].list_hand.size()), count);
}
if(count > 0) {
draw(nullptr, REASON_RULE, turn_player, turn_player, count);
emplace_process<Processors::PointEvent>(false, false, false);
}
}
emplace_process<Processors::PhaseEvent>(PHASE_DRAW);
/*if(core.set_forced_attack)
emplace_process<Processors::ForcedBattle>();*/
return FALSE;
}
case 4: {
// EVENT_PHASE_PRESTART is removed
return FALSE;
}
case 5: {
//Standby Phase
infos.phase = PHASE_STANDBY;
core.phase_action = false;
core.new_fchain.clear();
core.new_ochain.clear();
core.quick_f_chain.clear();
core.delayed_quick_tmp.clear();
if(is_flag(DUEL_NO_STANDBY_PHASE) || is_player_affected_by_effect(infos.turn_player, EFFECT_SKIP_SP) || core.force_turn_end) {
arg.step = 6;
reset_phase(PHASE_STANDBY);
adjust_all();
return FALSE;
}
auto message = pduel->new_message(MSG_NEW_PHASE);
message->write<uint16_t>(infos.phase);
raise_event(nullptr, EVENT_PHASE_START + PHASE_STANDBY, nullptr, 0, 0, turn_player, 0);
process_instant_event();
/*if(core.set_forced_attack)
emplace_process<Processors::ForcedBattle>();*/
return FALSE;
}
case 6: {
// EVENT_PHASE_START + PHASE_STANDBY is a special case(c89642993)
if(core.new_fchain.size() || core.new_ochain.size() || core.instant_event.back().event_code != EVENT_PHASE_START + PHASE_STANDBY)
emplace_process<Processors::PointEvent>(false, false, false);
emplace_process<Processors::PhaseEvent>(PHASE_STANDBY);
/*if(core.set_forced_attack)
emplace_process<Processors::ForcedBattle>();*/
return FALSE;
}
case 7: {
//Main1
infos.phase = PHASE_MAIN1;
core.phase_action = false;
raise_event(nullptr, EVENT_PHASE_START + PHASE_MAIN1, nullptr, 0, 0, turn_player, 0);
process_instant_event();
adjust_all();
/*if(core.set_forced_attack)
emplace_process<Processors::ForcedBattle>();*/
return FALSE;
}
case 8: {
return FALSE;
}
case 9: {
core.new_fchain.clear();
core.new_ochain.clear();
core.quick_f_chain.clear();
core.delayed_quick_tmp.clear();
auto message = pduel->new_message(MSG_NEW_PHASE);
message->write<uint16_t>(infos.phase);
emplace_process<Processors::IdleCommand>();
/*if(core.set_forced_attack)
emplace_process<Processors::ForcedBattle>();*/
return FALSE;
}
case 10: {
if(returns.at<int32_t>(0) == 7) { // End Phase
arg.step = 15;
return FALSE;
}
infos.phase = PHASE_BATTLE_START;
core.new_fchain.clear();
core.new_ochain.clear();
core.quick_f_chain.clear();
core.delayed_quick_tmp.clear();
core.phase_action = false;
++core.battle_phase_count[infos.turn_player];
auto message = pduel->new_message(MSG_NEW_PHASE);
message->write<uint16_t>(infos.phase);
// Show the texts to indicate that BP is entered and skipped
if(is_player_affected_by_effect(infos.turn_player, EFFECT_SKIP_BP) || core.force_turn_end) {
arg.step = 15;
reset_phase(PHASE_BATTLE_START);
reset_phase(PHASE_BATTLE_STEP);
reset_phase(PHASE_BATTLE);
adjust_all();
/*if(core.set_forced_attack)
emplace_process<Processors::ForcedBattle>();*/
return FALSE;
}
raise_event(nullptr, EVENT_PHASE_START + PHASE_BATTLE_START, nullptr, 0, 0, turn_player, 0);
process_instant_event();
adjust_all();
/*if(core.set_forced_attack)
emplace_process<Processors::ForcedBattle>();*/
return FALSE;
}
case 11: {
if(core.new_fchain.size() || core.new_ochain.size())
emplace_process<Processors::PointEvent>(false, false, false);
emplace_process<Processors::PhaseEvent>(PHASE_BATTLE_START);
/*if(core.set_forced_attack)
emplace_process<Processors::ForcedBattle>();*/
return FALSE;
}
case 12: {
infos.phase = PHASE_BATTLE_STEP;
core.new_fchain.clear();
core.new_ochain.clear();
core.quick_f_chain.clear();
core.delayed_quick_tmp.clear();
core.phase_action = false;
core.chain_attack = false;
emplace_process<Processors::BattleCommand>();
/*if(core.set_forced_attack)
emplace_process<Processors::ForcedBattle>();*/
return FALSE;
}
case 13: {
if(!arg.has_performed_second_battle_phase && returns.at<int32_t>(1)) { // 2nd Battle Phase
arg.has_performed_second_battle_phase = true;
arg.step = 9;
for(uint8_t p = 0; p < 2; ++p) {
for(auto& pcard : player[p].list_mzone) {
if(!pcard)
continue;
pcard->attack_announce_count = 0;
pcard->announce_count = 0;
pcard->attacked_count = 0;
pcard->announced_cards.clear();
pcard->attacked_cards.clear();
pcard->battled_cards.clear();
}
}
return FALSE;
}
arg.has_performed_second_battle_phase = false;
if(is_flag(DUEL_NO_MAIN_PHASE_2)) {
arg.step = 15;
adjust_all();
/*if(core.set_forced_attack)
emplace_process<Processors::ForcedBattle>();*/
return FALSE;
}
core.skip_m2 = false;
if(returns.at<int32_t>(0) == 3 || core.force_turn_end) { // End Phase
core.skip_m2 = true;
}
//Main2
infos.phase = PHASE_MAIN2;
core.phase_action = false;
raise_event(nullptr, EVENT_PHASE_START + PHASE_MAIN2, nullptr, 0, 0, turn_player, 0);
process_instant_event();
adjust_all();
/*if(core.set_forced_attack)
emplace_process<Processors::ForcedBattle>();*/
return FALSE;
}
case 14: {
if(core.new_fchain.size() || core.new_ochain.size())
emplace_process<Processors::PointEvent>(false, false, false);
/*if(core.set_forced_attack)
emplace_process<Processors::ForcedBattle>();*/
return FALSE;
}
case 15: {
core.new_fchain.clear();
core.new_ochain.clear();
core.quick_f_chain.clear();
core.delayed_quick_tmp.clear();
auto message = pduel->new_message(MSG_NEW_PHASE);
message->write<uint16_t>(infos.phase);
infos.can_shuffle = true;
emplace_process<Processors::IdleCommand>();
/*if(core.set_forced_attack)
emplace_process<Processors::ForcedBattle>();*/
return FALSE;
}
case 16: {
//End Phase
infos.phase = PHASE_END;
core.phase_action = false;
if(is_player_affected_by_effect(infos.turn_player, EFFECT_SKIP_EP)) {
arg.step = 18;
reset_phase(PHASE_END);
adjust_all();
return FALSE;
}
auto message = pduel->new_message(MSG_NEW_PHASE);
message->write<uint16_t>(infos.phase);
raise_event(nullptr, EVENT_PHASE_START + PHASE_END, nullptr, 0, 0, turn_player, 0);
process_instant_event();
adjust_all();
/*if(core.set_forced_attack)
emplace_process<Processors::ForcedBattle>();*/
return FALSE;
}
case 17: {
if(core.new_fchain.size() || core.new_ochain.size())
emplace_process<Processors::PointEvent>(false, false, false);
/*if(core.set_forced_attack)
emplace_process<Processors::ForcedBattle>();*/
return FALSE;
}
case 18: {
core.new_fchain.clear();
core.new_ochain.clear();
core.quick_f_chain.clear();
core.delayed_quick_tmp.clear();
emplace_process<Processors::PhaseEvent>(PHASE_END);
/*if(core.set_forced_attack)
emplace_process<Processors::ForcedBattle>();*/
return FALSE;
}
case 19: {
raise_event(nullptr, EVENT_TURN_END, nullptr, 0, 0, turn_player, 0);
process_instant_event();
adjust_all();
/*if(core.set_forced_attack)
emplace_process<Processors::ForcedBattle>();*/
return FALSE;
}
case 20: {
core.new_fchain.clear();
core.new_ochain.clear();
core.quick_f_chain.clear();
core.delayed_quick_tmp.clear();
arg.step = Processors::restart;
arg.turn_player = 1 - arg.turn_player;
return FALSE;
}
}
return TRUE;
}
bool field::process(Processors::AddChain& arg) {
switch (arg.step) {
case 0: {
if (!core.new_chains.size())
return TRUE;
auto& clit = core.new_chains.front();
effect* peffect = clit.triggering_effect;
arg.is_activated_effect = false;
// if it's an activate effect, go to subroutine checking for effects allowing the card
// to be activated even when it shouldn't normally (the turn it was set, from the hand, etc)
if((peffect->type & EFFECT_TYPE_ACTIVATE) != 0)
arg.step = 9;
return FALSE;
}
case 1: {
auto& clit = core.new_chains.front();
effect_set eset;
filter_player_effect(clit.triggering_player, EFFECT_ACTIVATE_COST, &eset);
for(const auto& peff : eset) {
pduel->lua->add_param<LuaParam::EFFECT>(peff);
pduel->lua->add_param<LuaParam::EFFECT>(clit.triggering_effect);
pduel->lua->add_param<LuaParam::INT>(clit.triggering_player);
if(!pduel->lua->check_condition(peff->target, 3))
continue;
if(peff->operation) {
core.sub_solving_event.push_back(clit.evt);
emplace_process<Processors::ExecuteOperation>(peff, clit.triggering_player);
}
}
if(!arg.is_activated_effect)
return FALSE;
effect* peffect = clit.triggering_effect;
card* phandler = peffect->get_handler();
phandler->set_status(STATUS_ACT_FROM_HAND, phandler->current.location == LOCATION_HAND);
if(phandler->current.location == LOCATION_SZONE) {
change_position(phandler, nullptr, phandler->current.controler, POS_FACEUP, 0);
} else {
uint32_t zone = 0xff;
if(!(phandler->data.type & (TYPE_FIELD | TYPE_PENDULUM)) && peffect->is_flag(EFFECT_FLAG_LIMIT_ZONE)) {
pduel->lua->add_param<LuaParam::INT>(clit.triggering_player);
pduel->lua->add_param<LuaParam::GROUP>(clit.evt.event_cards);
pduel->lua->add_param<LuaParam::INT>(clit.evt.event_player);
pduel->lua->add_param<LuaParam::INT>(clit.evt.event_value);
pduel->lua->add_param<LuaParam::EFFECT>(clit.evt.reason_effect);
pduel->lua->add_param<LuaParam::INT>(clit.evt.reason);
pduel->lua->add_param<LuaParam::INT>(clit.evt.reason_player);
zone = peffect->get_value(7);
if(!zone)
return TRUE;
}
int32_t loc = LOCATION_SZONE;
if(peffect->is_flag(EFFECT_FLAG2_FORCE_ACTIVATE_LOCATION)) {
loc = peffect->get_value();
if(!loc)
return TRUE;
} else if(phandler->current.location == LOCATION_HAND) {
if(phandler->data.type & TYPE_PENDULUM) {
loc = LOCATION_PZONE;
} else if(phandler->data.type & TYPE_FIELD) {
loc = LOCATION_FZONE;
}
}
phandler->enable_field_effect(false);
move_to_field(phandler, phandler->current.controler, phandler->current.controler, loc, (loc == LOCATION_MZONE) ? POS_FACEUP_ATTACK : POS_FACEUP, FALSE, 0, zone);
}
return FALSE;
}
case 2: {
auto& clit = core.new_chains.front();
effect* peffect = clit.triggering_effect;
card* phandler = peffect->get_handler();
if(peffect->type & EFFECT_TYPE_ACTIVATE) {
clit.set_triggering_state(phandler);
}
auto message = pduel->new_message(MSG_CHAINING);
message->write<uint32_t>(phandler->data.code);
message->write(phandler->get_info_location());
message->write<uint8_t>(clit.triggering_controler);
message->write<uint8_t>((uint8_t)clit.triggering_location);
message->write<uint32_t>(clit.triggering_sequence);
message->write<uint64_t>(peffect->description);
message->write<uint32_t>(core.current_chain.size() + 1);
for(auto& ch_lim : core.chain_limit)
ensure_luaL_stack(luaL_unref, pduel->lua->lua_state, LUA_REGISTRYINDEX, ch_lim.function);
core.chain_limit.clear();
peffect->card_type = phandler->get_type();
if((peffect->card_type & (TYPE_TRAP | TYPE_MONSTER)) == (TYPE_TRAP | TYPE_MONSTER))
peffect->card_type -= TYPE_TRAP;
if(is_flag(DUEL_TRIGGER_WHEN_PRIVATE_KNOWLEDGE)) {
if(!((peffect->type & EFFECT_TYPE_CONTINUOUS) == 0 && (peffect->type & EFFECT_TYPE_SINGLE) != 0))
peffect->set_active_type();
} else
peffect->set_active_type();
peffect->active_handler = peffect->handler->overlay_target;
clit.chain_count = static_cast<uint8_t>(core.current_chain.size()) + 1;
clit.target_cards = nullptr;
clit.target_player = PLAYER_NONE;
clit.target_param = 0;
clit.disable_reason = nullptr;
clit.disable_player = PLAYER_NONE;
clit.replace_op = 0;
if(phandler->current.location == LOCATION_HAND)
clit.flag |= CHAIN_HAND_EFFECT;
core.current_chain.push_back(clit);
core.current_chain.back().applied_chain_counters = check_chain_counter(peffect, clit.triggering_player, clit.chain_count);
// triggered events which are not caused by RaiseEvent create relation with the handler
if(!peffect->is_flag(EFFECT_FLAG_FIELD_ONLY) && (!(peffect->type & 0x2a0) || (peffect->code & 0xfffff000u) == EVENT_PHASE)) {
phandler->create_relation(clit);
}
peffect->effect_owner = clit.triggering_player;
// DISABLE_CHAIN should be check before cost
effect* deffect;
if(!peffect->is_flag(EFFECT_FLAG_FIELD_ONLY) && phandler->is_has_relation(clit) && (deffect = phandler->is_affected_by_effect(EFFECT_DISABLE_EFFECT)) != nullptr) {
effect* negeff = pduel->new_effect();
negeff->owner = deffect->owner;
negeff->type = EFFECT_TYPE_SINGLE;
negeff->code = EFFECT_DISABLE_CHAIN;
negeff->value = clit.chain_id;
negeff->reset_flag = RESET_CHAIN | RESET_EVENT | deffect->get_value();
phandler->add_effect(negeff);
}
core.new_chains.pop_front();
return FALSE;
}
case 3: {
auto& clit = core.current_chain.back();
int32_t playerid = clit.triggering_player;
effect* peffect = clit.triggering_effect;
if(!is_flag(DUEL_USE_TRAPS_IN_NEW_CHAIN) && get_cteffect(peffect, playerid, TRUE)) {
const bool damage_step = infos.phase == PHASE_DAMAGE && !peffect->is_flag(EFFECT_FLAG_DAMAGE_STEP);
const bool damage_cal = infos.phase == PHASE_DAMAGE_CAL && !peffect->is_flag(EFFECT_FLAG_DAMAGE_CAL);
if(damage_step || damage_cal) {
returns.set<int32_t>(0, TRUE);
return FALSE;
}
emplace_process<Processors::SelectEffectYesNo>(playerid, 94, peffect->get_handler());
} else
returns.set<int32_t>(0, FALSE);
return FALSE;
}
case 4: {
if(!returns.at<int32_t>(0)) {
core.select_chains.clear();
core.select_options.clear();
arg.step = 5;
return FALSE;
}
if(core.select_chains.size() > 1) {
auto& clit = core.current_chain.back();
emplace_process<Processors::SelectOption>(clit.triggering_player);
} else
returns.set<int32_t>(0, 0);
return FALSE;
}
case 5: {
auto& clit = core.current_chain.back();
auto ch = std::next(core.select_chains.begin(), returns.at<int32_t>(0));
int32_t playerid = clit.triggering_player;
effect* peffect = ch->triggering_effect;
card* phandler = peffect->get_handler();
auto message = pduel->new_message(MSG_HINT);
message->write<uint8_t>(HINT_OPSELECTED);
message->write<uint8_t>(playerid);
message->write<uint64_t>(returns.at<int32_t>(0) >= (int32_t)core.select_options.size() ? core.select_options[returns.at<int32_t>(0)] : 65);
clit.triggering_effect = peffect;
clit.evt = ch->evt;
phandler->create_relation(clit);
peffect->dec_count(playerid);
if(!(peffect->type & EFFECT_TYPE_ACTIVATE)) {
peffect->type |= EFFECT_TYPE_ACTIVATE;
clit.flag |= CHAIN_ACTIVATING;
}
core.select_chains.clear();
core.select_options.clear();
effect* deffect = pduel->new_effect();
deffect->owner = phandler;
deffect->code = 0;
deffect->type = EFFECT_TYPE_SINGLE;
deffect->flag[0] = EFFECT_FLAG_CANNOT_DISABLE | EFFECT_FLAG_CLIENT_HINT;
deffect->description = 65;
deffect->reset_flag = RESET_CHAIN;
phandler->add_effect(deffect);
return FALSE;
}
case 6: {
auto& clit = core.current_chain.back();
effect* peffect = clit.triggering_effect;
if(peffect->cost) {
core.sub_solving_event.push_back(clit.evt);
emplace_process<Processors::ExecuteCost>(peffect, clit.triggering_player);
}
return FALSE;
}
case 7: {
auto& clit = core.current_chain.back();
effect* peffect = clit.triggering_effect;
if(peffect->target) {
core.sub_solving_event.push_back(clit.evt);
emplace_process<Processors::ExecuteTarget>(peffect, clit.triggering_player);
}
return FALSE;
}
case 8: {
break_effect(false);
auto& clit = core.current_chain.back();
effect* peffect = clit.triggering_effect;
card* phandler = peffect->get_handler();
if(clit.target_cards && clit.target_cards->container.size()) {
if(peffect->is_flag(EFFECT_FLAG_CARD_TARGET)) {
for(auto& pcard : clit.target_cards->container)
raise_single_event(pcard, nullptr, EVENT_BECOME_TARGET, peffect, 0, clit.triggering_player, 0, clit.chain_count);
process_single_event();
if(clit.target_cards->container.size())
raise_event(clit.target_cards->container, EVENT_BECOME_TARGET, peffect, 0, clit.triggering_player, clit.triggering_player, clit.chain_count);
}
}
if(peffect->type & EFFECT_TYPE_ACTIVATE) {
core.leave_confirmed.insert(phandler);
if(!(phandler->data.type & (TYPE_CONTINUOUS | TYPE_FIELD | TYPE_EQUIP | TYPE_PENDULUM | TYPE_LINK))
&& !phandler->is_affected_by_effect(EFFECT_REMAIN_FIELD))
phandler->set_status(STATUS_LEAVE_CONFIRMED, TRUE);
}
if((phandler->get_type() & (TYPE_SPELL | TYPE_TRAP))
&& (phandler->get_type() & (TYPE_CONTINUOUS | TYPE_FIELD | TYPE_EQUIP | TYPE_PENDULUM | TYPE_LINK))
&& phandler->is_has_relation(clit) && phandler->current.location == LOCATION_SZONE
&& !peffect->is_flag(EFFECT_FLAG_FIELD_ONLY))
clit.flag |= CHAIN_CONTINUOUS_CARD;
core.phase_action = true;
if(clit.opinfos.count(0x200) && clit.opinfos[0x200].op_count) {
if(is_flag(DUEL_CANNOT_SUMMON_OATH_OLD)) {
core.spsummon_rst = true;
set_spsummon_counter(clit.triggering_player, true, true);
if(clit.opinfos[0x200].op_player == PLAYER_ALL)
set_spsummon_counter(1 - clit.triggering_player, true, true);
}
if(is_flag(DUEL_SPSUMMON_ONCE_OLD_NEGATE) && (core.global_flag & GLOBALFLAG_SPSUMMON_ONCE)) {
auto& optarget = clit.opinfos[0x200];
if(optarget.op_cards) {
if(optarget.op_player == PLAYER_ALL) {
auto opit = optarget.op_cards->container.begin();
uint32_t sumplayer = optarget.op_param;
if((*opit)->spsummon_code) {
++core.spsummon_once_map[sumplayer][(*opit)->spsummon_code];
++core.spsummon_once_map_rst[sumplayer][(*opit)->spsummon_code];
}
++opit;
if((*opit)->spsummon_code) {
++core.spsummon_once_map[1 - sumplayer][(*opit)->spsummon_code];
++core.spsummon_once_map_rst[1 - sumplayer][(*opit)->spsummon_code];
}
} else {
uint32_t sumplayer = clit.triggering_player;
// genarally setting op_player is unnecessary when the effect targets cards
// in the case of CATEGORY_SPECIAL_SUMMON(with EFFECT_FLAG_CARD_TARGET), op_player=0x10
// indecates that it is the opponent that special summons the target monsters
if(peffect->is_flag(EFFECT_FLAG_CARD_TARGET) && optarget.op_player == 0x10)
sumplayer = 1 - sumplayer;
for(auto& pcard : optarget.op_cards->container) {
if(pcard->spsummon_code) {
++core.spsummon_once_map[sumplayer][pcard->spsummon_code];
++core.spsummon_once_map_rst[sumplayer][pcard->spsummon_code];
}
}
}
}
}
}
auto message = pduel->new_message(MSG_CHAINED);
message->write<uint8_t>(clit.chain_count);
raise_event(phandler, EVENT_CHAINING, peffect, 0, clit.triggering_player, clit.triggering_player, clit.chain_count);
process_instant_event();
core.just_sent_cards.clear();
++core.real_chain_count;
if(core.new_chains.size())
emplace_process<Processors::AddChain>();
adjust_all();
return TRUE;
}
case 10: {
arg.is_activated_effect = true;
auto& clit = core.new_chains.front();
effect* peffect = clit.triggering_effect;
card* phandler = peffect->get_handler();
int32_t ecode = 0;
if(phandler->current.location == LOCATION_HAND) {
if(phandler->data.type & TYPE_TRAP)
ecode = EFFECT_TRAP_ACT_IN_HAND;
else if((phandler->data.type & TYPE_SPELL) && (phandler->data.type & TYPE_QUICKPLAY || phandler->is_affected_by_effect(EFFECT_BECOME_QUICK))
&& infos.turn_player != phandler->current.controler)
ecode = EFFECT_QP_ACT_IN_NTPHAND;
} else if(phandler->current.location == LOCATION_SZONE) {
if((phandler->data.type & TYPE_TRAP) && phandler->get_status(STATUS_SET_TURN))
ecode = EFFECT_TRAP_ACT_IN_SET_TURN;
if((phandler->data.type & TYPE_SPELL) && (phandler->data.type & TYPE_QUICKPLAY || phandler->is_affected_by_effect(EFFECT_BECOME_QUICK)) && phandler->get_status(STATUS_SET_TURN))
ecode = EFFECT_QP_ACT_IN_SET_TURN;
}
if(ecode) {
core.select_effects.clear();
core.select_options.clear();
effect_set eset;
phandler->filter_effect(ecode, &eset);
bool has_no_side_effect_effs = false;
if(!eset.empty()) {
for(const auto& peff : eset) {
if(peff->has_function_value() || peff->has_count_limit()) {
if(peff->check_count_limit(phandler->current.controler)) {
core.select_effects.push_back(peff);
core.select_options.push_back(peff->description);
}
} else {
has_no_side_effect_effs = has_no_side_effect_effs || true;
}
}
if(core.select_options.empty()) {
arg.step = 0;
return FALSE;
}
if(has_no_side_effect_effs) {
core.select_options.insert(core.select_options.begin(), 99);
core.select_effects.insert(core.select_effects.begin(), nullptr);
}
if(core.select_options.size() == 1)
returns.set<int32_t>(0, 0);
else
emplace_process<Processors::SelectOption>(phandler->current.controler);
}
} else {
arg.step = 0;
}
return FALSE;
}
case 11: {
auto& clit = core.new_chains.front();
effect* peffect = clit.triggering_effect;
card* phandler = peffect->get_handler();
if(!core.select_effects.empty()) {
auto* eff = core.select_effects[returns.at<int32_t>(0)];
if(eff) {
eff->dec_count(phandler->current.controler);
pduel->lua->add_param<LuaParam::EFFECT>(peffect);
eff->get_value(phandler, 1);
}
}
arg.step = 0;
return FALSE;
}
}
return TRUE;
}
bool field::process(Processors::SortChain& arg) {
auto is_turn_player = arg.playerid == infos.turn_player;
auto& chains = is_turn_player ? core.tpchain : core.ntpchain;
switch(arg.step) {
case 0: {
core.select_cards.clear();
for(const auto& ch : chains)
core.select_cards.push_back(ch.triggering_effect->get_handler());
emplace_process<Processors::SortCard>(arg.playerid, true);
return FALSE;
}
case 1: {
if(returns.at<int8_t>(0) == -1)
return TRUE;
std::map<const chain*, int8_t> sort_map;
int i = 0;
for(const auto& ch : chains)
sort_map[&ch] = returns.at<int8_t>(i++);
chains.sort([&sort_map](const chain& c1, const chain& c2) { return sort_map[&c1] < sort_map[&c2]; });
return TRUE;
}
}
return TRUE;
}
void field::solve_continuous(uint8_t playerid, effect* peffect, const tevent& e) {
auto& newchain = core.sub_solving_continuous.emplace_back();
newchain.chain_id = 0;
newchain.chain_count = 0;
newchain.triggering_effect = peffect;
newchain.triggering_player = playerid;
newchain.evt = e;
newchain.target_cards = nullptr;
newchain.target_player = PLAYER_NONE;
newchain.target_param = 0;
newchain.disable_player = PLAYER_NONE;
newchain.disable_reason = nullptr;
newchain.flag = 0;
emplace_process<Processors::SolveContinuous>();
}
bool field::process(Processors::SolveContinuous& arg) {
switch(arg.step) {
case 0: {
core.solving_continuous.splice(core.solving_continuous.begin(), core.sub_solving_continuous);
auto& clit = core.solving_continuous.front();
effect* peffect = clit.triggering_effect;
uint8_t triggering_player = clit.triggering_player;
if(!peffect->check_count_limit(triggering_player)) {
core.solving_continuous.pop_front();
return TRUE;
}
core.continuous_chain.push_back(clit);
if(peffect->is_flag(EFFECT_FLAG_DELAY) || (!(peffect->code & 0xfffff000u) && (peffect->code & (EVENT_PHASE | EVENT_PHASE_START))))
core.conti_solving = true;
arg.reason_effect = core.reason_effect;
arg.reason_player = core.reason_player;
if(!peffect->target)
return FALSE;
core.sub_solving_event.push_back(clit.evt);
emplace_process<Processors::ExecuteTarget>(peffect, triggering_player);
return FALSE;
}
case 1: {
return FALSE;
}
case 2: {
auto& clit = core.solving_continuous.front();
effect* peffect = clit.triggering_effect;
uint8_t triggering_player = clit.triggering_player;
if(!peffect->operation)
return FALSE;
peffect->dec_count(triggering_player);
core.sub_solving_event.push_back(clit.evt);
emplace_process<Processors::ExecuteOperation>(peffect, triggering_player);
return FALSE;
}
case 3: {
auto& clit = core.solving_continuous.front();
effect* peffect = clit.triggering_effect;
core.reason_effect = arg.reason_effect;
core.reason_player = arg.reason_player;
core.continuous_chain.pop_back();
core.solving_continuous.pop_front();
if(peffect->is_flag(EFFECT_FLAG_DELAY) || (!(peffect->code & 0xfffff000u) && (peffect->code & (EVENT_PHASE | EVENT_PHASE_START)))) {
core.conti_solving = false;
adjust_all();
return FALSE;
}
return TRUE;
}
case 4: {
if(core.conti_player == PLAYER_NONE)
core.conti_player = infos.turn_player;
if(core.conti_player == infos.turn_player) {
if(core.delayed_continuous_tp.size()) {
core.sub_solving_continuous.splice(core.sub_solving_continuous.end(), core.delayed_continuous_tp, core.delayed_continuous_tp.begin());
emplace_process<Processors::SolveContinuous>();
} else
core.conti_player = 1 - infos.turn_player;
}
if(core.conti_player == 1 - infos.turn_player) {
if(core.delayed_continuous_ntp.size()) {
core.sub_solving_continuous.splice(core.sub_solving_continuous.end(), core.delayed_continuous_ntp, core.delayed_continuous_ntp.begin());
emplace_process<Processors::SolveContinuous>();
} else if(core.delayed_continuous_tp.size()) {
core.conti_player = infos.turn_player;
core.sub_solving_continuous.splice(core.sub_solving_continuous.end(), core.delayed_continuous_tp, core.delayed_continuous_tp.begin());
emplace_process<Processors::SolveContinuous>();
} else
core.conti_player = PLAYER_NONE;
}
return TRUE;
}
}
return TRUE;
}
bool field::process(Processors::SolveChain& arg) {
auto step = arg.step;
if(core.current_chain.size() == 0 && step == 0)
return TRUE;
auto cait = core.current_chain.rbegin();
switch(step) {
case 0: {
if(core.spsummon_rst) {
if(is_flag(DUEL_CANNOT_SUMMON_OATH_OLD)) {
set_spsummon_counter(0, false, true);
set_spsummon_counter(1, false, true);
core.spsummon_rst = false;
}
if(is_flag(DUEL_SPSUMMON_ONCE_OLD_NEGATE)) {
for(int plr = 0; plr < 2; ++plr) {
for(auto& iter : core.spsummon_once_map[plr]) {
auto spcode = iter.first;
core.spsummon_once_map[plr][spcode] -= core.spsummon_once_map_rst[plr][spcode];
core.spsummon_once_map_rst[plr][spcode] = 0;
}
}
}
}
auto message = pduel->new_message(MSG_CHAIN_SOLVING);
message->write<uint8_t>(cait->chain_count);
add_to_disable_check_list(cait->triggering_effect->get_handler());
adjust_instant();
raise_event(nullptr, EVENT_CHAIN_ACTIVATING, cait->triggering_effect, 0, cait->triggering_player, cait->triggering_player, cait->chain_count);
process_instant_event();
return FALSE;
}
case 1: {
effect* peffect = cait->triggering_effect;
if(cait->flag & CHAIN_DISABLE_ACTIVATE && is_chain_negatable(cait->chain_count)) {
remove_oath_effect(peffect);
if(peffect->is_flag(EFFECT_FLAG_COUNT_LIMIT) && (peffect->count_flag & EFFECT_COUNT_CODE_OATH)) {
dec_effect_code(peffect->count_code, peffect->count_flag, peffect->count_hopt_index, cait->triggering_player);
}
if(cait->applied_chain_counters != nullptr) {
restore_chain_counter(cait->triggering_player, *cait->applied_chain_counters);
delete cait->applied_chain_counters;
cait->applied_chain_counters = nullptr;
}
core.new_fchain.remove_if([chaincount = cait->chain_count](const chain& ch) { return ch.evt.event_code == EVENT_CHAINING && ch.evt.event_value == chaincount; });
core.new_ochain.remove_if([chaincount = cait->chain_count](const chain& ch) { return ch.evt.event_code == EVENT_CHAINING && ch.evt.event_value == chaincount; });
raise_event(nullptr, EVENT_CHAIN_NEGATED, peffect, 0, cait->triggering_player, cait->triggering_player, cait->chain_count);
process_instant_event();
arg.step = 9;
return FALSE;
}
if(cait->applied_chain_counters != nullptr) {
delete cait->applied_chain_counters;
cait->applied_chain_counters = nullptr;
}
release_oath_relation(peffect);
break_effect();
core.chain_solving = true;
raise_event(nullptr, EVENT_CHAIN_SOLVING, peffect, 0, cait->triggering_player, cait->triggering_player, cait->chain_count);
process_instant_event();
return FALSE;
}
case 2: {
core.spsummon_state_count_tmp[0] = core.spsummon_state_count[0];
core.spsummon_state_count_tmp[1] = core.spsummon_state_count[1];
effect* peffect = cait->triggering_effect;
card* pcard = peffect->get_handler();
if((cait->flag & CHAIN_CONTINUOUS_CARD) && !pcard->is_has_relation(*cait)) {
arg.step = 3;
return FALSE;
}
if((peffect->type & EFFECT_TYPE_ACTIVATE) && pcard->is_has_relation(*cait) && !cait->replace_op) {
pcard->enable_field_effect(true);
if(is_flag(DUEL_1_FACEUP_FIELD)) {
if(pcard->data.type & TYPE_FIELD) {
card* fscard = player[1 - pcard->current.controler].list_szone[5];
if(fscard && fscard->is_position(POS_FACEUP))
fscard->enable_field_effect(false);
}
}
adjust_instant();
}
// creating continuous target: peffect->is_flag(EFFECT_FLAG_CONTINUOUS_TARGET) && !cait->replace_op
// operation function creating continuous target should be executed even when disabled
if(is_chain_disablable(cait->chain_count) && (!peffect->is_flag(EFFECT_FLAG_CONTINUOUS_TARGET) || cait->replace_op)) {
if(is_chain_disabled(cait->chain_count) || (pcard->get_status(STATUS_DISABLED | STATUS_FORBIDDEN) && pcard->is_has_relation(*cait))) {
if(!(cait->flag & CHAIN_DISABLE_EFFECT)) {
auto message = pduel->new_message(MSG_CHAIN_DISABLED);
message->write<uint8_t>(cait->chain_count);
}
raise_event(nullptr, EVENT_CHAIN_DISABLED, peffect, 0, cait->triggering_player, cait->triggering_player, cait->chain_count);
process_instant_event();
arg.step = 3;
return FALSE;
}
}
return FALSE;
}
case 3 : {
effect* peffect = cait->triggering_effect;
card* pcard = peffect->get_handler();
if((cait->flag & CHAIN_CONTINUOUS_CARD) && !pcard->is_has_relation(*cait)){
return FALSE;
}
if(cait->replace_op) {
arg.backed_up_operation = cait->triggering_effect->operation;
cait->triggering_effect->operation = cait->replace_op;
} else
arg.backed_up_operation = 0;
if(cait->triggering_effect->operation) {
core.sub_solving_event.push_back(cait->evt);
emplace_process<Processors::ExecuteOperation>(cait->triggering_effect, cait->triggering_player);
}
return FALSE;
}
case 4: {
effect* peffect = cait->triggering_effect;
if(arg.backed_up_operation) {
if(peffect->operation != 0)
ensure_luaL_stack(luaL_unref, pduel->lua->lua_state, LUA_REGISTRYINDEX, peffect->operation);
peffect->operation = arg.backed_up_operation;
}
core.special_summoning.clear();
core.equiping_cards.clear();
return FALSE;
}
case 5: {
if(std::exchange(arg.backed_up_operation, 0) == 0) {
if(cait->opinfos.count(0x200) && cait->opinfos[0x200].op_count) {
if(is_flag(DUEL_CANNOT_SUMMON_OATH_OLD)) {
if(core.spsummon_state_count_tmp[cait->triggering_player] == core.spsummon_state_count[cait->triggering_player])
set_spsummon_counter(cait->triggering_player);
if(cait->opinfos[0x200].op_player == PLAYER_ALL && core.spsummon_state_count_tmp[1 - cait->triggering_player] == core.spsummon_state_count[1 - cait->triggering_player])
set_spsummon_counter(1 - cait->triggering_player);
}
if(is_flag(DUEL_CANNOT_SUMMON_OATH_OLD)) {
//sometimes it may add twice, only works for once per turn
auto& optarget = cait->opinfos[0x200];
if(optarget.op_cards) {
if(optarget.op_player == PLAYER_ALL) {
uint32_t sumplayer = optarget.op_param;
if(is_flag(DUEL_SPSUMMON_ONCE_OLD_NEGATE) && (core.global_flag & GLOBALFLAG_SPSUMMON_ONCE)) {
auto opit = optarget.op_cards->container.begin();
if((*opit)->spsummon_code)
++core.spsummon_once_map[sumplayer][(*opit)->spsummon_code];
++opit;
if((*opit)->spsummon_code)
++core.spsummon_once_map[1 - sumplayer][(*opit)->spsummon_code];
}
auto opit = optarget.op_cards->container.begin();
check_card_counter(*opit, ACTIVITY_SPSUMMON, sumplayer);
++opit;
check_card_counter(*opit, ACTIVITY_SPSUMMON, 1 - sumplayer);
} else {
uint32_t sumplayer = cait->triggering_player;
// genarally setting op_player is unnecessary when the effect targets cards
// in the case of CATEGORY_SPECIAL_SUMMON(with EFFECT_FLAG_CARD_TARGET), op_player=0x10
// indecates that it is the opponent that special summons the target monsters
if(cait->triggering_effect->is_flag(EFFECT_FLAG_CARD_TARGET) && optarget.op_player == 0x10)
sumplayer = 1 - sumplayer;
for(auto& ptarget : optarget.op_cards->container) {
if(is_flag(DUEL_SPSUMMON_ONCE_OLD_NEGATE) && (core.global_flag & GLOBALFLAG_SPSUMMON_ONCE) && ptarget->spsummon_code)
++core.spsummon_once_map[sumplayer][ptarget->spsummon_code];
check_card_counter(ptarget, ACTIVITY_SPSUMMON, sumplayer);
}
}
}
}
}
}
core.spsummon_state_count_tmp[0] = 0;
core.spsummon_state_count_tmp[1] = 0;
core.chain_solving = false;
if(core.delayed_continuous_tp.size()) {
core.conti_player = infos.turn_player;
core.sub_solving_continuous.splice(core.sub_solving_continuous.end(), core.delayed_continuous_tp, core.delayed_continuous_tp.begin());
emplace_process<Processors::SolveContinuous>();
} else if(core.delayed_continuous_ntp.size()) {
core.conti_player = 1 - infos.turn_player;
core.sub_solving_continuous.splice(core.sub_solving_continuous.end(), core.delayed_continuous_ntp, core.delayed_continuous_ntp.begin());
emplace_process<Processors::SolveContinuous>();
} else
core.conti_player = PLAYER_NONE;
raise_event(nullptr, EVENT_CHAIN_SOLVED, cait->triggering_effect, 0, cait->triggering_player, cait->triggering_player, cait->chain_count);
adjust_disable_check_list();
process_instant_event();
arg.step = 9;
return FALSE;
}
case 10: {
auto message = pduel->new_message(MSG_CHAIN_SOLVED);
message->write<uint8_t>(cait->chain_count);
effect* peffect = cait->triggering_effect;
card* pcard = peffect->get_handler();
if((peffect->type & EFFECT_TYPE_ACTIVATE) && (cait->flag & CHAIN_ACTIVATING))
peffect->type &= ~EFFECT_TYPE_ACTIVATE;
if((cait->flag & CHAIN_HAND_EFFECT) && !pcard->is_position(POS_FACEUP) && (pcard->current.location == LOCATION_HAND))
shuffle(pcard->current.controler, LOCATION_HAND);
if(cait->target_cards && cait->target_cards->container.size()) {
for(auto& ptarget : cait->target_cards->container)
ptarget->release_relation(*cait);
}
if((pcard->data.type & TYPE_EQUIP) && (peffect->type & EFFECT_TYPE_ACTIVATE)
&& !pcard->equiping_target && pcard->is_has_relation(*cait))
pcard->set_status(STATUS_LEAVE_CONFIRMED, TRUE);
if(is_flag(DUEL_1_FACEUP_FIELD)) {
if((pcard->data.type & TYPE_FIELD) && (peffect->type & EFFECT_TYPE_ACTIVATE)
&& !pcard->is_status(STATUS_LEAVE_CONFIRMED) && pcard->is_has_relation(*cait)) {
card* fscard = player[1 - pcard->current.controler].list_szone[5];
if(fscard && fscard->is_position(POS_FACEUP))
destroy(fscard, nullptr, REASON_RULE, 1 - pcard->current.controler);
}
}
peffect->active_type = 0;
peffect->active_handler = nullptr;
pcard->release_relation(*cait);
for(auto& cit : core.delayed_enable_set) {
if(cit->current.location == LOCATION_MZONE)
cit->enable_field_effect(true);
}
core.delayed_enable_set.clear();
adjust_all();
core.current_chain.pop_back();
if(--core.real_chain_count < 0)
core.real_chain_count = 0;
if(!core.current_chain.size()) {
for(auto& ch_lim : core.chain_limit)
ensure_luaL_stack(luaL_unref, pduel->lua->lua_state, LUA_REGISTRYINDEX, ch_lim.function);
core.chain_limit.clear();
return FALSE;
}
if((core.summoning_card || core.summoning_proc_group_type) && core.reserved)
core.subunits.push_back(*std::exchange(core.reserved, std::nullopt));
core.summoning_card = nullptr;
core.summoning_proc_group_type = 0;
arg.step = Processors::restart;
return FALSE;
}
case 11: {
for(auto cit = core.leave_confirmed.begin(); cit != core.leave_confirmed.end();) {
auto rm = cit++;
if(!(*rm)->is_status(STATUS_LEAVE_CONFIRMED))
core.leave_confirmed.erase(rm);
}
if(core.leave_confirmed.size())
send_to(core.leave_confirmed, nullptr, REASON_RULE, PLAYER_NONE, PLAYER_NONE, LOCATION_GRAVE, 0, POS_FACEUP);
return FALSE;
}
case 12: {
core.used_event.splice(core.used_event.end(), core.point_event);
pduel->new_message(MSG_CHAIN_END);
reset_chain();
if((core.summoning_card || core.summoning_proc_group_type || core.effect_damage_step == 1) && core.reserved)
core.subunits.push_back(*std::exchange(core.reserved, std::nullopt));
core.summoning_proc_group_type = 0;
core.summoning_card = nullptr;
return FALSE;
}
case 13: {
core.just_sent_cards.clear();
raise_event(nullptr, EVENT_CHAIN_END, nullptr, 0, 0, 0, 0);
process_instant_event();
adjust_all();
if(!arg.skip_trigger || !arg.skip_new) {
core.hint_timing[0] |= TIMING_CHAIN_END;
core.hint_timing[1] |= TIMING_CHAIN_END;
emplace_process<Processors::PointEvent>(arg.skip_trigger, arg.skip_freechain || arg.skip_new, arg.skip_new);
}
returns.set<int32_t>(0, TRUE);
return TRUE;
}
}
return TRUE;
}
int32_t field::break_effect(bool clear_sent) {
if(clear_sent)
core.just_sent_cards.clear();
core.hint_timing[0] &= TIMING_DAMAGE_STEP | TIMING_DAMAGE_CAL;
core.hint_timing[1] &= TIMING_DAMAGE_STEP | TIMING_DAMAGE_CAL;
for (auto chit = core.new_ochain.begin(); chit != core.new_ochain.end();) {
auto rm = chit++;
effect* peffect = rm->triggering_effect;
if (!peffect->is_flag(EFFECT_FLAG_DELAY)) {
if (peffect->is_flag(EFFECT_FLAG_FIELD_ONLY)
|| !(peffect->type & EFFECT_TYPE_FIELD) || peffect->in_range(*rm)) {
auto message = pduel->new_message(MSG_MISSED_EFFECT);
message->write(peffect->get_handler()->get_info_location());
message->write<uint32_t>(peffect->get_handler()->data.code);
}
core.new_ochain.erase(rm);
}
}
core.used_event.splice(core.used_event.end(), core.instant_event);
adjust_instant();
if(!is_flag(DUEL_RELAY) && !core.force_turn_end) {
uint32_t winp = 5, rea = 1;
if(player[0].lp <= 0 && player[1].lp > 0 && !is_player_affected_by_effect(0, EFFECT_CANNOT_LOSE_LP)) {
winp = 1;
rea = 1;
}
if(core.overdraw[0] && !core.overdraw[1] && !is_player_affected_by_effect(0, EFFECT_CANNOT_LOSE_DECK)) {
winp = 1;
rea = 2;
}
if(player[1].lp <= 0 && player[0].lp > 0 && !is_player_affected_by_effect(1, EFFECT_CANNOT_LOSE_LP)) {
winp = 0;
rea = 1;
}
if(core.overdraw[1] && !core.overdraw[0] && !is_player_affected_by_effect(1, EFFECT_CANNOT_LOSE_DECK)) {
winp = 0;
rea = 2;
}
if(player[1].lp <= 0 && player[0].lp <= 0 && !(is_player_affected_by_effect(0, EFFECT_CANNOT_LOSE_LP) && is_player_affected_by_effect(1, EFFECT_CANNOT_LOSE_LP))) {
if(is_player_affected_by_effect(0, EFFECT_CANNOT_LOSE_LP))
winp = 0;
else if(is_player_affected_by_effect(1, EFFECT_CANNOT_LOSE_LP))
winp = 1;
else
winp = PLAYER_NONE;
rea = 1;
}
if(core.overdraw[1] && core.overdraw[0] && !(is_player_affected_by_effect(0, EFFECT_CANNOT_LOSE_DECK) && is_player_affected_by_effect(1, EFFECT_CANNOT_LOSE_DECK))) {
if(is_player_affected_by_effect(0, EFFECT_CANNOT_LOSE_DECK))
winp = 0;
else if(is_player_affected_by_effect(1, EFFECT_CANNOT_LOSE_DECK))
winp = 1;
else
winp = PLAYER_NONE;
rea = 2;
}
if(winp != 5) {
auto message = pduel->new_message(MSG_WIN);
message->write<uint8_t>(winp);
message->write<uint8_t>(rea);
core.overdraw[0] = core.overdraw[1] = false;
core.win_player = 5;
core.win_reason = 0;
} else if(core.win_player != 5) {
auto message = pduel->new_message(MSG_WIN);
message->write<uint8_t>(core.win_player);
message->write<uint8_t>(core.win_reason);
core.win_player = 5;
core.win_reason = 0;
core.overdraw[0] = core.overdraw[1] = false;
}
}
return 0;
}
void field::adjust_instant() {
++infos.event_id;
adjust_disable_check_list();
adjust_self_destroy_set();
}
void field::adjust_all() {
++infos.event_id;
core.readjust_map.clear();
emplace_process<Processors::Adjust>();
}
void field::refresh_location_info_instant() {
effect_set eset;
uint32_t dis1 = player[0].disabled_location | (player[1].disabled_location << 16);
player[0].disabled_location = 0;
player[1].disabled_location = 0;
filter_field_effect(EFFECT_DISABLE_FIELD, &eset);
for(const auto& peff : eset) {
uint32_t value = peff->get_value();
player[0].disabled_location |= value & 0xff7f;
player[1].disabled_location |= (value >> 16) & 0xff7f;
}
eset.clear();
filter_field_effect(EFFECT_USE_EXTRA_MZONE, &eset);
for(const auto& peff : eset) {
uint32_t p = peff->get_handler_player();
uint32_t value = peff->get_value();
player[p].disabled_location |= (value >> 16) & 0x1f;
}
eset.clear();
filter_field_effect(EFFECT_USE_EXTRA_SZONE, &eset);
for(const auto& peff : eset) {
uint32_t p = peff->get_handler_player();
uint32_t value = peff->get_value();
player[p].disabled_location |= (value >> 8) & 0x1f00;
}
player[0].disabled_location |= (((player[1].disabled_location >> 5) & 1) << 6) | (((player[1].disabled_location >> 6) & 1) << 5);
player[1].disabled_location |= (((player[0].disabled_location >> 5) & 1) << 6) | (((player[0].disabled_location >> 6) & 1) << 5);
uint32_t dis2 = player[0].disabled_location | (player[1].disabled_location << 16);
if(dis1 != dis2) {
auto message = pduel->new_message(MSG_FIELD_DISABLED);
message->write<uint32_t>(dis2);
}
}
bool field::process(Processors::RefreshLoc& arg) {
switch(arg.step) {
case 0: {
effect_set eset;
if(is_flag(DUEL_3_COLUMNS_FIELD)) {
player[0].used_location |= 0x1111;
player[1].used_location |= 0x1111;
}
arg.previously_disabled_locations = (player[0].disabled_location & 0xffff) | (player[1].disabled_location << 16);
player[0].disabled_location = 0;
player[1].disabled_location = 0;
core.disfield_effects.clear();
core.extra_mzone_effects.clear();
core.extra_szone_effects.clear();
filter_field_effect(EFFECT_DISABLE_FIELD, &eset);
for(const auto& peff : eset) {
uint32_t value = peff->get_value();
if(value && !peff->is_flag(EFFECT_FLAG_REPEAT)) {
player[0].disabled_location |= value & 0xff7f;
player[1].disabled_location |= (value >> 16) & 0xff7f;
} else
core.disfield_effects.push_back(peff);
}
eset.clear();
filter_field_effect(EFFECT_USE_EXTRA_MZONE, &eset);
for(const auto& peff : eset) {
uint32_t p = peff->get_handler_player();
uint32_t value = peff->get_value();
player[p].disabled_location |= (value >> 16) & 0x1f;
if((uint32_t)field_used_count[(value >> 16) & 0x1f] < (value & 0xffff))
core.extra_mzone_effects.push_back(peff);
}
eset.clear();
filter_field_effect(EFFECT_USE_EXTRA_SZONE, &eset);
for(const auto& peff : eset) {
uint32_t p = peff->get_handler_player();
uint32_t value = peff->get_value();
player[p].disabled_location |= (value >> 8) & 0x1f00;
if((uint32_t)field_used_count[(value >> 16) & 0x1f] < (value & 0xffff))
core.extra_szone_effects.push_back(peff);
}
return FALSE;
}
case 1: {
if(core.disfield_effects.empty()) {
arg.step = 2;
return FALSE;
}
effect* peffect = core.disfield_effects[0];
arg.current_disable_field_effect = peffect;
core.disfield_effects.erase(core.disfield_effects.begin());
if(!peffect->operation) {
peffect->value = 0x80;
arg.step = 0;
return FALSE;
}
core.sub_solving_event.push_back(nil_event);
emplace_process<Processors::ExecuteOperation>(peffect, peffect->get_handler_player());
return FALSE;
}
case 2: {
auto disabled_locations = returns.at<uint32_t>(0);
disabled_locations &= 0xff7fff7f;
if(disabled_locations == 0)
disabled_locations = 0x80;
if(arg.current_disable_field_effect->get_handler_player() == 0) {
arg.current_disable_field_effect->value = disabled_locations;
player[0].disabled_location |= disabled_locations & 0xff7f;
player[1].disabled_location |= (disabled_locations >> 16) & 0xff7f;
} else {
arg.current_disable_field_effect->value = ((disabled_locations << 16) | (disabled_locations >> 16));
player[1].disabled_location |= disabled_locations & 0xff7f;
player[0].disabled_location |= (disabled_locations >> 16) & 0xff7f;
}
returns.set<uint32_t>(0, disabled_locations);
arg.step = 0;
return FALSE;
}
case 3: {
if(core.extra_mzone_effects.size() == 0) {
arg.step = 4;
return FALSE;
}
effect* peffect = core.extra_mzone_effects[0];
arg.current_disable_field_effect = peffect;
core.extra_mzone_effects.erase(core.extra_mzone_effects.begin());
uint32_t p = peffect->get_handler_player();
uint32_t mzone_flag = (player[p].disabled_location | player[p].used_location) & 0x1f;
if(mzone_flag == 0x1f) {
arg.step = 4;
return FALSE;
}
int32_t val = peffect->get_value();
int32_t dis_count = (val & 0xffff) - field_used_count[(val >> 16) & 0x1f];
int32_t empty_count = 5 - field_used_count[mzone_flag];
uint32_t flag = mzone_flag | 0xffffffe0;
if(dis_count > empty_count)
dis_count = empty_count;
arg.dis_count = dis_count;
emplace_process<Processors::SelectDisField>(p, flag, dis_count);
return FALSE;
}
case 4: {
uint32_t dis_count = arg.dis_count;
uint32_t mzone_flag = 0;
uint8_t pt = 0;
for(uint32_t i = 0; i < dis_count; ++i) {
uint8_t s = returns.at<int8_t>(pt + 2);
mzone_flag |= 0x1u << s;
pt += 3;
}
effect* peffect = arg.current_disable_field_effect;
player[peffect->get_handler_player()].disabled_location |= mzone_flag;
peffect->value = (int32_t)(peffect->value | (mzone_flag << 16));
arg.step = 2;
return FALSE;
}
case 5: {
if(core.extra_szone_effects.size() == 0) {
arg.step = 6;
return FALSE;
}
effect* peffect = core.extra_szone_effects[0];
arg.current_disable_field_effect = peffect;
core.extra_szone_effects.erase(core.extra_szone_effects.begin());
uint32_t p = peffect->get_handler_player();
uint32_t szone_flag = ((player[p].disabled_location | player[p].used_location) >> 8) & 0x1f;
if(szone_flag == 0x1f) {
arg.step = 6;
return FALSE;
}
int32_t val = peffect->get_value();
uint32_t dis_count = (val & 0xffff) - field_used_count[(val >> 16) & 0x1f];
uint32_t empty_count = 5 - field_used_count[szone_flag];
uint32_t flag = (szone_flag << 8) | 0xffffe0ff;
if(dis_count > empty_count)
dis_count = empty_count;
arg.dis_count = dis_count;
emplace_process<Processors::SelectDisField>(p, flag, dis_count);
return FALSE;
}
case 6: {
uint32_t dis_count = arg.dis_count;
uint32_t szone_flag = 0;
uint8_t pt = 0;
for(uint32_t i = 0; i < dis_count; ++i) {
uint8_t s = returns.at<int8_t>(pt + 2);
szone_flag |= 0x1u << s;
pt += 3;
}
effect* peffect = arg.current_disable_field_effect;
player[peffect->get_handler_player()].disabled_location |= szone_flag << 8;
peffect->value = (int32_t)(peffect->value | (szone_flag << 16));
arg.step = 4;
return FALSE;
}
case 7: {
player[0].disabled_location |= (((player[1].disabled_location >> 5) & 1) << 6) | (((player[1].disabled_location >> 6) & 1) << 5);
player[1].disabled_location |= (((player[0].disabled_location >> 5) & 1) << 6) | (((player[0].disabled_location >> 6) & 1) << 5);
uint32_t dis = player[0].disabled_location | (player[1].disabled_location << 16);
if(dis != arg.previously_disabled_locations) {
auto message = pduel->new_message(MSG_FIELD_DISABLED);
message->write<uint32_t>(dis);
}
return TRUE;
}
}
return TRUE;
}
bool field::process(Processors::Adjust& arg) {
switch(arg.step) {
case 0: {
core.re_adjust = false;
return FALSE;
}
case 1: {
//win check(deck=0 or lp=0)
if(!core.force_turn_end){
uint32_t winp = 5, rea = 1;
if (player[0].lp <= 0 && player[1].lp > 0 && !is_player_affected_by_effect(0, EFFECT_CANNOT_LOSE_LP)) {
winp = 1;
rea = 1;
}
if (core.overdraw[0] && !core.overdraw[1] && !is_player_affected_by_effect(0, EFFECT_CANNOT_LOSE_DECK)) {
winp = 1;
rea = 2;
}
if (player[1].lp <= 0 && player[0].lp > 0 && !is_player_affected_by_effect(1, EFFECT_CANNOT_LOSE_LP)) {
winp = 0;
rea = 1;
}
if (core.overdraw[1] && !core.overdraw[0] && !is_player_affected_by_effect(1, EFFECT_CANNOT_LOSE_DECK)) {
winp = 0;
rea = 2;
}
if (player[1].lp <= 0 && player[0].lp <= 0 && !(is_player_affected_by_effect(0, EFFECT_CANNOT_LOSE_LP) && is_player_affected_by_effect(1, EFFECT_CANNOT_LOSE_LP))) {
if (is_player_affected_by_effect(0, EFFECT_CANNOT_LOSE_LP))
winp = 0;
else if (is_player_affected_by_effect(1, EFFECT_CANNOT_LOSE_LP))
winp = 1;
else
winp = PLAYER_NONE;
rea = 1;
}
if (core.overdraw[1] && core.overdraw[0] && !(is_player_affected_by_effect(0, EFFECT_CANNOT_LOSE_DECK) && is_player_affected_by_effect(1, EFFECT_CANNOT_LOSE_DECK))) {
if (is_player_affected_by_effect(0, EFFECT_CANNOT_LOSE_DECK))
winp = 0;
else if (is_player_affected_by_effect(1, EFFECT_CANNOT_LOSE_DECK))
winp = 1;
else
winp = PLAYER_NONE;
rea = 2;
}
if (is_flag(DUEL_RELAY)) {
if (winp == PLAYER_NONE) {
bool p1 = relay_check(0);
bool p2 = relay_check(1);
if (p1 && !p2)
winp = 0;
else if (!p1 && p2)
winp = 1;
else if (p1 && p2) {
winp = 5;
core.overdraw[0] = core.overdraw[1] = false;
}
} else if (winp < PLAYER_NONE)
if (relay_check(1 - winp)) {
winp = 5;
core.overdraw[0] = core.overdraw[1] = false;
}
}
if(winp != 5) {
auto message = pduel->new_message(MSG_WIN);
message->write<uint8_t>(winp);
message->write<uint8_t>(rea);
core.overdraw[0] = core.overdraw[1] = false;
core.win_player = 5;
core.win_reason = 0;
} else if(core.win_player != 5) {
auto message = pduel->new_message(MSG_WIN);
message->write<uint8_t>(core.win_player);
message->write<uint8_t>(core.win_reason);
core.win_player = 5;
core.win_reason = 0;
core.overdraw[0] = core.overdraw[1] = false;
}
}
return FALSE;
}
case 2: {
//disable check
uint8_t tp = infos.turn_player;
for(uint8_t p = 0; p < 2; ++p) {
for(auto& pcard : player[tp].list_mzone) {
if(pcard)
add_to_disable_check_list(pcard);
}
for(auto& pcard : player[tp].list_szone) {
if(pcard)
add_to_disable_check_list(pcard);
}
tp = 1 - tp;
}
adjust_disable_check_list();
emplace_process<Processors::RefreshLoc>();
return FALSE;
}
case 3: {
//trap monster
core.trap_monster_adjust_set[0].clear();
core.trap_monster_adjust_set[1].clear();
for(uint8_t p = 0; p < 2; ++p) {
for(auto& pcard : player[p].list_mzone) {
if(!pcard) continue;
if((pcard->get_type() & TYPE_TRAPMONSTER) && pcard->is_affected_by_effect(EFFECT_DISABLE_TRAPMONSTER)) {
core.trap_monster_adjust_set[p].insert(pcard);
}
}
}
if(core.trap_monster_adjust_set[0].size() || core.trap_monster_adjust_set[1].size()) {
core.re_adjust = true;
emplace_process<Processors::TrapMonsterAdjust>();
}
return FALSE;
}
case 4: {
//control
core.control_adjust_set[0].clear();
core.control_adjust_set[1].clear();
card_set reason_cards;
for(uint8_t p = 0; p < 2; ++p) {
for(auto& pcard : player[p].list_mzone) {
if(!pcard) continue;
uint8_t cur = pcard->current.controler;
auto [ref, peffect] = pcard->refresh_control_status();
if(cur != ref && pcard->is_capable_change_control()) {
core.control_adjust_set[p].insert(pcard);
if(peffect && (!(peffect->type & EFFECT_TYPE_SINGLE) || peffect->condition))
reason_cards.insert(peffect->get_handler());
}
}
}
if(core.control_adjust_set[0].size() || core.control_adjust_set[1].size()) {
core.re_adjust = true;
get_control(core.control_adjust_set[1 - infos.turn_player], nullptr, PLAYER_NONE, infos.turn_player, 0, 0, 0xff);
get_control(core.control_adjust_set[infos.turn_player], nullptr, PLAYER_NONE, 1 - infos.turn_player, 0, 0, 0xff);
for(auto& rcard : reason_cards) {
++core.readjust_map[rcard];
if(core.readjust_map[rcard] > 3)
destroy(rcard, nullptr, REASON_RULE, PLAYER_NONE);
}
}
core.last_control_changed_id = infos.field_id;
return FALSE;
}
case 5: {
//remove brainwashing
if(core.global_flag & GLOBALFLAG_BRAINWASHING_CHECK) {
core.control_adjust_set[0].clear();
core.control_adjust_set[1].clear();
effect_set eset;
filter_field_effect(EFFECT_REMOVE_BRAINWASHING, &eset, false);
if(core.remove_brainwashing = !eset.empty(); core.remove_brainwashing) {
for(uint8_t p = 0; p < 2; ++p) {
for(auto& pcard : player[p].list_mzone) {
if(!pcard || !pcard->is_affected_by_effect(EFFECT_REMOVE_BRAINWASHING))
continue;
//the opposite of pcard->check_control_effect()
auto pr = pcard->single_effect.equal_range(EFFECT_SET_CONTROL);
for(auto eit = pr.first; eit != pr.second;) {
effect* peffect = eit->second;
++eit;
if(!peffect->condition)
peffect->handler->remove_effect(peffect);
}
if(p != pcard->owner && pcard->is_capable_change_control())
core.control_adjust_set[p].insert(pcard);
}
}
}
if(core.control_adjust_set[0].size() || core.control_adjust_set[1].size()) {
core.re_adjust = true;
get_control(core.control_adjust_set[1 - infos.turn_player], nullptr, PLAYER_NONE, infos.turn_player, 0, 0, 0xff);
get_control(core.control_adjust_set[infos.turn_player], nullptr, PLAYER_NONE, 1 - infos.turn_player, 0, 0, 0xff);
}
}
arg.step = 7;
return FALSE;
}
case 8: {
if(adjust_grant_effect())
core.re_adjust = true;
return FALSE;
}
case 9: {
if(core.selfdes_disabled) {
arg.step = 10;
return FALSE;
}
//self destroy
adjust_self_destroy_set();
return FALSE;
}
case 10: {
//equip check
uint8_t tp = infos.turn_player;
card_set destroy_set;
for(uint8_t p = 0; p < 2; ++p) {
for(uint8_t i = 0; i < 5; ++i) {
card* pcard = player[tp].list_szone[i];
if(pcard && pcard->equiping_target && !pcard->is_affected_by_effect(EFFECT_EQUIP_LIMIT, pcard->equiping_target))
destroy_set.insert(pcard);
}
tp = 1 - tp;
}
if(destroy_set.size()) {
core.re_adjust = true;
destroy(std::move(destroy_set), nullptr, REASON_RULE, PLAYER_NONE);
}
return FALSE;
}
case 11: {
//position
uint32_t tp = infos.turn_player, pos;
card_set pos_adjust;
effect_set eset;
for(uint8_t p = 0; p < 2; ++p) {
for(auto& pcard : player[tp].list_mzone) {
if(!pcard || ((pcard->data.type & TYPE_LINK) && (pcard->data.type & TYPE_MONSTER)) || pcard->is_affected_by_effect(EFFECT_CANNOT_CHANGE_POS_E))
continue;
eset.clear();
pcard->filter_effect(EFFECT_SET_POSITION, &eset);
if(eset.size()) {
pos = eset.back()->get_value();
if((pos & 0xff) != pcard->current.position) {
pos_adjust.insert(pcard);
pcard->position_param = pos;
if(pcard->is_status(STATUS_JUST_POS))
pcard->set_status(STATUS_CONTINUOUS_POS, TRUE);
else
pcard->set_status(STATUS_CONTINUOUS_POS, FALSE);
} else
pcard->set_status(STATUS_CONTINUOUS_POS, FALSE);
pcard->set_status(STATUS_JUST_POS, FALSE);
}
}
tp = 1 - tp;
}
if(pos_adjust.size()) {
core.re_adjust = true;
auto ng = pduel->new_group();
ng->container.swap(pos_adjust);
ng->is_readonly = true;
emplace_process<Processors::ChangePos>(ng, nullptr, PLAYER_NONE, true);
}
return FALSE;
}
case 12: {
//shuffle check
for(auto& pcard : player[0].list_hand) {
effect* pub = pcard->is_affected_by_effect(EFFECT_PUBLIC);
if(!pub && pcard->is_position(POS_FACEUP))
core.shuffle_hand_check[0] = true;
pcard->current.position = pub ? POS_FACEUP : POS_FACEDOWN;
}
for(auto& pcard : player[1].list_hand) {
effect* pub = pcard->is_affected_by_effect(EFFECT_PUBLIC);
if(!pub && pcard->is_position(POS_FACEUP))
core.shuffle_hand_check[1] = true;
pcard->current.position = pub ? POS_FACEUP : POS_FACEDOWN;
}
if(core.shuffle_hand_check[infos.turn_player])
shuffle(infos.turn_player, LOCATION_HAND);
if(core.shuffle_hand_check[1 - infos.turn_player])
shuffle(1 - infos.turn_player, LOCATION_HAND);
return FALSE;
}
case 13: {
//reverse_deck
if(core.global_flag & GLOBALFLAG_DECK_REVERSE_CHECK) {
effect_set eset;
filter_field_effect(EFFECT_REVERSE_DECK, &eset, false);
auto reversed = !eset.empty();
if(std::exchange(core.deck_reversed, reversed) != reversed) {
reverse_deck(0);
reverse_deck(1);
pduel->new_message(MSG_REVERSE_DECK);
if(reversed) {
if(player[0].list_main.size()) {
card* ptop = player[0].list_main.back();
auto message = pduel->new_message(MSG_DECK_TOP);
message->write<uint8_t>(0);
message->write<uint32_t>(0);
message->write<uint32_t>(ptop->data.code);
message->write<uint32_t>(ptop->current.position);
}
if(player[1].list_main.size()) {
card* ptop = player[1].list_main.back();
auto message = pduel->new_message(MSG_DECK_TOP);
message->write<uint8_t>(1);
message->write<uint32_t>(0);
message->write<uint32_t>(ptop->data.code);
message->write<uint32_t>(ptop->current.position);
}
}
}
}
return FALSE;
}
case 14: {
//attack cancel
card* attacker = core.attacker;
if(!attacker)
return FALSE;
if(!attacker->is_affected_by_effect(EFFECT_UNSTOPPABLE_ATTACK)) {
if(attacker->is_status(STATUS_ATTACK_CANCELED))
return FALSE;
}
if(infos.phase != PHASE_DAMAGE && infos.phase != PHASE_DAMAGE_CAL) {
if(!core.attacker->is_capable_attack()
|| core.attacker->current.controler != core.attacker->attack_controler
|| core.attacker->fieldid_r != core.pre_field[0]) {
attacker->set_status(STATUS_ATTACK_CANCELED, TRUE);
return FALSE;
}
if(core.attack_rollback)
return FALSE;
std::set<uint32_t> fidset;
for(auto& pcard : player[1 - infos.turn_player].list_mzone) {
if(pcard)
fidset.insert(pcard->fieldid_r);
}
if(fidset != core.opp_mzone || !confirm_attack_target())
core.attack_rollback = true;
} else {
if(core.attacker->current.location != LOCATION_MZONE || core.attacker->fieldid_r != core.pre_field[0]
|| ((core.attacker->current.position & POS_DEFENSE) && !(core.attacker->is_affected_by_effect(EFFECT_DEFENSE_ATTACK)))
|| core.attacker->current.controler != core.attacker->attack_controler
|| (core.attack_target && (core.attack_target->current.location != LOCATION_MZONE
|| core.attack_target->current.controler != core.attack_target->attack_controler
|| core.attack_target->fieldid_r != core.pre_field[1])))
core.attacker->set_status(STATUS_ATTACK_CANCELED, TRUE);
}
return FALSE;
}
case 15: {
raise_event(nullptr, EVENT_ADJUST, nullptr, 0, PLAYER_NONE, PLAYER_NONE, 0);
process_instant_event();
return FALSE;
}
case 16: {
if(core.re_adjust) {
arg.step = Processors::restart;
return FALSE;
}
if(core.shuffle_hand_check[0])
shuffle(0, LOCATION_HAND);
if(core.shuffle_hand_check[1])
shuffle(1, LOCATION_HAND);
if(core.shuffle_deck_check[0])
shuffle(0, LOCATION_DECK);
if(core.shuffle_deck_check[1])
shuffle(1, LOCATION_DECK);
return TRUE;
}
}
return TRUE;
}
bool field::process(Processors::Startup& arg) {
switch(arg.step) {
case 0: {
core.shuffle_hand_check[0] = false;
core.shuffle_hand_check[1] = false;
core.shuffle_deck_check[0] = false;
core.shuffle_deck_check[1] = false;
raise_event(nullptr, EVENT_STARTUP, nullptr, 0, 0, 0, 0);
process_instant_event();
return FALSE;
}
case 1: {
for(int p = 0; p < 2; p++) {
core.shuffle_hand_check[p] = false;
core.shuffle_deck_check[p] = false;
if(player[p].start_count > 0)
draw(nullptr, REASON_RULE, PLAYER_NONE, p, player[p].start_count);
auto list_size = player[p].extra_lists_main.size();
for(size_t l = 0; l < list_size; l++) {
auto& main = player[p].extra_lists_main[l];
auto& hand = player[p].extra_lists_hand[l];
for(int i = 0; i < player[p].start_count && !main.empty(); ++i) {
card* pcard = main.back();
main.pop_back();
hand.push_back(pcard);
pcard->current.controler = p;
pcard->current.location = LOCATION_HAND;
pcard->current.sequence = static_cast<uint32_t>(hand.size() - 1);
pcard->current.position = POS_FACEDOWN;
}
}
}
emplace_process<Processors::Turn>(0);
return TRUE;
}
}
return TRUE;
}
bool field::process(Processors::SortDeck& arg) {
auto sort_player = arg.sort_player;
auto target_player = arg.target_player;
auto count = arg.count;
bool bottom = arg.bottom;
auto& list = player[target_player].list_main;
if(count > list.size())
count = static_cast<uint16_t>(list.size());
switch(arg.step) {
case 0: {
if(bottom) {
const auto clit = list.begin();
core.select_cards.assign(clit, clit + count);
} else {
const auto clit = list.rbegin();
core.select_cards.assign(clit, clit + count);
}
emplace_process<Processors::SortCard>(sort_player, false);
return FALSE;
}
case 1: {
if(returns.at<int8_t>(0) == -1 || count == 0)
return TRUE;
card_vector tc(core.select_cards.size());
for(uint32_t i = 0; i < count; ++i)
tc[returns.at<uint8_t>(i)] = core.select_cards[i];
for(uint32_t i = 0; i < count; ++i) {
card* pcard = nullptr;
if(bottom)
pcard = tc[i];
else
pcard = tc[count - i - 1];
auto message = pduel->new_message(MSG_MOVE);
message->write<uint32_t>(0);
message->write(pcard->get_info_location());
list.erase(list.begin() + pcard->current.sequence);
if(bottom)
list.insert(list.begin(), pcard);
else
list.push_back(pcard);
reset_sequence(target_player, LOCATION_DECK);
message->write(pcard->get_info_location());
message->write<uint32_t>(pcard->current.reason);
}
if(core.global_flag & GLOBALFLAG_DECK_REVERSE_CHECK) {
card* ptop = list.back();
if(core.deck_reversed || (ptop->current.position == POS_FACEUP_DEFENSE)) {
auto message = pduel->new_message(MSG_DECK_TOP);
message->write<uint8_t>(target_player);
message->write<uint32_t>(0);
message->write<uint32_t>(ptop->data.code);
message->write<uint32_t>(ptop->current.position);
}
}
return TRUE;
}
}
return TRUE;
}
bool field::process(Processors::DiscardHand& arg) {
auto playerid = arg.playerid;
auto min = arg.min;
auto max = arg.max;
auto reason = arg.reason;
switch(arg.step) {
case 0: {
auto message = pduel->new_message(MSG_HINT);
message->write<uint8_t>(HINT_SELECTMSG);
message->write<uint8_t>(playerid);
if(reason & REASON_DISCARD)
message->write<uint64_t>(501);
else
message->write<uint64_t>(504);
emplace_process<Processors::SelectCard>(playerid, false, min, max);
return FALSE;
}
case 1: {
if(return_cards.list.empty())
returns.set<int32_t>(0, 0);
else
send_to(card_set{ return_cards.list.begin(), return_cards.list.end() }, core.reason_effect, reason, core.reason_player, PLAYER_NONE, LOCATION_GRAVE, 0, POS_FACEUP);
return TRUE;
}
}
return TRUE;
}
bool field::process(Processors::AttackDisable& arg) {
switch(arg.step) {
case 0: {
auto* attacker = core.attacker;
if(!attacker
|| core.effect_damage_step != 0
|| (attacker->fieldid_r != core.pre_field[0])
|| (attacker->current.location != LOCATION_MZONE)
|| !attacker->is_capable_attack()
|| !attacker->is_affect_by_effect(core.reason_effect)
|| attacker->is_affected_by_effect(EFFECT_UNSTOPPABLE_ATTACK)) {
returns.set<int32_t>(0, 0);
return TRUE;
}
auto* peffect = pduel->new_effect();
peffect->code = EFFECT_ATTACK_DISABLED;
peffect->type = EFFECT_TYPE_SINGLE;
attacker->add_effect(peffect);
attacker->set_status(STATUS_ATTACK_CANCELED, TRUE);
raise_event(attacker, EVENT_ATTACK_DISABLED, core.reason_effect, 0, core.reason_player, PLAYER_NONE, 0);
process_instant_event();
return FALSE;
}
case 1: {
returns.set<int32_t>(0, 1);
return TRUE;
}
}
return TRUE;
}
bool field::process(Processors::SelectFusion& arg) {
auto playerid = arg.playerid;
auto fusion_materials = arg.fusion_materials;
auto pcard = arg.pcard;
auto forced_materials = arg.forced_materials;
auto chkf = arg.chkf;
switch(arg.step) {
case 0: {
effect_set eset;
pcard->fusion_filter_valid(fusion_materials, forced_materials, chkf, &eset);
core.select_effects.clear();
core.select_options.clear();
if(eset.empty())
return TRUE;
for(const auto& peff : eset) {
core.select_effects.push_back(peff);
core.select_options.push_back(peff->description);
}
if(core.select_options.size() == 1)
returns.set<int32_t>(0, 0);
else
emplace_process<Processors::SelectOption>(playerid);
return FALSE;
}
case 1: {
core.fusion_materials.clear();
if(!core.select_effects[returns.at<int32_t>(0)])
return TRUE;
auto& e = core.sub_solving_event.emplace_back();
e.event_cards = fusion_materials;
e.reason_effect = core.select_effects[returns.at<int32_t>(0)];
e.reason_player = playerid;
pduel->lua->add_param<LuaParam::GROUP>(forced_materials);
pduel->lua->add_param<LuaParam::INT>(chkf);
pduel->lua->add_param<LuaParam::EFFECT>(core.reason_effect);
emplace_process<Processors::ExecuteOperation>(core.select_effects[returns.at<int32_t>(0)], playerid);
return FALSE;
}
}
return TRUE;
}
bool field::process(Processors::RefreshRelay& arg) {
auto step = arg.step;
switch(step) {
case 0:
case 1:
if(player[step].recharge)
next_player(step);
return FALSE;
default:
return TRUE;
}
}