mirror of
https://github.com/edo9300/ygopro-core
synced 2026-08-25 16:23:07 -04:00
Functions from the auxiliary library, assume the stack has always at least 5 empty slots, add macro ensure_luaL_stack to wrap every such call so that the stack is checked for enough free space beforehand. This will make mandatory to use the latest version of visual studio 2017 as minimum for windows (it was already being required for c++17 support regardless), as it needs the conformant c++ preprocessor option to be used.
471 lines
13 KiB
C++
471 lines
13 KiB
C++
/*
|
|
* Copyright (c) 2010-2015, Argon Sun (Fluorohydride)
|
|
* Copyright (c) 2017-2024, Edoardo Lolletti (edo9300) <edoardo762@gmail.com>
|
|
*
|
|
* SPDX-License-Identifier: AGPL-3.0-or-later
|
|
*/
|
|
#include "duel.h"
|
|
#include "effect.h"
|
|
#include "field.h"
|
|
#include "scriptlib.h"
|
|
|
|
#define LUA_MODULE Effect
|
|
using LUA_CLASS = effect;
|
|
#include "function_array_helper.h"
|
|
|
|
namespace {
|
|
|
|
using namespace scriptlib;
|
|
|
|
LUA_STATIC_FUNCTION(CreateEffect) {
|
|
auto pcard = lua_get<card*, true>(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<uint64_t>(L, 2);
|
|
return 0;
|
|
}
|
|
LUA_FUNCTION(SetCode) {
|
|
check_param_count(L, 2);
|
|
self->code = lua_get<uint32_t>(L, 2);
|
|
return 0;
|
|
}
|
|
LUA_FUNCTION(SetRange) {
|
|
check_param_count(L, 2);
|
|
auto& range = self->range;
|
|
range = lua_get<uint16_t>(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<uint16_t>(L, 2);
|
|
self->o_range = lua_get<uint16_t>(L, 3);
|
|
self->flag[0] &= ~EFFECT_FLAG_ABSOLUTE_TARGET;
|
|
return 0;
|
|
}
|
|
LUA_FUNCTION(SetAbsoluteRange) {
|
|
check_param_count(L, 4);
|
|
auto playerid = lua_get<uint8_t>(L, 2);
|
|
auto s = lua_get<uint16_t>(L, 3);
|
|
auto o = lua_get<uint16_t>(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<uint8_t>(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)) {
|
|
lua_pushnil(L);
|
|
if(lua_next(L, 3) != 0) {
|
|
code = lua_get<uint32_t>(L, -1);
|
|
lua_pop(L, 1);
|
|
if(!code) {
|
|
hopt_index = 0;
|
|
lua_pop(L, 1); //manually pop the key from the stack as there won't be a next iteration
|
|
} else if(lua_next(L, 3) != 0) {
|
|
hopt_index = lua_get<uint8_t>(L, -1);
|
|
lua_pop(L, 1);
|
|
lua_pop(L, 1); //manually pop the key from the stack as there won't be a next iteration
|
|
}
|
|
}
|
|
} else
|
|
code = lua_get<uint32_t, 0>(L, 3);
|
|
uint8_t flag = lua_get<uint8_t, 0>(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<uint32_t>(L, 2);
|
|
auto c = lua_get<uint16_t, 0>(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<uint16_t>(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<uint32_t>(L, 2);
|
|
auto v2 = lua_get<uint32_t, 0>(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<lua_Integer>(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<lua_obj*>(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<uint32_t>(L, 2);
|
|
self->category = v;
|
|
return 0;
|
|
}
|
|
LUA_FUNCTION(SetHintTiming) {
|
|
check_param_count(L, 2);
|
|
auto vs = lua_get<uint32_t>(L, 2);
|
|
auto vo = lua_get<uint32_t>(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<function, true>(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<function, true>(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<function, true>(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<bool>(L, 2);
|
|
else {
|
|
auto value = lua_get<lua_Integer>(L, 2);
|
|
if(value > INT32_MAX || value < INT32_MIN) {
|
|
lua_pushvalue(L, 2);
|
|
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<int32_t>(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<function>(L, 2);
|
|
if(findex)
|
|
self->operation = interpreter::get_function_handle(L, findex);
|
|
return 0;
|
|
}
|
|
LUA_FUNCTION(SetOwnerPlayer) {
|
|
auto p = lua_get<uint8_t>(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<int>(label.size()), nullptr);
|
|
for(const auto& lab : label)
|
|
lua_pushinteger(L, lab);
|
|
return static_cast<int32_t>(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<uint32_t>(L, 2)) != 0);
|
|
return 1;
|
|
}
|
|
LUA_FUNCTION(IsHasProperty) {
|
|
check_param_count(L, 2);
|
|
auto tflag1 = lua_get<uint32_t>(L, 2);
|
|
auto tflag2 = lua_get<uint32_t, 0>(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<uint32_t>(L, 2)) != 0);
|
|
return 1;
|
|
}
|
|
LUA_FUNCTION(IsHasType) {
|
|
check_param_count(L, 2);
|
|
lua_pushboolean(L, (self->type & lua_get<uint16_t>(L, 2)) != 0);
|
|
return 1;
|
|
}
|
|
LUA_FUNCTION(IsActivatable) {
|
|
check_param_count(L, 2);
|
|
auto playerid = lua_get<uint8_t>(L, 2);
|
|
bool neglect_loc = lua_get<bool, false>(L, 3);
|
|
bool neglect_target = lua_get<bool, false>(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<uint8_t>(L, 2);
|
|
lua_pushboolean(L, self->check_count_limit(p));
|
|
return 1;
|
|
}
|
|
LUA_FUNCTION(UseCountLimit) {
|
|
check_param_count(L, 2);
|
|
auto p = lua_get<uint8_t>(L, 2);
|
|
auto count = lua_get<uint32_t, 1>(L, 3);
|
|
bool oath_only = lua_get<bool, false>(L, 4);
|
|
if (!oath_only || self->count_flag & EFFECT_COUNT_CODE_OATH)
|
|
while(count) {
|
|
self->dec_count(p);
|
|
--count;
|
|
}
|
|
return 0;
|
|
}
|
|
LUA_FUNCTION_EXISTING(GetLuaRef, get_lua_ref<effect>);
|
|
LUA_FUNCTION_EXISTING(FromLuaRef, from_lua_ref<effect>);
|
|
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<int>(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");
|
|
}
|