Commit 6934ed76 authored by nanahira's avatar nanahira

Merge branch 'master' of github.com:Fluorohydride/ygopro-core into develop

parents 74520eb0 cd23edd2
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
The core logic and lua script processor of YGOPro. This library can be made external of the project and used to power server technologies. It maintains a state engine that is manipulated by Lua scripts using manipulation functions it exposes. The core logic and lua script processor of YGOPro. This library can be made external of the project and used to power server technologies. It maintains a state engine that is manipulated by Lua scripts using manipulation functions it exposes.
## Compiling ## Compiling
See [ygopro wiki](https://github.com/Fluorohydride/ygopro/wiki).
### 1.) Download Fluorohydride/ygopro ### 1.) Download Fluorohydride/ygopro
Start by downloading the most parent of the source code. The team developing this project are the de facto edge and experts in our community. The most up-to-date `ocgcore` is a compiled dll version of the `Fluorohydride/ygopro/ocgcore` folders project. Start by downloading the most parent of the source code. The team developing this project are the de facto edge and experts in our community. The most up-to-date `ocgcore` is a compiled dll version of the `Fluorohydride/ygopro/ocgcore` folders project.
...@@ -11,56 +13,83 @@ Start by downloading the most parent of the source code. The team developing thi ...@@ -11,56 +13,83 @@ Start by downloading the most parent of the source code. The team developing thi
Download premake5.exe, put it in `c:\windows` or a similar folder that is globally accessible via `cmd` or PowerShell. Install Visual Studio 2022, it is the system used for the guide because other parts of the project use C# and most the development team are Windows users. Download premake5.exe, put it in `c:\windows` or a similar folder that is globally accessible via `cmd` or PowerShell. Install Visual Studio 2022, it is the system used for the guide because other parts of the project use C# and most the development team are Windows users.
### 3.) Download dependencies ### 3.) Download dependencies
Dependencies are absent from the main project. There is information on how to build them but the easiest thing to do is to download the following folders from a [soarqin/ygopro](https://github.com/soarqin/ygopro) fork and simply copy them into the `Fluorohydride/ygopro` folder.
* event
* freetype
* irrlicht
* lua * lua
* sqlite3
### 4.) Create the project files ### 4.) Create the project files
Run the following commands from the command line in the `Fluorohydride/ygopro` folder. Run the following commands from the command line in the `Fluorohydride/ygopro` folder.
` premake5 vs2022 ` `premake5 vs2022`
If you are not using Visual Studio 2022 or higher, make necessary adjustments. In the file system open `Fluorohydride/ygopro/build` folder open the `ygo` project. If you are not using Visual Studio 2022 or higher, make necessary adjustments. In the file system open `Fluorohydride/ygopro/build` folder open the `ygopro` project.
### 5.) Build the system ### 5.) Build the system
Make sure the code actually compiles. Compile them in the following order one by one: Make sure the code actually compiles. Compile them in the following order one by one:
* lua * lua
* sqlite3
* ocgcore * ocgcore
This should provide you with `ocgcore.lib` in the build output folder. `YGOCore` requires a `*.dll`; in `ocgcore` project properties change it to a dynamically linked library. Recompile, it should fail with an error indicating missing dependencies. Right click the project, add an existing file. Add `lua.lib` from the build folder to the project. It should now compile. This should provide you with `ocgcore.lib` in the build output folder. `YGOCore` requires a `*.dll`; in `ocgcore` project properties change it to a dynamically linked library. Recompile, it should fail with an error indicating missing dependencies. Right click the project, add an existing file. Add `lua.lib` from the build folder to the project. It should now compile.
## Exposed Functions ## Exposed Functions
These three function need to be provided to the core so it can get card and database information. The 3 functions need to be provided to the core so it can get card and database information.
- `void set_script_reader(script_reader f);` : Interface provided returns scripts based on number that corresponds to a lua file, send in a string. - `void set_script_reader(script_reader f);`
Interface provided returns scripts based on number that corresponds to a lua file, send in a string.
- `void set_card_reader(card_reader f);` : Interface provided function that provides database information from the `data` table of `cards.cdb`. - `void set_card_reader(card_reader f);`
Interface provided function that provides database information from the `data` table of `cards.cdb`.
- `void set_message_handler(message_handler f);` : Interface provided function that handles errors - `void set_message_handler(message_handler f);`
Interface provided function that handles error messages.
These functions create the game itself and then manipulate it. These functions create the game itself and then manipulate it.
- `ptr create_duel(uint32 seed);` : Create a the instance of the duel using a random number. - `intptr_t create_duel(uint_fast32_t seed);`
- `void start_duel(ptr pduel, int32 options);` : Starts the duel Create a the instance of the duel with a PRNG seed.
- `void end_duel(ptr pduel);` : ends the duel
- `void set_player_info(ptr pduel, int32 playerid, int32 lp, int32 startcount, int32 drawcount);` sets the duel up - `void start_duel(intptr_t pduel, int32 options);`
- `void get_log_message(ptr pduel, byte* buf);` Start the duel.
- `int32 get_message(ptr pduel, byte* buf);`
- `int32 process(ptr pduel);` : do a game tick - `void end_duel(intptr_t pduel);`
- `void new_card(ptr pduel, uint32 code, uint8 owner, uint8 playerid, uint8 location, uint8 sequence, uint8 position);` : add a card to the duel state. End the duel.
- `void new_tag_card(ptr pduel, uint32 code, uint8 owner, uint8 location);` : add a new card to the tag pool.
- `int32 query_card(ptr pduel, uint8 playerid, uint8 location, uint8 sequence, int32 query_flag, byte* buf, int32 use_cache);` : find out about a card in a specific spot. - `void set_player_info(intptr_t pduel, int32 playerid, int32 lp, int32 startcount, int32 drawcount);`
- `int32 query_field_count(ptr pduel, uint8 playerid, uint8 location);` : Get the number of cards in a specific field/zone. Set up the duel information.
- `int32 query_field_card(ptr pduel, uint8 playerid, uint8 location, int32 query_flag, byte* buf, int32 use_cache);`
- `int32 query_field_info(ptr pduel, byte* buf);` - `void get_log_message(intptr_t pduel, char* buf);`
- `void set_responsei(ptr pduel, int32 value);`
- `void set_responseb(ptr pduel, byte* buf);` - `int32 get_message(intptr_t pduel, byte* buf);`
- `int32 preload_script(ptr pduel, char* script, int32 len);`
- `int32 process(intptr_t pduel);`
Do a game tick.
- `void new_card(intptr_t pduel, uint32 code, uint8 owner, uint8 playerid, uint8 location, uint8 sequence, uint8 position);`
Add a card to the duel state.
- `void new_tag_card(intptr_t pduel, uint32 code, uint8 owner, uint8 location);`
Add a new card to the tag pool.
- `int32 query_field_card(intptr_t pduel, uint8 playerid, uint8 location, uint32 query_flag, byte* buf, int32 use_cache);`
Get a card in a specific location.
- `int32 query_field_count(intptr_t pduel, uint8 playerid, uint8 location);`
Get the number of cards in a specific location.
- `int32 query_field_card(intptr_t pduel, uint8 playerid, uint8 location, uint32 query_flag, byte* buf, int32 use_cache);`
Get all cards in some location.
- `int32 query_field_info(intptr_t pduel, byte* buf);`
- `void set_responsei(intptr_t pduel, int32 value);`
- `void set_responseb(intptr_t pduel, byte* buf);`
- `int32 preload_script(intptr_t pduel, const char* script, int32 len);`
# Lua functions # Lua functions
`interpreter.cpp` - `libcard.cpp`
- `libdebug.cpp`
- `libduel.cpp`
- `libeffect.cpp`
- `libgroup.cpp`
...@@ -23,9 +23,7 @@ const std::unordered_map<uint32, uint32> card::second_code = { ...@@ -23,9 +23,7 @@ const std::unordered_map<uint32, uint32> card::second_code = {
{CARD_HERMOS, 10000070u} {CARD_HERMOS, 10000070u}
}; };
bool card_sort::operator()(void* const & p1, void* const & p2) const { bool card_sort::operator()(card* const& c1, card* const& c2) const {
card* c1 = (card*)p1;
card* c2 = (card*)p2;
return c1->cardid < c2->cardid; return c1->cardid < c2->cardid;
} }
bool card_state::is_location(int32 loc) const { bool card_state::is_location(int32 loc) const {
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include "common.h" #include "common.h"
#include "effectset.h" #include "effectset.h"
#include "card_data.h" #include "card_data.h"
#include "sort.h"
#include <set> #include <set>
#include <map> #include <map>
#include <unordered_set> #include <unordered_set>
......
...@@ -42,9 +42,6 @@ typedef signed char int8; ...@@ -42,9 +42,6 @@ typedef signed char int8;
#ifndef NULL #ifndef NULL
#define NULL 0 #define NULL 0
#endif #endif
struct card_sort {
bool operator()(void* const & c1, void* const & c2) const;
};
#define CURRENT_RULE 5 #define CURRENT_RULE 5
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#define DUEL_H_ #define DUEL_H_
#include "common.h" #include "common.h"
#include "sort.h"
#include "mtrandom.h" #include "mtrandom.h"
#include <set> #include <set>
#include <unordered_set> #include <unordered_set>
......
...@@ -613,19 +613,19 @@ int32 field::is_location_useable(uint32 playerid, uint32 location, uint32 sequen ...@@ -613,19 +613,19 @@ int32 field::is_location_useable(uint32 playerid, uint32 location, uint32 sequen
return TRUE; return TRUE;
} }
/** /**
* Return usable count in zone of playerid's MZONE or SZONE(0~4) when uplayer moves pcard to playerid's field (can be negative). * Return usable count in `zone` when `uplayer` moves `pcard` to the MZONE or SZONE(0~4) of `playerid` (can be negative).
* for LOCATION_MZONE, "usable" means not used, not disabled, satisfying EFFECT_MUST_USE_MZONE, satisfying EFFECT_MAX_MZONE * For LOCATION_MZONE, "usable" means not used, not disabled, satisfying EFFECT_MUST_USE_MZONE, satisfying EFFECT_MAX_MZONE.
* for LOCATION_SZONE, "usable" means not used, not disabled, satisfying EFFECT_MAX_SZONE * For LOCATION_SZONE, "usable" means not used, not disabled, satisfying EFFECT_MAX_SZONE.
* *
* @param pcard the card about to move * @param pcard the card about to move
* @param playerid the target player * @param playerid target player
* @param location LOCATION_MZONE or LOCATION_SZONE * @param location LOCATION_MZONE or LOCATION_SZONE
* @param uplayer the request player, PLAYER_NONE means ignoring EFFECT_MUST_USE_MZONE of uplayer, ignoring EFFECT_MAX_MZONE, EFFECT_MAX_SZONE of playerid * @param uplayer request player, PLAYER_NONE means ignoring EFFECT_MUST_USE_MZONE of uplayer, ignoring EFFECT_MAX_MZONE, EFFECT_MAX_SZONE of playerid
* @param reason location reason * @param reason location reason
* @param zone specified zones, 0xff by default * @param zone specified zones, default: 0xff
* @param list storing unavailable or unspecified zones * @param list storing unavailable or unspecified zones
* *
* @return usable count in zone of playerid's MZONE or SZONE(0~4) (can be negative) * @return usable count in the MZONE or SZONE(0~4) of `playerid` (can be negative)
*/ */
int32 field::get_useable_count(card* pcard, uint8 playerid, uint8 location, uint8 uplayer, uint32 reason, uint32 zone, uint32* list) { int32 field::get_useable_count(card* pcard, uint8 playerid, uint8 location, uint8 uplayer, uint32 reason, uint32 zone, uint32* list) {
if(location == LOCATION_MZONE && pcard && pcard->current.location == LOCATION_EXTRA) if(location == LOCATION_MZONE && pcard && pcard->current.location == LOCATION_EXTRA)
...@@ -635,7 +635,7 @@ int32 field::get_useable_count(card* pcard, uint8 playerid, uint8 location, uint ...@@ -635,7 +635,7 @@ int32 field::get_useable_count(card* pcard, uint8 playerid, uint8 location, uint
} }
/** /**
* @param pcard the card about to move from Extra Deck (nullptr means any card in Extra Deck) * @param pcard the card about to move from Extra Deck (nullptr means any card in Extra Deck)
* @return usable count in zone of playerid's MZONE for pcard * @return usable count in the MZONE of `playerid`
*/ */
int32 field::get_useable_count_fromex(card* pcard, uint8 playerid, uint8 uplayer, uint32 zone, uint32* list) { int32 field::get_useable_count_fromex(card* pcard, uint8 playerid, uint8 uplayer, uint32 zone, uint32* list) {
bool use_temp_card = false; bool use_temp_card = false;
...@@ -654,8 +654,8 @@ int32 field::get_useable_count_fromex(card* pcard, uint8 playerid, uint8 uplayer ...@@ -654,8 +654,8 @@ int32 field::get_useable_count_fromex(card* pcard, uint8 playerid, uint8 uplayer
return useable_count; return useable_count;
} }
/** /**
* @return the number of available grids in zone of playerid's MZONE for pcard sp_summoned by playerid * @return the number of available slots in `zone` when `pcard` is sp_summoned to the MZONE of `playerid`
* for LOCATION_MZONE, "available" means not used, not disabled, satisfying EFFECT_MUST_USE_MZONE * For LOCATION_MZONE, "available" means not used, not disabled, satisfying EFFECT_MUST_USE_MZONE.
*/ */
int32 field::get_spsummonable_count(card* pcard, uint8 playerid, uint32 zone, uint32* list) { int32 field::get_spsummonable_count(card* pcard, uint8 playerid, uint32 zone, uint32* list) {
if(pcard->current.location == LOCATION_EXTRA) if(pcard->current.location == LOCATION_EXTRA)
...@@ -665,7 +665,7 @@ int32 field::get_spsummonable_count(card* pcard, uint8 playerid, uint32 zone, ui ...@@ -665,7 +665,7 @@ int32 field::get_spsummonable_count(card* pcard, uint8 playerid, uint32 zone, ui
} }
/** /**
* @param pcard the card about to move from Extra Deck (nullptr means any card in Extra Deck) * @param pcard the card about to move from Extra Deck (nullptr means any card in Extra Deck)
* @return the number of available grids in zone of playerid's MZONE for pcard * @return the number of available slots in `zone` when `pcard` is sp_summoned to the MZONE of `playerid` by `uplayer`
*/ */
int32 field::get_spsummonable_count_fromex(card* pcard, uint8 playerid, uint8 uplayer, uint32 zone, uint32* list) { int32 field::get_spsummonable_count_fromex(card* pcard, uint8 playerid, uint8 uplayer, uint32 zone, uint32* list) {
bool use_temp_card = false; bool use_temp_card = false;
...@@ -684,7 +684,7 @@ int32 field::get_spsummonable_count_fromex(card* pcard, uint8 playerid, uint8 up ...@@ -684,7 +684,7 @@ int32 field::get_spsummonable_count_fromex(card* pcard, uint8 playerid, uint8 up
return spsummonable_count; return spsummonable_count;
} }
/** /**
* @return usable count in zone of Main MZONE or SZONE(0~4) * @return usable count in `zone` of Main MZONE or SZONE(0~4)
*/ */
int32 field::get_useable_count_other(card* pcard, uint8 playerid, uint8 location, uint8 uplayer, uint32 reason, uint32 zone, uint32* list) { int32 field::get_useable_count_other(card* pcard, uint8 playerid, uint8 location, uint8 uplayer, uint32 reason, uint32 zone, uint32* list) {
int32 count = get_tofield_count(pcard, playerid, location, uplayer, reason, zone, list); int32 count = get_tofield_count(pcard, playerid, location, uplayer, reason, zone, list);
...@@ -698,7 +698,7 @@ int32 field::get_useable_count_other(card* pcard, uint8 playerid, uint8 location ...@@ -698,7 +698,7 @@ int32 field::get_useable_count_other(card* pcard, uint8 playerid, uint8 location
return count; return count;
} }
/** /**
* @return the number of available grids in zone of Main MZONE or SZONE(0~4) * @return the number of available slots in `zone` of Main MZONE or SZONE(0~4)
* for LOCATION_MZONE, "available" means not used, not disabled, satisfying EFFECT_MUST_USE_MZONE * for LOCATION_MZONE, "available" means not used, not disabled, satisfying EFFECT_MUST_USE_MZONE
* for LOCATION_SZONE, "available" means not used, not disabled * for LOCATION_SZONE, "available" means not used, not disabled
*/ */
...@@ -754,11 +754,11 @@ int32 field::get_spsummonable_count_fromex_rule4(card* pcard, uint8 playerid, ui ...@@ -754,11 +754,11 @@ int32 field::get_spsummonable_count_fromex_rule4(card* pcard, uint8 playerid, ui
return count; return count;
} }
/** /**
* @param playerid the target player * @param playerid target player
* @param uplayer the request player, PLAYER_NONE means ignoring EFFECT_MAX_MZONE * @param uplayer request player, PLAYER_NONE means ignoring EFFECT_MAX_MZONE
* @param reason location reason * @param reason location reason
* *
* @return the remaining count in playerid's MZONE after applying EFFECT_MAX_MZONE (can be negative). * @return the remaining count in the MZONE of `playerid` after applying EFFECT_MAX_MZONE (can be negative).
*/ */
int32 field::get_mzone_limit(uint8 playerid, uint8 uplayer, uint32 reason) { int32 field::get_mzone_limit(uint8 playerid, uint8 uplayer, uint32 reason) {
uint32 used_flag = player[playerid].used_location; uint32 used_flag = player[playerid].used_location;
...@@ -787,11 +787,11 @@ int32 field::get_mzone_limit(uint8 playerid, uint8 uplayer, uint32 reason) { ...@@ -787,11 +787,11 @@ int32 field::get_mzone_limit(uint8 playerid, uint8 uplayer, uint32 reason) {
return limit; return limit;
} }
/** /**
* @param playerid the target player * @param playerid target player
* @param uplayer the request player, PLAYER_NONE means ignoring EFFECT_MAX_SZONE * @param uplayer request player, PLAYER_NONE means ignoring EFFECT_MAX_SZONE
* @param reason location reason * @param reason location reason
* *
* @return the remaining count in playerid's SZONE(0~4) after applying EFFECT_MAX_SZONE. * @return the remaining count in the SZONE(0~4) of `playerid` after applying EFFECT_MAX_SZONE.
*/ */
int32 field::get_szone_limit(uint8 playerid, uint8 uplayer, uint32 reason) { int32 field::get_szone_limit(uint8 playerid, uint8 uplayer, uint32 reason) {
uint32 used_flag = player[playerid].used_location; uint32 used_flag = player[playerid].used_location;
...@@ -835,8 +835,8 @@ uint32 field::get_rule_zone_fromex(int32 playerid, card* pcard) { ...@@ -835,8 +835,8 @@ uint32 field::get_rule_zone_fromex(int32 playerid, card* pcard) {
} }
} }
/** /**
* @param playerid the target player * @param playerid target player
* @param uplayer the request player, PLAYER_NONE means ignoring EFFECT_MUST_USE_MZONE of uplayer * @param uplayer request player, PLAYER_NONE means ignoring EFFECT_MUST_USE_MZONE of uplayer
* @param reason location reason * @param reason location reason
* @param pcard the card about to move * @param pcard the card about to move
* @param flag storing the zones in MZONE blocked by EFFECT_MUST_USE_MZONE * @param flag storing the zones in MZONE blocked by EFFECT_MUST_USE_MZONE
......
...@@ -303,6 +303,7 @@ struct processor { ...@@ -303,6 +303,7 @@ struct processor {
int32 summon_count[2]{}; int32 summon_count[2]{};
uint8 extra_summon[2]{}; uint8 extra_summon[2]{};
int32 spe_effect[2]{}; int32 spe_effect[2]{};
int32 last_select_hint[2]{ 0 };
int32 duel_options{ 0 }; int32 duel_options{ 0 };
int32 duel_rule{ CURRENT_RULE }; //current rule: 5, Master Rule 2020 int32 duel_rule{ CURRENT_RULE }; //current rule: 5, Master Rule 2020
uint32 copy_reset{ 0 }; uint32 copy_reset{ 0 };
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#define GROUP_H_ #define GROUP_H_
#include "common.h" #include "common.h"
#include "sort.h"
#include <set> #include <set>
#include <list> #include <list>
......
...@@ -2094,7 +2094,7 @@ int32 scriptlib::duel_get_location_count_fromex(lua_State *L) { ...@@ -2094,7 +2094,7 @@ int32 scriptlib::duel_get_location_count_fromex(lua_State *L) {
} }
return 2; return 2;
} }
// Return the number of available grids in playerid's Main MZONE and Extra MZONE // Return the number of available slots in the Main MZONE and Extra MZONE of `playerid`.
int32 scriptlib::duel_get_usable_mzone_count(lua_State *L) { int32 scriptlib::duel_get_usable_mzone_count(lua_State *L) {
check_param_count(L, 1); check_param_count(L, 1);
uint32 playerid = (uint32)lua_tointeger(L, 1); uint32 playerid = (uint32)lua_tointeger(L, 1);
...@@ -3703,12 +3703,24 @@ int32 scriptlib::duel_hint(lua_State * L) { ...@@ -3703,12 +3703,24 @@ int32 scriptlib::duel_hint(lua_State * L) {
if(htype == HINT_OPSELECTED) if(htype == HINT_OPSELECTED)
playerid = 1 - playerid; playerid = 1 - playerid;
duel* pduel = interpreter::get_duel_info(L); duel* pduel = interpreter::get_duel_info(L);
if(htype == HINT_SELECTMSG)
pduel->game_field->core.last_select_hint[playerid] = desc;
pduel->write_buffer8(MSG_HINT); pduel->write_buffer8(MSG_HINT);
pduel->write_buffer8(htype); pduel->write_buffer8(htype);
pduel->write_buffer8(playerid); pduel->write_buffer8(playerid);
pduel->write_buffer32(desc); pduel->write_buffer32(desc);
return 0; return 0;
} }
int32 scriptlib::duel_get_last_select_hint(lua_State* L) {
duel* pduel = interpreter::get_duel_info(L);
uint8 playerid = pduel->game_field->core.reason_player;
if(lua_gettop(L) >= 1)
playerid = (uint8)lua_tointeger(L, 1);
if(playerid != 0 && playerid != 1)
return 0;
lua_pushinteger(L, pduel->game_field->core.last_select_hint[playerid]);
return 1;
}
int32 scriptlib::duel_hint_selection(lua_State *L) { int32 scriptlib::duel_hint_selection(lua_State *L) {
check_param_count(L, 1); check_param_count(L, 1);
check_param(L, PARAM_TYPE_GROUP, 1); check_param(L, PARAM_TYPE_GROUP, 1);
...@@ -4963,6 +4975,7 @@ static const struct luaL_Reg duellib[] = { ...@@ -4963,6 +4975,7 @@ static const struct luaL_Reg duellib[] = {
{ "CheckRemoveOverlayCard", scriptlib::duel_check_remove_overlay_card }, { "CheckRemoveOverlayCard", scriptlib::duel_check_remove_overlay_card },
{ "RemoveOverlayCard", scriptlib::duel_remove_overlay_card }, { "RemoveOverlayCard", scriptlib::duel_remove_overlay_card },
{ "Hint", scriptlib::duel_hint }, { "Hint", scriptlib::duel_hint },
{ "GetLastSelectHint", scriptlib::duel_get_last_select_hint },
{ "HintSelection", scriptlib::duel_hint_selection }, { "HintSelection", scriptlib::duel_hint_selection },
{ "SelectEffectYesNo", scriptlib::duel_select_effect_yesno }, { "SelectEffectYesNo", scriptlib::duel_select_effect_yesno },
{ "SelectYesNo", scriptlib::duel_select_yesno }, { "SelectYesNo", scriptlib::duel_select_yesno },
......
...@@ -569,6 +569,7 @@ public: ...@@ -569,6 +569,7 @@ public:
static int32 duel_remove_overlay_card(lua_State *L); static int32 duel_remove_overlay_card(lua_State *L);
static int32 duel_hint(lua_State *L); static int32 duel_hint(lua_State *L);
static int32 duel_get_last_select_hint(lua_State *L);
static int32 duel_hint_selection(lua_State *L); static int32 duel_hint_selection(lua_State *L);
static int32 duel_select_effect_yesno(lua_State *L); static int32 duel_select_effect_yesno(lua_State *L);
static int32 duel_select_yesno(lua_State *L); static int32 duel_select_yesno(lua_State *L);
......
#ifndef SORT_H_
#define SORT_H_
class card;
struct card_sort {
bool operator()(card* const& c1, card* const& c2) const;
};
#endif /* SORT_H_ */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment