mirror of
https://github.com/edo9300/ygopro-core
synced 2026-08-25 16:23:07 -04:00
299 lines
9.9 KiB
C++
299 lines
9.9 KiB
C++
/*
|
|
* Copyright (c) 2019, Dylam De La Torre, (DyXel)
|
|
* Copyright (c) 2019-2025, Edoardo Lolletti (edo9300) <edoardo762@gmail.com>
|
|
*
|
|
* SPDX-License-Identifier: AGPL-3.0-or-later
|
|
*/
|
|
#include <cstring> //std::memcpy
|
|
#include <new> //std::nothrow
|
|
#include <vector>
|
|
#include "ocgapi.h"
|
|
#include "interpreter.h"
|
|
#include "duel.h"
|
|
#include "field.h"
|
|
#include "effect.h"
|
|
|
|
OCGAPI void OCG_GetVersion(int* major, int* minor) {
|
|
if(major)
|
|
*major = OCG_VERSION_MAJOR;
|
|
if(minor)
|
|
*minor = OCG_VERSION_MINOR;
|
|
}
|
|
|
|
OCGAPI int OCG_CreateDuel(OCG_Duel* out_ocg_duel, const OCG_DuelOptions* options_ptr) {
|
|
if(out_ocg_duel == nullptr)
|
|
return OCG_DUEL_CREATION_NO_OUTPUT;
|
|
auto options = *options_ptr;
|
|
if(options.cardReader == nullptr) {
|
|
*out_ocg_duel = nullptr;
|
|
return OCG_DUEL_CREATION_NULL_DATA_READER;
|
|
}
|
|
if(options.scriptReader == nullptr) {
|
|
*out_ocg_duel = nullptr;
|
|
return OCG_DUEL_CREATION_NULL_SCRIPT_READER;
|
|
}
|
|
if(options.logHandler == nullptr) {
|
|
options.logHandler = [](void* /*payload*/, const char* /*string*/, int /*type*/) {};
|
|
options.payload3 = nullptr;
|
|
}
|
|
if(options.cardReaderDone == nullptr) {
|
|
options.cardReaderDone = [](void* /*payload*/, OCG_CardData* /*data*/) {};
|
|
options.payload4 = nullptr;
|
|
}
|
|
bool valid_lua_lib = true;
|
|
auto* duelPtr = new (std::nothrow) duel(options, valid_lua_lib);
|
|
if(duelPtr == nullptr)
|
|
return OCG_DUEL_CREATION_NOT_CREATED;
|
|
if(!valid_lua_lib) {
|
|
delete duelPtr;
|
|
return OCG_DUEL_CREATION_NOT_CREATED;
|
|
}
|
|
*out_ocg_duel = static_cast<OCG_Duel>(duelPtr);
|
|
return OCG_DUEL_CREATION_SUCCESS;
|
|
}
|
|
|
|
OCGAPI void OCG_DestroyDuel(OCG_Duel ocg_duel) {
|
|
if(ocg_duel)
|
|
delete static_cast<duel*>(ocg_duel);
|
|
}
|
|
|
|
OCGAPI void OCG_DuelNewCard(OCG_Duel ocg_duel, const OCG_NewCardInfo* info_ptr) {
|
|
auto* pduel = static_cast<duel*>(ocg_duel);
|
|
auto& game_field = *(pduel->game_field);
|
|
const auto& info = *info_ptr;
|
|
if(bit::popcnt(info.loc) > 1)
|
|
return;
|
|
auto duelist = info.duelist;
|
|
if(duelist == 0) {
|
|
if(game_field.is_location_useable(info.con, info.loc, info.seq)) {
|
|
card* pcard = pduel->new_card(info.code);
|
|
pcard->owner = info.team;
|
|
game_field.add_card(info.con, pcard, (uint8_t)info.loc, (uint8_t)info.seq);
|
|
pcard->current.position = info.pos;
|
|
if(!(info.loc & LOCATION_ONFIELD) || (info.pos & POS_FACEUP)) {
|
|
pcard->enable_field_effect(true);
|
|
game_field.adjust_instant();
|
|
}
|
|
if(info.loc & LOCATION_ONFIELD) {
|
|
if(info.loc == LOCATION_MZONE)
|
|
pcard->set_status(STATUS_PROC_COMPLETE, TRUE);
|
|
}
|
|
}
|
|
} else {
|
|
if(info.team > 1 || !(info.loc & (LOCATION_DECK | LOCATION_EXTRA)))
|
|
return;
|
|
card* pcard = pduel->new_card(info.code);
|
|
auto& player = game_field.player[info.team];
|
|
if(duelist > player.extra_lists_main.size()) {
|
|
player.extra_lists_main.resize(duelist);
|
|
player.extra_lists_extra.resize(duelist);
|
|
player.extra_lists_hand.resize(duelist);
|
|
player.extra_extra_p_count.resize(duelist);
|
|
}
|
|
--duelist;
|
|
pcard->current.location = static_cast<uint8_t>(info.loc);
|
|
pcard->owner = info.team;
|
|
pcard->current.controler = info.team;
|
|
pcard->current.position = POS_FACEDOWN_DEFENSE;
|
|
auto& list = (info.loc == LOCATION_DECK) ? player.extra_lists_main[duelist] : player.extra_lists_extra[duelist];
|
|
list.push_back(pcard);
|
|
pcard->current.sequence = static_cast<uint32_t>(list.size() - 1);
|
|
}
|
|
}
|
|
|
|
OCGAPI void OCG_StartDuel(OCG_Duel ocg_duel) {
|
|
auto* pduel = static_cast<duel*>(ocg_duel);
|
|
pduel->game_field->emplace_process<Processors::Startup>();
|
|
}
|
|
|
|
OCGAPI int OCG_DuelProcess(OCG_Duel ocg_duel) {
|
|
auto* pduel = static_cast<duel*>(ocg_duel);
|
|
pduel->buff.clear();
|
|
auto flag = OCG_DUEL_STATUS_END;
|
|
do {
|
|
flag = pduel->game_field->process();
|
|
pduel->generate_buffer();
|
|
} while(pduel->buff.size() == 0 && flag == OCG_DUEL_STATUS_CONTINUE);
|
|
return flag;
|
|
}
|
|
|
|
OCGAPI void* OCG_DuelGetMessage(OCG_Duel ocg_duel, uint32_t* length) {
|
|
auto* pduel = static_cast<duel*>(ocg_duel);
|
|
pduel->generate_buffer();
|
|
if(length)
|
|
*length = static_cast<uint32_t>(pduel->buff.size());
|
|
return pduel->buff.data();
|
|
}
|
|
|
|
OCGAPI void OCG_DuelSetResponse(OCG_Duel ocg_duel, const void* buffer, uint32_t length) {
|
|
auto* pduel = static_cast<duel*>(ocg_duel);
|
|
pduel->set_response(buffer, length);
|
|
}
|
|
|
|
OCGAPI int OCG_LoadScript(OCG_Duel ocg_duel, const char* buffer, uint32_t length, const char* name) {
|
|
auto* pduel = static_cast<duel*>(ocg_duel);
|
|
return pduel->lua->load_script(buffer, length, name);
|
|
}
|
|
|
|
OCGAPI uint32_t OCG_DuelQueryCount(OCG_Duel ocg_duel, uint8_t team, uint32_t loc) {
|
|
auto* pduel = static_cast<duel*>(ocg_duel);
|
|
if(team > 1)
|
|
return 0;
|
|
if(bit::popcnt(loc) != 1)
|
|
return 0;
|
|
auto& player = pduel->game_field->player[team];
|
|
if(loc == LOCATION_HAND)
|
|
return static_cast<uint32_t>(player.list_hand.size());
|
|
if(loc == LOCATION_GRAVE)
|
|
return static_cast<uint32_t>(player.list_grave.size());
|
|
if (loc == LOCATION_REMOVED)
|
|
return static_cast<uint32_t>(player.list_remove.size());
|
|
if (loc == LOCATION_EXTRA)
|
|
return static_cast<uint32_t>(player.list_extra.size());
|
|
if (loc == LOCATION_DECK)
|
|
return static_cast<uint32_t>(player.list_main.size());
|
|
uint32_t count = 0;
|
|
if(loc == LOCATION_MZONE) {
|
|
for(auto& pcard : player.list_mzone)
|
|
if(pcard) ++count;
|
|
}
|
|
if(loc == LOCATION_SZONE) {
|
|
for(auto& pcard : player.list_szone)
|
|
if(pcard) ++count;
|
|
}
|
|
return count;
|
|
}
|
|
template<typename T>
|
|
void insert_value_int(std::vector<uint8_t>& vec, T val) {
|
|
const auto vec_size = vec.size();
|
|
const auto val_size = sizeof(T);
|
|
vec.resize(vec_size + val_size);
|
|
std::memcpy(&vec[vec_size], &val, val_size);
|
|
}
|
|
template<typename T, typename T2>
|
|
ForceInline void insert_value(std::vector<uint8_t>& vec, T2 val) {
|
|
insert_value_int<T>(vec, static_cast<T>(val));
|
|
}
|
|
|
|
OCGAPI void* OCG_DuelQuery(OCG_Duel ocg_duel, uint32_t* length, const OCG_QueryInfo* info_ptr) {
|
|
const auto& info = *info_ptr;
|
|
auto* pduel = static_cast<duel*>(ocg_duel);
|
|
if(bit::popcnt(info.loc & ~LOCATION_OVERLAY) != 1)
|
|
return 0;
|
|
pduel->query_buffer.clear();
|
|
card* pcard = nullptr;
|
|
if(info.loc & LOCATION_OVERLAY) {
|
|
auto olcard = pduel->game_field->get_field_card(info.con, (info.loc & LOCATION_OVERLAY), info.seq);
|
|
if(olcard && olcard->xyz_materials.size() > info.overlay_seq) {
|
|
pcard = olcard->xyz_materials[info.overlay_seq];
|
|
}
|
|
} else {
|
|
pcard = pduel->game_field->get_field_card(info.con, info.loc, info.seq);
|
|
}
|
|
if(pcard == nullptr) {
|
|
if(length)
|
|
*length = 0;
|
|
return nullptr;
|
|
} else {
|
|
pcard->get_infos(info.flags);
|
|
}
|
|
if(length)
|
|
*length = static_cast<uint32_t>(pduel->query_buffer.size());
|
|
return pduel->query_buffer.data();
|
|
}
|
|
|
|
OCGAPI void* OCG_DuelQueryLocation(OCG_Duel ocg_duel, uint32_t* length, const OCG_QueryInfo* info_ptr) {
|
|
const auto& info = *info_ptr;
|
|
auto* pduel = static_cast<duel*>(ocg_duel);
|
|
auto& buffer = pduel->query_buffer;
|
|
auto populate = [&flags = info.flags, &buffer](const card_vector& list) {
|
|
for(auto& pcard : list) {
|
|
if(pcard == nullptr) {
|
|
insert_value<int16_t>(buffer, 0);
|
|
} else {
|
|
pcard->get_infos(flags);
|
|
}
|
|
}
|
|
};
|
|
buffer.clear();
|
|
if(info.con <= 1u && bit::popcnt(info.loc & ~LOCATION_OVERLAY) == 1) {
|
|
if(info.loc & LOCATION_OVERLAY) {
|
|
insert_value<int16_t>(buffer, 0);
|
|
} else {
|
|
auto& player = pduel->game_field->player[info.con];
|
|
if(info.loc == LOCATION_MZONE)
|
|
populate(player.list_mzone);
|
|
else if(info.loc == LOCATION_SZONE)
|
|
populate(player.list_szone);
|
|
else if(info.loc == LOCATION_HAND)
|
|
populate(player.list_hand);
|
|
else if(info.loc == LOCATION_GRAVE)
|
|
populate(player.list_grave);
|
|
else if(info.loc == LOCATION_REMOVED)
|
|
populate(player.list_remove);
|
|
else if(info.loc == LOCATION_EXTRA)
|
|
populate(player.list_extra);
|
|
else if(info.loc == LOCATION_DECK)
|
|
populate(player.list_main);
|
|
}
|
|
std::vector<uint8_t> tmp_vector;
|
|
insert_value<uint32_t>(tmp_vector, buffer.size());
|
|
buffer.insert(buffer.begin(), tmp_vector.begin(), tmp_vector.end());
|
|
}
|
|
if(length)
|
|
*length = static_cast<uint32_t>(buffer.size());
|
|
return buffer.data();
|
|
}
|
|
|
|
OCGAPI void* OCG_DuelQueryField(OCG_Duel ocg_duel, uint32_t* length) {
|
|
auto* pduel = static_cast<duel*>(ocg_duel);
|
|
auto& query = pduel->query_buffer;
|
|
query.clear();
|
|
//insert_value<int8_t>(query, MSG_RELOAD_FIELD);
|
|
insert_value<uint32_t>(query, pduel->game_field->core.duel_options);
|
|
for(int playerid = 0; playerid < 2; ++playerid) {
|
|
auto& player = pduel->game_field->player[playerid];
|
|
insert_value<uint32_t>(query, player.lp);
|
|
for(auto& pcard : player.list_mzone) {
|
|
if(pcard) {
|
|
insert_value<uint8_t>(query, 1);
|
|
insert_value<uint8_t>(query, pcard->current.position);
|
|
insert_value<uint32_t>(query, pcard->xyz_materials.size());
|
|
} else {
|
|
insert_value<uint8_t>(query, 0);
|
|
}
|
|
}
|
|
for(auto& pcard : player.list_szone) {
|
|
if(pcard) {
|
|
insert_value<uint8_t>(query, 1);
|
|
insert_value<uint8_t>(query, pcard->current.position);
|
|
insert_value<uint32_t>(query, pcard->xyz_materials.size());
|
|
} else {
|
|
insert_value<uint8_t>(query, 0);
|
|
}
|
|
}
|
|
insert_value<uint32_t>(query, player.list_main.size());
|
|
insert_value<uint32_t>(query, player.list_hand.size());
|
|
insert_value<uint32_t>(query, player.list_grave.size());
|
|
insert_value<uint32_t>(query, player.list_remove.size());
|
|
insert_value<uint32_t>(query, player.list_extra.size());
|
|
insert_value<uint32_t>(query, player.extra_p_count);
|
|
}
|
|
insert_value<uint32_t>(query, pduel->game_field->core.current_chain.size());
|
|
for(const auto& ch : pduel->game_field->core.current_chain) {
|
|
effect* peffect = ch.triggering_effect;
|
|
insert_value<uint32_t>(query, peffect->get_handler()->data.code);
|
|
loc_info info = peffect->get_handler()->get_info_location();
|
|
insert_value<uint8_t>(query, info.controler);
|
|
insert_value<uint8_t>(query, info.location);
|
|
insert_value<uint32_t>(query, info.sequence);
|
|
insert_value<uint32_t>(query, info.position);
|
|
insert_value<uint8_t>(query, ch.triggering_controler);
|
|
insert_value<uint8_t>(query, static_cast<uint8_t>(ch.triggering_location));
|
|
insert_value<uint32_t>(query, ch.triggering_sequence);
|
|
insert_value<uint64_t>(query, peffect->description);
|
|
}
|
|
if(length)
|
|
*length = static_cast<uint32_t>(query.size());
|
|
return query.data();
|
|
}
|