/* * Copyright (c) 2010-2015, Argon Sun (Fluorohydride) * Copyright (c) 2017-2025, Edoardo Lolletti (edo9300) * * SPDX-License-Identifier: AGPL-3.0-or-later */ #include //std::find, std::remove, std::includes, std::set_intersection #include //std::advance, std::inserter #include #include #include //std::move, std::swap #include "bit.h" #include "card.h" #include "duel.h" #include "field.h" #include "group.h" #include "scriptlib.h" #define LUA_MODULE Group #define LUA_CLASS group #include "function_array_helper.h" namespace { namespace LUA_NAMESPACE { using namespace scriptlib; void assert_readonly_group(lua_State* L, group* pgroup) { if(!pgroup->is_readonly) return; lua_error(L, "attempt to modify a read only group"); } LUA_STATIC_FUNCTION(CreateGroup) { auto pgroup = pduel->new_group(); lua_iterate_table_or_stack(L, 1, lua_gettop(L), [L, &container = pgroup->container] { if(!lua_isnil(L, -1)) { auto pcard = lua_get(L, -1); container.insert(pcard); } }); interpreter::pushobject(L, pgroup); return 1; } LUA_FUNCTION_ALIAS(FromCards); LUA_FUNCTION(Clone) { auto newgroup = pduel->new_group(self); interpreter::pushobject(L, newgroup); return 1; } LUA_FUNCTION(DeleteGroup) { return 0; } LUA_FUNCTION(KeepAlive) { interpreter::pushobject(L, self); return 1; } LUA_FUNCTION(Clear) { assert_readonly_group(L, self); self->is_iterator_dirty = true; self->container.clear(); interpreter::pushobject(L, self); return 1; } LUA_FUNCTION(AddCard) { check_param_count(L, 2); assert_readonly_group(L, self); self->is_iterator_dirty = true; if(auto [pcard, pgroup] = lua_get_card_or_group(L, 2); pcard) self->container.insert(pcard); else self->container.insert(pgroup->container.begin(), pgroup->container.end()); interpreter::pushobject(L, self); return 1; } LUA_FUNCTION_ALIAS(Merge); LUA_FUNCTION(RemoveCard) { check_param_count(L, 2); assert_readonly_group(L, self); self->is_iterator_dirty = true; if(auto [pcard, pgroup] = lua_get_card_or_group(L, 2); pcard) self->container.erase(pcard); else { if(self == pgroup) lua_error(L, "Attempting to remove a group from itself"); for(auto& _pcard : pgroup->container) self->container.erase(_pcard); } interpreter::pushobject(L, self); return 1; } LUA_FUNCTION_ALIAS(Sub); LUA_FUNCTION(GetNext) { if(self->is_iterator_dirty) lua_error(L, "Called Group.GetNext without first calling Group.GetFirst"); if(self->it == self->container.end() || (++self->it) == self->container.end()) lua_pushnil(L); else interpreter::pushobject(L, *self->it); return 1; } LUA_FUNCTION(GetFirst) { self->is_iterator_dirty = false; if(self->it = self->container.begin(); self->it != self->container.end()) interpreter::pushobject(L, *self->it); else lua_pushnil(L); return 1; } LUA_FUNCTION(TakeatPos) { check_param_count(L, 2); auto pos = lua_get(L, 2); if(pos >= self->container.size()) lua_pushnil(L); else { auto cit = self->container.begin(); std::advance(cit, pos); interpreter::pushobject(L, *cit); } return 1; } LUA_FUNCTION(GetCount) { lua_pushinteger(L, self->container.size()); return 1; } LUA_FUNCTION(Filter) { check_param_count(L, 3); const auto findex = lua_get(L, 2); card_set cset(self->container); if(auto [pexception, pexgroup] = lua_get_card_or_group(L, 3); pexception) { cset.erase(pexception); } else if(pexgroup) { for(auto& pcard : pexgroup->container) cset.erase(pcard); } auto new_group = pduel->new_group(); uint32_t extraargs = lua_gettop(L) - 3; for(auto& pcard : cset) { if(pduel->lua->check_matching(pcard, findex, extraargs)) { new_group->container.insert(pcard); } } interpreter::pushobject(L, new_group); return 1; } LUA_FUNCTION(Match) { check_param_count(L, 3); const auto findex = lua_get(L, 2); assert_readonly_group(L, self); self->is_iterator_dirty = true; uint32_t extraargs = lua_gettop(L) - 3; auto& cset = self->container; if(auto [pexception, pexgroup] = lua_get_card_or_group(L, 3); pexception) { for(auto cit = cset.begin(), cend = cset.end(); cit != cend; ) { auto rm = cit++; auto* pcard = *rm; if(pcard == pexception || !pduel->lua->check_matching(pcard, findex, extraargs)) cset.erase(rm); } } else if(pexgroup) { auto should_remove = [pexbegin = pexgroup->container.cbegin(), pexend = pexgroup->container.cend()](card* pcard) mutable { if(pexbegin == pexend) return false; if(*pexbegin == pcard) { ++pexbegin; return true; } return false; }; for(auto cit = cset.begin(), cend = cset.end(); cit != cend; ) { auto rm = cit++; auto* pcard = *rm; if(should_remove(pcard) || !pduel->lua->check_matching(pcard, findex, extraargs)) cset.erase(rm); } } else { for(auto cit = cset.begin(), cend = cset.end(); cit != cend; ) { auto rm = cit++; auto* pcard = *rm; if(!pduel->lua->check_matching(pcard, findex, extraargs)) cset.erase(rm); } } interpreter::pushobject(L, self); return 1; } LUA_FUNCTION(FilterCount) { check_param_count(L, 3); const auto findex = lua_get(L, 2); card_set cset(self->container); if(auto [pexception, pexgroup] = lua_get_card_or_group(L, 3); pexception) { cset.erase(pexception); } else if(pexgroup) { for(auto& pcard : pexgroup->container) cset.erase(pcard); } uint32_t extraargs = lua_gettop(L) - 3; uint32_t count = 0; for (auto& pcard : cset) { if(pduel->lua->check_matching(pcard, findex, extraargs)) ++count; } lua_pushinteger(L, count); return 1; } LUA_FUNCTION(FilterSelect) { check_action_permission(L); check_param_count(L, 6); const auto findex = lua_get(L, 3); card_set cset(self->container); bool cancelable = false; uint8_t lastarg = 6; if(lua_isboolean(L, lastarg)) { cancelable = lua_get(L, lastarg); ++lastarg; } if(auto [pexception, pexgroup] = lua_get_card_or_group(L, lastarg); pexception) { cset.erase(pexception); } else if(pexgroup) { for(auto& pcard : pexgroup->container) cset.erase(pcard); } auto playerid = lua_get(L, 2); if(playerid != 0 && playerid != 1) return 0; auto min = lua_get(L, 4); auto max = lua_get(L, 5); uint32_t extraargs = lua_gettop(L) - lastarg; pduel->game_field->core.select_cards.clear(); for(auto& pcard : cset) { if(pduel->lua->check_matching(pcard, findex, extraargs)) pduel->game_field->core.select_cards.push_back(pcard); } pduel->game_field->emplace_process(playerid, cancelable, min, max); return push_return_cards(L, cancelable); } LUA_FUNCTION(Select) { check_action_permission(L); check_param_count(L, 5); card_set cset(self->container); bool cancelable = false; uint8_t lastarg = 5; if(lua_isboolean(L, lastarg)) { cancelable = lua_get(L, lastarg); ++lastarg; } if(auto [pexception, pexgroup] = lua_get_card_or_group(L, lastarg); pexception) { cset.erase(pexception); } else if(pexgroup) { for(auto& pcard : pexgroup->container) cset.erase(pcard); } auto playerid = lua_get(L, 2); if(playerid != 0 && playerid != 1) return 0; auto min = lua_get(L, 3); auto max = lua_get(L, 4); pduel->game_field->core.select_cards.assign(cset.begin(), cset.end()); pduel->game_field->emplace_process(playerid, cancelable, min, max); return push_return_cards(L, cancelable); } LUA_FUNCTION(SelectUnselect) { check_action_permission(L); check_param_count(L, 3); auto pgroup2 = lua_get(L, 2); auto playerid = lua_get(L, 3); if(playerid != 0 && playerid != 1) return 0; if(pgroup2) { auto first1 = self->container.begin(); auto last1 = self->container.end(); auto first2 = pgroup2->container.begin(); auto last2 = pgroup2->container.end(); while(first1 != last1 && first2 != last2) { if((*first1)->cardid < (*first2)->cardid) { ++first1; } else { if(!((*first2)->cardid < (*first1)->cardid)) { return 0; } ++first2; } } } bool finishable = lua_get(L, 4); bool cancelable = lua_get(L, 5); uint16_t min = lua_get(L, 6); uint16_t max = lua_get(L, 7); if(min > max) min = max; pduel->game_field->core.select_cards.assign(self->container.begin(), self->container.end()); if(pgroup2) pduel->game_field->core.unselect_cards.assign(pgroup2->container.begin(), pgroup2->container.end()); else pduel->game_field->core.unselect_cards.clear(); pduel->game_field->emplace_process(playerid, cancelable, min, max, finishable); return yieldk({ if(pduel->game_field->return_cards.canceled) lua_pushnil(L); else interpreter::pushobject(L, pduel->game_field->return_cards.list[0]); return 1; }); } LUA_FUNCTION(RandomSelect) { check_param_count(L, 3); auto playerid = lua_get(L, 2); size_t count = lua_get(L, 3); auto newgroup = pduel->new_group(); if(count > self->container.size()) count = self->container.size(); if(count == 0) { interpreter::pushobject(L, newgroup); return 1; } if(count == self->container.size()) newgroup->container = self->container; else { while(newgroup->container.size() < count) { int32_t i = pduel->get_next_integer(0, (int32_t)self->container.size() - 1); auto cit = self->container.begin(); std::advance(cit, i); newgroup->container.insert(*cit); } } auto message = pduel->new_message(MSG_RANDOM_SELECTED); message->write(playerid); message->write(count); for(auto& pcard : newgroup->container) { message->write(pcard->get_info_location()); } interpreter::pushobject(L, newgroup); return 1; } LUA_FUNCTION(IsExists) { check_param_count(L, 4); const auto findex = lua_get(L, 2); card_set cset(self->container); if(auto [pexception, pexgroup] = lua_get_card_or_group(L, 4); pexception) { cset.erase(pexception); } else if(pexgroup) { for(auto& pcard : pexgroup->container) cset.erase(pcard); } auto count = lua_get(L, 3); uint32_t extraargs = lua_gettop(L) - 4; uint32_t fcount = 0; for(auto& pcard : cset) { if(pduel->lua->check_matching(pcard, findex, extraargs)) { ++fcount; if(fcount >= count) break; } } lua_pushboolean(L, fcount >= count); return 1; } LUA_FUNCTION(CheckWithSumEqual) { check_param_count(L, 5); const auto findex = lua_get(L, 2); auto acc = lua_get(L, 3); auto min = lua_get(L, 4); auto max = lua_get(L, 5); if(min < 0) min = 0; if(max < min) max = min; int32_t extraargs = lua_gettop(L) - 5; card_vector cv(pduel->game_field->core.must_select_cards); int32_t mcount = static_cast(cv.size()); const auto beginit = pduel->game_field->core.must_select_cards.begin(); const auto endit = pduel->game_field->core.must_select_cards.end(); for(auto& pcard : self->container) { if(std::find(beginit, endit, pcard) == endit) cv.push_back(pcard); } pduel->game_field->core.must_select_cards.clear(); for(auto& pcard : cv) { if(pcard->sum_param = pduel->lua->get_operation_value(pcard, findex, extraargs); pcard->sum_param == 0) { lua_error(L, "Group contains a card for which the value function returned 0."); } } int32_t should_continue = TRUE; lua_pushboolean(L, field::check_with_sum_limit_m(cv, acc, 0, min, max, mcount, &should_continue)); lua_pushboolean(L, should_continue); return 2; } LUA_FUNCTION(SelectWithSumEqual) { check_action_permission(L); check_param_count(L, 6); const auto findex = lua_get(L, 3); auto playerid = lua_get(L, 2); if(playerid != 0 && playerid != 1) return 0; auto acc = lua_get(L, 4); auto min = lua_get(L, 5); auto max = lua_get(L, 6); if(min < 0) min = 0; if(max < min) max = min; int32_t extraargs = lua_gettop(L) - 6; pduel->game_field->core.select_cards.assign(self->container.begin(), self->container.end()); for(auto& pcard : pduel->game_field->core.must_select_cards) { auto it = std::remove(pduel->game_field->core.select_cards.begin(), pduel->game_field->core.select_cards.end(), pcard); pduel->game_field->core.select_cards.erase(it, pduel->game_field->core.select_cards.end()); } card_vector cv(pduel->game_field->core.must_select_cards); int32_t mcount = static_cast(cv.size()); cv.insert(cv.end(), pduel->game_field->core.select_cards.begin(), pduel->game_field->core.select_cards.end()); for(auto& pcard : cv) { if(pcard->sum_param = pduel->lua->get_operation_value(pcard, findex, extraargs); pcard->sum_param == 0) { lua_error(L, "Group contains a card for which the value function returned 0."); } } if(!field::check_with_sum_limit_m(cv, acc, 0, min, max, mcount, nullptr)) { pduel->game_field->core.must_select_cards.clear(); auto empty_group = pduel->new_group(); interpreter::pushobject(L, empty_group); return 1; } pduel->game_field->emplace_process(playerid, acc, min, max); return yieldk({ auto pgroup = pduel->new_group(pduel->game_field->return_cards.list); pduel->game_field->core.must_select_cards.clear(); interpreter::pushobject(L, pgroup); return 1; }); } LUA_FUNCTION(CheckWithSumGreater) { check_param_count(L, 3); const auto findex = lua_get(L, 2); auto acc = lua_get(L, 3); int32_t extraargs = lua_gettop(L) - 3; card_vector cv(pduel->game_field->core.must_select_cards); int32_t mcount = static_cast(cv.size()); const auto beginit = pduel->game_field->core.must_select_cards.begin(); const auto endit = pduel->game_field->core.must_select_cards.end(); for(auto& pcard : self->container) { if(std::find(beginit, endit, pcard) == endit) cv.push_back(pcard); } pduel->game_field->core.must_select_cards.clear(); for(auto& pcard : cv) { if(pcard->sum_param = pduel->lua->get_operation_value(pcard, findex, extraargs); pcard->sum_param == 0) { lua_error(L, "Group contains a card for which the value function returned 0."); } } int32_t should_continue = TRUE; lua_pushboolean(L, field::check_with_sum_greater_limit_m(cv, acc, 0, 0xffff, mcount, &should_continue)); lua_pushboolean(L, should_continue); return 2; } LUA_FUNCTION(SelectWithSumGreater) { check_action_permission(L); check_param_count(L, 4); const auto findex = lua_get(L, 3); auto playerid = lua_get(L, 2); if(playerid != 0 && playerid != 1) return 0; auto acc = lua_get(L, 4); int32_t extraargs = lua_gettop(L) - 4; pduel->game_field->core.select_cards.assign(self->container.begin(), self->container.end()); for(auto& pcard : pduel->game_field->core.must_select_cards) { auto it = std::remove(pduel->game_field->core.select_cards.begin(), pduel->game_field->core.select_cards.end(), pcard); pduel->game_field->core.select_cards.erase(it, pduel->game_field->core.select_cards.end()); } card_vector cv(pduel->game_field->core.must_select_cards); int32_t mcount = static_cast(cv.size()); cv.insert(cv.end(), pduel->game_field->core.select_cards.begin(), pduel->game_field->core.select_cards.end()); for(auto& pcard : cv) { if(pcard->sum_param = pduel->lua->get_operation_value(pcard, findex, extraargs); pcard->sum_param == 0) { lua_error(L, "Group contains a card for which the value function returned 0."); } } if(!field::check_with_sum_greater_limit_m(cv, acc, 0, 0xffff, mcount, nullptr)) { pduel->game_field->core.must_select_cards.clear(); auto empty_group = pduel->new_group(); interpreter::pushobject(L, empty_group); return 1; } pduel->game_field->emplace_process(playerid, acc, 0, 0); return yieldk({ auto pgroup = pduel->new_group(pduel->game_field->return_cards.list); pduel->game_field->core.must_select_cards.clear(); interpreter::pushobject(L, pgroup); return 1; }); } LUA_FUNCTION(GetMinGroup) { check_param_count(L, 2); const auto findex = lua_get(L, 2); if(self->container.size() == 0) return 0; auto newgroup = pduel->new_group(); int64_t min, op; int32_t extraargs = lua_gettop(L) - 2; auto cit = self->container.begin(); min = pduel->lua->get_operation_value(*cit, findex, extraargs); newgroup->container.insert(*cit); ++cit; for(; cit != self->container.end(); ++cit) { op = pduel->lua->get_operation_value(*cit, findex, extraargs); if(op == min) newgroup->container.insert(*cit); else if(op < min) { newgroup->container.clear(); newgroup->container.insert(*cit); min = op; } } interpreter::pushobject(L, newgroup); lua_pushinteger(L, min); return 2; } LUA_FUNCTION(GetMaxGroup) { check_param_count(L, 2); const auto findex = lua_get(L, 2); if(self->container.size() == 0) return 0; auto newgroup = pduel->new_group(); int64_t max, op; int32_t extraargs = lua_gettop(L) - 2; auto cit = self->container.begin(); max = pduel->lua->get_operation_value(*cit, findex, extraargs); newgroup->container.insert(*cit); ++cit; for(; cit != self->container.end(); ++cit) { op = pduel->lua->get_operation_value(*cit, findex, extraargs); if(op == max) newgroup->container.insert(*cit); else if(op > max) { newgroup->container.clear(); newgroup->container.insert(*cit); max = op; } } interpreter::pushobject(L, newgroup); lua_pushinteger(L, max); return 2; } LUA_FUNCTION(GetSum) { check_param_count(L, 2); const auto findex = lua_get(L, 2); int32_t extraargs = lua_gettop(L) - 2; int64_t sum = 0; for(auto& pcard : self->container) { sum += pduel->lua->get_operation_value(pcard, findex, extraargs); } lua_pushinteger(L, sum); return 1; } LUA_FUNCTION(GetBitwiseAnd) { check_param_count(L, 2); const auto findex = lua_get(L, 2); int32_t extraargs = lua_gettop(L) - 2; uint64_t total = 0; for(auto& pcard : self->container) { total &= static_cast(pduel->lua->get_operation_value(pcard, findex, extraargs)); } lua_pushinteger(L, total); return 1; } LUA_FUNCTION(GetBitwiseOr) { check_param_count(L, 2); const auto findex = lua_get(L, 2); int32_t extraargs = lua_gettop(L) - 2; uint64_t total = 0; for(auto& pcard : self->container) { total |= static_cast(pduel->lua->get_operation_value(pcard, findex, extraargs)); } lua_pushinteger(L, total); return 1; } LUA_FUNCTION(GetClass) { check_param_count(L, 2); const auto findex = lua_get(L, 2); int32_t extraargs = lua_gettop(L) - 2; std::set er; for(auto& pcard : self->container) { er.insert(pduel->lua->get_operation_value(pcard, findex, extraargs)); } lua_createtable(L, static_cast(er.size()), 0); int i = 1; for(auto& val : er) { lua_pushinteger(L, i++); lua_pushinteger(L, val); lua_settable(L, -3); } return 1; } LUA_FUNCTION(GetClassCount) { check_param_count(L, 2); const auto findex = lua_get(L, 2); int32_t extraargs = lua_gettop(L) - 2; std::set er; for(auto& pcard : self->container) { er.insert(pduel->lua->get_operation_value(pcard, findex, extraargs)); } lua_pushinteger(L, er.size()); return 1; } LUA_FUNCTION(Remove) { check_param_count(L, 3); const auto findex = lua_get(L, 2); assert_readonly_group(L, self); self->is_iterator_dirty = true; uint32_t extraargs = lua_gettop(L) - 3; auto& cset = self->container; if(auto [pexception, pexgroup] = lua_get_card_or_group(L, 3); pexception) { for(auto cit = cset.begin(), cend = cset.end(); cit != cend; ) { auto rm = cit++; auto* pcard = *rm; if(pcard != pexception && pduel->lua->check_matching(pcard, findex, extraargs)) cset.erase(rm); } } else if(pexgroup) { auto should_keep = [pexbegin = pexgroup->container.cbegin(), pexend = pexgroup->container.cend()](card* pcard) mutable { if(pexbegin == pexend) return false; if(*pexbegin == pcard) { ++pexbegin; return true; } return false; }; for(auto cit = cset.begin(), cend = cset.end(); cit != cend; ) { auto rm = cit++; auto* pcard = *rm; if(!should_keep(pcard) && pduel->lua->check_matching(pcard, findex, extraargs)) cset.erase(rm); } } else { for(auto cit = cset.begin(), cend = cset.end(); cit != cend; ) { auto rm = cit++; auto* pcard = *rm; if(pduel->lua->check_matching(pcard, findex, extraargs)) cset.erase(rm); } } interpreter::pushobject(L, self); return 1; } std::tuple get_binary_op_group_card_parameters(lua_State* L) { auto obj1 = lua_get(L, 1); auto obj2 = lua_get(L, 2); if(!obj1 || !obj2) lua_error(L, "At least 1 parameter should be \"Group\"."); if(obj1->lua_type != LuaParam::GROUP) std::swap(obj1, obj2); if(obj1->lua_type != LuaParam::GROUP) lua_error(L, "At least 1 parameter should be \"Group\"."); switch(obj2->lua_type) { case LuaParam::GROUP: return { static_cast(obj1), static_cast(obj2), nullptr}; case LuaParam::CARD: return { static_cast(obj1), nullptr, static_cast(obj2) }; default: lua_error(L, "A parameter isn't \"Group\" nor \"Card\"."); } } LUA_STATIC_FUNCTION(__band) { check_param_count(L, 2); auto [pgroup1, pgroup2, pcard] = get_binary_op_group_card_parameters(L); card_set cset; if(pcard) { if(pgroup1->has_card(pcard)) { cset.insert(pcard); } } else { std::set_intersection(pgroup1->container.cbegin(), pgroup1->container.cend(), pgroup2->container.cbegin(), pgroup2->container.cend(), std::inserter(cset, cset.begin()), card_sort()); } interpreter::pushobject(L, pduel->new_group(std::move(cset))); return 1; } LUA_STATIC_FUNCTION(__add) { check_param_count(L, 2); auto [pgroup1, pgroup2, pcard] = get_binary_op_group_card_parameters(L); auto newgroup = pduel->new_group(pgroup1); if(pcard) { newgroup->container.insert(pcard); } else { newgroup->container.insert(pgroup2->container.begin(), pgroup2->container.end()); } interpreter::pushobject(L, newgroup); return 1; } LUA_FUNCTION(__sub) { check_param_count(L, 2); auto [pcard, pgroup] = lua_get_card_or_group(L, 2); auto newgroup = pduel->new_group(self); if(pgroup) { for(auto& _pcard : pgroup->container) newgroup->container.erase(_pcard); } else newgroup->container.erase(pcard); interpreter::pushobject(L, newgroup); return 1; } LUA_FUNCTION(__len) { lua_pushinteger(L, self->container.size()); return 1; } LUA_FUNCTION(__eq) { check_param_count(L, 2); auto sgroup = lua_get(L, 2); lua_pushboolean(L, self->container.size() == sgroup->container.size()); return 1; } LUA_FUNCTION(Equal) { check_param_count(L, 2); auto sgroup = lua_get(L, 2); lua_pushboolean(L, self->container == sgroup->container); return 1; } LUA_FUNCTION(__lt) { check_param_count(L, 2); auto sgroup = lua_get(L, 2); lua_pushboolean(L, self->container.size() < sgroup->container.size()); return 1; } LUA_FUNCTION(__le) { check_param_count(L, 2); auto sgroup = lua_get(L, 2); lua_pushboolean(L, self->container.size() <= sgroup->container.size()); return 1; } LUA_FUNCTION(__gc) { pduel->delete_group(self); return 1; } LUA_FUNCTION(IsContains) { check_param_count(L, 2); auto pcard = lua_get(L, 2); lua_pushboolean(L, self->has_card(pcard)); return 1; } LUA_FUNCTION(SearchCard) { check_param_count(L, 2); const auto findex = lua_get(L, 2); uint32_t extraargs = lua_gettop(L) - 2; for(auto& pcard : self->container) if(pduel->lua->check_matching(pcard, findex, extraargs)) { interpreter::pushobject(L, pcard); return 1; } return 0; } LUA_FUNCTION(Split) { check_param_count(L, 3); const auto findex = lua_get(L, 2); card_set cset(self->container); card_set notmatching; if(auto [pexception, pexgroup] = lua_get_card_or_group(L, 3); pexception) { cset.erase(pexception); notmatching.insert(pexception); } else if(pexgroup) { for(auto& pcard : pexgroup->container) { cset.erase(pcard); notmatching.insert(pcard); } } uint32_t extraargs = lua_gettop(L) - 3; for(auto it = cset.begin(); it != cset.end();) { auto pcard = *it; if(pduel->lua->check_matching(pcard, findex, extraargs)) { ++it; } else { notmatching.insert(pcard); it = cset.erase(it); } } interpreter::pushobject(L, pduel->new_group(std::move(cset))); interpreter::pushobject(L, pduel->new_group(std::move(notmatching))); return 2; } LUA_FUNCTION(Includes) { check_param_count(L, 2); auto pgroup2 = lua_get(L, 2); int res = TRUE; if(self->container.size() < pgroup2->container.size()) res = FALSE; else if(!pgroup2->container.empty()) res = std::includes(self->container.cbegin(), self->container.cend(), pgroup2->container.cbegin(), pgroup2->container.cend(), card_sort()); lua_pushboolean(L, res); return 1; } LUA_FUNCTION(GetBinClassCount) { check_param_count(L, 2); const auto findex = lua_get(L, 2); int32_t extraargs = lua_gettop(L) - 2; uint64_t er = 0; for(auto& pcard : self->container) er |= static_cast(pduel->lua->get_operation_value(pcard, findex, extraargs)); lua_pushinteger(L, bit::popcnt(er)); return 1; } LUA_FUNCTION_EXISTING(GetLuaRef, get_lua_ref); LUA_FUNCTION_EXISTING(FromLuaRef, from_lua_ref); LUA_FUNCTION_EXISTING(IsDeleted, is_deleted_object); } } void scriptlib::push_group_lib(lua_State* L) { static constexpr auto grouplib = GET_LUA_FUNCTIONS_ARRAY(); static_assert(grouplib.back().name == nullptr); lua_createtable(L, 0, static_cast(grouplib.size() - 1)); ensure_luaL_stack(luaL_setfuncs, L, grouplib.data(), 0); lua_pushstring(L, "__index"); lua_pushvalue(L, -2); lua_rawset(L, -3); lua_setglobal(L, "Group"); }