/* * Copyright (c) 2010-2015, Argon Sun (Fluorohydride) * Copyright (c) 2017-2026, Edoardo Lolletti (edo9300) * * SPDX-License-Identifier: AGPL-3.0-or-later */ #include "duel.h" #include "effect.h" #include "field.h" #include "scriptlib.h" #define LUA_MODULE Effect #define LUA_CLASS effect #include "function_array_helper.h" namespace { namespace LUA_NAMESPACE { using namespace scriptlib; LUA_STATIC_FUNCTION(CreateEffect) { auto pcard = lua_get(L, 1); effect* peffect = pduel->new_effect(); peffect->effect_owner = pduel->game_field->core.reason_player; peffect->owner = pcard; interpreter::pushobject(L, peffect); return 1; } LUA_STATIC_FUNCTION(GlobalEffect) { effect* peffect = pduel->new_effect(); peffect->effect_owner = 0; peffect->owner = pduel->game_field->temp_card; interpreter::pushobject(L, peffect); return 1; } LUA_FUNCTION(Clone) { interpreter::pushobject(L, self->clone()); return 1; } LUA_FUNCTION(Reset) { if(self->owner == nullptr) return 0; if(self->is_flag(EFFECT_FLAG_FIELD_ONLY)) pduel->game_field->remove_effect(self); else { if(self->handler) self->handler->remove_effect(self); else pduel->game_field->core.reseted_effects.insert(self); } return 0; } LUA_FUNCTION(GetFieldID) { lua_pushinteger(L, self->id); return 1; } LUA_FUNCTION(SetDescription) { check_param_count(L, 2); self->description = lua_get(L, 2); return 0; } LUA_FUNCTION(SetCode) { check_param_count(L, 2); self->code = lua_get(L, 2); return 0; } LUA_FUNCTION(SetRange) { check_param_count(L, 2); auto& range = self->range; range = lua_get(L, 2); if((range & (LOCATION_MMZONE | LOCATION_EMZONE)) == (LOCATION_MMZONE | LOCATION_EMZONE)) range |= LOCATION_MZONE; if(range & LOCATION_MZONE) range &= ~(LOCATION_MMZONE | LOCATION_EMZONE); if((range & (LOCATION_FZONE | LOCATION_STZONE | LOCATION_PZONE)) == (LOCATION_FZONE | LOCATION_STZONE | LOCATION_PZONE)) range |= LOCATION_SZONE; if(range & LOCATION_SZONE) range &= ~(LOCATION_FZONE | LOCATION_STZONE | LOCATION_PZONE); return 0; } LUA_FUNCTION(SetTargetRange) { check_param_count(L, 3); self->s_range = lua_get(L, 2); self->o_range = lua_get(L, 3); self->flag[0] &= ~EFFECT_FLAG_ABSOLUTE_TARGET; return 0; } LUA_FUNCTION(SetAbsoluteRange) { check_param_count(L, 4); auto playerid = lua_get(L, 2); auto s = lua_get(L, 3); auto o = lua_get(L, 4); if(playerid == 0) { self->s_range = s; self->o_range = o; } else { self->s_range = o; self->o_range = s; } self->flag[0] |= EFFECT_FLAG_ABSOLUTE_TARGET; return 0; } LUA_FUNCTION(SetCountLimit) { check_param_count(L, 2); auto count = lua_get(L, 2); if(count == 0) lua_error(L, "The count must not be 0"); uint8_t hopt_index = 0; uint32_t code = 0; if(lua_istable(L, 3)) { auto top = lua_gettop(L); lua_rawgeti(L, 3, 1); code = lua_get(L, -1); if(code != 0 && lua_rawgeti(L, 3, 2) != LUA_TNIL) { hopt_index = lua_get(L, -1); } lua_settop(L, top); } else code = lua_get(L, 3); uint8_t flag = lua_get(L, 4); if((flag & EFFECT_COUNT_CODE_CHAIN) != 0 && code == 0) flag |= EFFECT_COUNT_CODE_SINGLE; if((flag & (EFFECT_COUNT_CODE_DUEL | EFFECT_COUNT_CODE_CHAIN)) == (EFFECT_COUNT_CODE_DUEL | EFFECT_COUNT_CODE_CHAIN)) lua_error(L, "Cannot use EFFECT_COUNT_CODE_DUEL together with EFFECT_COUNT_CODE_CHAIN"); if((flag & EFFECT_COUNT_CODE_DUEL) != 0 && code == 0) lua_error(L, "EFFECT_COUNT_CODE_DUEL must have an associated code"); self->flag[0] |= EFFECT_FLAG_COUNT_LIMIT; self->count_limit = count; self->count_limit_max = count; self->count_code = code; self->count_flag = flag; self->count_hopt_index = hopt_index; return 0; } LUA_FUNCTION(SetReset) { check_param_count(L, 2); auto v = lua_get(L, 2); auto c = lua_get(L, 3); if(c == 0) c = 1; if(v & (RESET_PHASE) && !(v & (RESET_SELF_TURN | RESET_OPPO_TURN))) v |= (RESET_SELF_TURN | RESET_OPPO_TURN); self->reset_flag = v; self->reset_count = c; return 0; } LUA_FUNCTION(SetType) { check_param_count(L, 2); auto v = lua_get(L, 2); if (v & 0x0ff0) v |= EFFECT_TYPE_ACTIONS; else v &= ~EFFECT_TYPE_ACTIONS; if(v & (EFFECT_TYPE_ACTIVATE | EFFECT_TYPE_IGNITION | EFFECT_TYPE_QUICK_O | EFFECT_TYPE_QUICK_F)) v |= EFFECT_TYPE_FIELD; if(v & EFFECT_TYPE_ACTIVATE) self->range = LOCATION_SZONE + LOCATION_FZONE + LOCATION_HAND; if(v & EFFECT_TYPE_FLIP) { self->code = EVENT_FLIP; if(!(v & EFFECT_TYPE_TRIGGER_O)) v |= EFFECT_TYPE_TRIGGER_F; } self->type = v; return 0; } LUA_FUNCTION(SetProperty) { check_param_count(L, 2); auto v1 = lua_get(L, 2); auto v2 = lua_get(L, 3); self->flag[0] = (self->flag[0] & 0x4fu) | (v1 & ~0x4fu); self->flag[1] = v2; return 0; } LUA_FUNCTION(SetLabel) { check_param_count(L, 2); self->label.clear(); lua_iterate_table_or_stack(L, 2, lua_gettop(L), [&] { self->label.push_back(lua_get(L, -1)); }); return 0; } LUA_FUNCTION(SetLabelObject) { check_param_count(L, 2); if(self->label_object) ensure_luaL_stack(luaL_unref, L, LUA_REGISTRYINDEX, self->label_object); self->label_object = 0; if(lua_isnoneornil(L, 2)) return 0; if(lua_get(L, 2) != nullptr || lua_istable(L, 2)) { lua_pushvalue(L, 2); self->label_object = ensure_luaL_stack(luaL_ref, L, LUA_REGISTRYINDEX); } else lua_error(L, "Parameter 2 should be \"Card\" or \"Effect\" or \"Group\" or \"table\"."); return 0; } LUA_FUNCTION(SetCategory) { check_param_count(L, 2); auto v = lua_get(L, 2); self->category = v; return 0; } LUA_FUNCTION(SetHintTiming) { check_param_count(L, 2); auto vs = lua_get(L, 2); auto vo = lua_get(L, 3, vs); self->hint_timing[0] = vs; self->hint_timing[1] = vo; return 0; } LUA_FUNCTION(SetCondition) { check_param_count(L, 2); const auto findex = lua_get(L, 2); if(self->condition) ensure_luaL_stack(luaL_unref, L, LUA_REGISTRYINDEX, self->condition); self->condition = interpreter::get_function_handle(L, findex); return 0; } LUA_FUNCTION(SetTarget) { check_param_count(L, 2); const auto findex = lua_get(L, 2); if(self->target) ensure_luaL_stack(luaL_unref, L, LUA_REGISTRYINDEX, self->target); self->target = interpreter::get_function_handle(L, findex); return 0; } LUA_FUNCTION(SetCost) { check_param_count(L, 2); const auto findex = lua_get(L, 2); if(self->cost) ensure_luaL_stack(luaL_unref, L, LUA_REGISTRYINDEX, self->cost); self->cost = interpreter::get_function_handle(L, findex); return 0; } LUA_FUNCTION(SetValue) { check_param_count(L, 2); if(self->value && self->is_flag(EFFECT_FLAG_FUNC_VALUE)) ensure_luaL_stack(luaL_unref, L, LUA_REGISTRYINDEX, self->value); if (lua_isfunction(L, 2)) { self->value = interpreter::get_function_handle(L, 2); self->flag[0] |= EFFECT_FLAG_FUNC_VALUE; } else { self->flag[0] &= ~EFFECT_FLAG_FUNC_VALUE; if(lua_isboolean(L, 2)) self->value = lua_get(L, 2); else { auto value = lua_get(L, 2); if(value > INT32_MAX || value < INT32_MIN) { lua_pushinteger(L, value); lua_pushcclosure(L, [](lua_State* L) -> int32_t { lua_pushvalue(L, lua_upvalueindex(1)); return 1; }, 1); self->value = interpreter::get_function_handle(L, -1); self->flag[0] |= EFFECT_FLAG_FUNC_VALUE; } else self->value = static_cast(value); } } return 0; } LUA_FUNCTION(SetOperation) { check_param_count(L, 2); if(self->operation) ensure_luaL_stack(luaL_unref, L, LUA_REGISTRYINDEX, self->operation); self->operation = 0; const auto findex = lua_get(L, 2); if(findex) self->operation = interpreter::get_function_handle(L, findex); return 0; } LUA_FUNCTION(SetOwnerPlayer) { auto p = lua_get(L, 2); if(p != 0 && p != 1) return 0; self->effect_owner = p; return 0; } LUA_FUNCTION(GetDescription) { lua_pushinteger(L, self->description); return 1; } LUA_FUNCTION(GetCode) { lua_pushinteger(L, self->code); return 1; } LUA_FUNCTION(GetRange) { lua_pushinteger(L, self->range); return 1; } LUA_FUNCTION(GetTargetRange) { lua_pushinteger(L, self->s_range); lua_pushinteger(L, self->o_range); return 2; } LUA_FUNCTION(GetCountLimit) { lua_pushinteger(L, self->count_limit); lua_pushinteger(L, self->count_limit_max); lua_pushinteger(L, self->count_code); lua_pushinteger(L, self->count_flag); //return the count_code flags as their shifted representation lua_pushinteger(L, self->count_hopt_index); return 5; } LUA_FUNCTION(GetReset) { lua_pushinteger(L, self->reset_flag); lua_pushinteger(L, self->reset_count); return 2; } LUA_FUNCTION(GetType) { lua_pushinteger(L, self->type); return 1; } LUA_FUNCTION(GetProperty) { lua_pushinteger(L, self->flag[0]); lua_pushinteger(L, self->flag[1]); return 2; } LUA_FUNCTION(GetLabel) { const auto& label = self->label; if(label.empty()) { lua_pushinteger(L, 0); return 1; } luaL_checkstack(L, static_cast(label.size()), nullptr); for(const auto& lab : label) lua_pushinteger(L, lab); return static_cast(label.size()); } LUA_FUNCTION(GetLabelObject) { if(!self->label_object) { lua_pushnil(L); return 1; } lua_rawgeti(L, LUA_REGISTRYINDEX, self->label_object); if(lua_touserdata(L, -1) == nullptr && !lua_istable(L, -1)) { lua_pop(L, 1); lua_pushnil(L); } return 1; } LUA_FUNCTION(GetCategory) { lua_pushinteger(L, self->category); return 1; } LUA_FUNCTION(GetOwner) { interpreter::pushobject(L, self->get_owner()); return 1; } LUA_FUNCTION(GetHandler) { interpreter::pushobject(L, self->get_handler()); return 1; } LUA_FUNCTION(GetOwnerPlayer) { lua_pushinteger(L, self->get_owner_player()); return 1; } LUA_FUNCTION(GetHandlerPlayer) { lua_pushinteger(L, self->get_handler_player()); return 1; } LUA_FUNCTION(GetHintTiming) { lua_pushinteger(L, self->hint_timing[0]); lua_pushinteger(L, self->hint_timing[1]); return 2; } LUA_FUNCTION(GetCondition) { interpreter::pushobject(L, self->condition); return 1; } LUA_FUNCTION(GetTarget) { interpreter::pushobject(L, self->target); return 1; } LUA_FUNCTION(GetCost) { interpreter::pushobject(L, self->cost); return 1; } LUA_FUNCTION(GetValue) { if(self->is_flag(EFFECT_FLAG_FUNC_VALUE)) interpreter::pushobject(L, self->value); else lua_pushinteger(L, self->value); return 1; } LUA_FUNCTION(GetOperation) { interpreter::pushobject(L, self->operation); return 1; } LUA_FUNCTION(GetActiveType) { lua_pushinteger(L, self->get_active_type()); return 1; } LUA_FUNCTION(IsActiveType) { check_param_count(L, 2); lua_pushboolean(L, (self->get_active_type() & lua_get(L, 2)) != 0); return 1; } LUA_FUNCTION(IsHasProperty) { check_param_count(L, 2); auto tflag1 = lua_get(L, 2); auto tflag2 = lua_get(L, 3); lua_pushboolean(L, ((!tflag1 || (self->flag[0] & tflag1)) && (!tflag2 || (self->flag[1] & tflag2)))); return 1; } LUA_FUNCTION(IsHasCategory) { check_param_count(L, 2); lua_pushboolean(L, (self->category & lua_get(L, 2)) != 0); return 1; } LUA_FUNCTION(IsHasType) { check_param_count(L, 2); lua_pushboolean(L, (self->type & lua_get(L, 2)) != 0); return 1; } LUA_FUNCTION(IsActivatable) { check_param_count(L, 2); auto playerid = lua_get(L, 2); bool neglect_loc = lua_get(L, 3); bool neglect_target = lua_get(L, 4); lua_pushboolean(L, self->is_activateable(playerid, pduel->game_field->nil_event, 0, 0, neglect_target, neglect_loc)); return 1; } LUA_FUNCTION(IsActivated) { lua_pushboolean(L, (self->type & 0x7f0) != 0); return 1; } LUA_FUNCTION(GetActivateLocation) { lua_pushinteger(L, self->active_location); return 1; } LUA_FUNCTION(GetActivateSequence) { lua_pushinteger(L, self->active_sequence); return 1; } LUA_FUNCTION(CheckCountLimit) { check_param_count(L, 2); auto p = lua_get(L, 2); lua_pushboolean(L, self->check_count_limit(p)); return 1; } LUA_FUNCTION(UseCountLimit) { check_param_count(L, 2); auto p = lua_get(L, 2); auto count = lua_get(L, 3); bool oath_only = lua_get(L, 4); if (!oath_only || self->count_flag & EFFECT_COUNT_CODE_OATH) while(count) { self->dec_count(p); --count; } return 0; } LUA_FUNCTION(RestoreCountLimit) { check_param_count(L, 2); auto p = lua_get(L, 2); auto count = lua_get(L, 3); bool oath_only = lua_get(L, 4); if(!oath_only || self->count_flag & EFFECT_COUNT_CODE_OATH) while(count) { self->inc_count(p); --count; } return 0; } LUA_FUNCTION_EXISTING(GetLuaRef, get_lua_ref); LUA_FUNCTION_EXISTING(FromLuaRef, from_lua_ref); LUA_FUNCTION_EXISTING(IsDeleted, is_deleted_object); } } void scriptlib::push_effect_lib(lua_State* L) { static constexpr auto effectlib = GET_LUA_FUNCTIONS_ARRAY(); static_assert(effectlib.back().name == nullptr); lua_createtable(L, 0, static_cast(effectlib.size() - 1)); ensure_luaL_stack(luaL_setfuncs, L, effectlib.data(), 0); lua_pushstring(L, "__index"); lua_pushvalue(L, -2); lua_rawset(L, -3); lua_setglobal(L, "Effect"); }