implement $item command on non-bb and on proxy
This commit is contained in:
+42
-6
@@ -803,23 +803,59 @@ static void server_command_item(shared_ptr<ServerState>, shared_ptr<Lobby> l,
|
||||
|
||||
string data = parse_data_string(encode_sjis(args));
|
||||
if (data.size() < 2) {
|
||||
send_text_message(c, u"$C6Item codes must be\n2 bytes or more.");
|
||||
send_text_message(c, u"$C6Item codes must be\n2 bytes or more");
|
||||
return;
|
||||
}
|
||||
if (data.size() > 16) {
|
||||
send_text_message(c, u"$C6Item codes must be\n16 bytes or fewer.");
|
||||
send_text_message(c, u"$C6Item codes must be\n16 bytes or fewer");
|
||||
return;
|
||||
}
|
||||
|
||||
ItemData item_data;
|
||||
l->next_drop_item.clear();
|
||||
if (data.size() <= 12) {
|
||||
memcpy(&l->next_drop_item.data.data1, data.data(), data.size());
|
||||
} else {
|
||||
memcpy(&l->next_drop_item.data.data1, data.data(), 12);
|
||||
memcpy(&l->next_drop_item.data.data2, data.data() + 12, 12 - data.size());
|
||||
memcpy(&l->next_drop_item.data.data2, data.data() + 12, data.size() - 12);
|
||||
}
|
||||
|
||||
send_text_message(c, u"$C6Next drop chosen.");
|
||||
string name = name_for_item(l->next_drop_item.data, true);
|
||||
send_text_message(c, u"$C7Next drop:\n" + decode_sjis(name));
|
||||
}
|
||||
|
||||
static void proxy_command_item(shared_ptr<ServerState>,
|
||||
ProxyServer::LinkedSession& session, const std::u16string& args) {
|
||||
if (session.version == GameVersion::BB) {
|
||||
send_text_message(session.client_channel,
|
||||
u"$C6This command cannot\nbe used on the proxy\nserver in BB games");
|
||||
return;
|
||||
}
|
||||
if (session.lobby_client_id != session.leader_client_id) {
|
||||
send_text_message(session.client_channel,
|
||||
u"$C6You must be the\nleader to use this\ncommand");
|
||||
return;
|
||||
}
|
||||
|
||||
string data = parse_data_string(encode_sjis(args));
|
||||
if (data.size() < 2) {
|
||||
send_text_message(session.client_channel, u"$C6Item codes must be\n2 bytes or more");
|
||||
return;
|
||||
}
|
||||
if (data.size() > 16) {
|
||||
send_text_message(session.client_channel, u"$C6Item codes must be\n16 bytes or fewer");
|
||||
return;
|
||||
}
|
||||
|
||||
session.next_drop_item.clear();
|
||||
if (data.size() <= 12) {
|
||||
memcpy(&session.next_drop_item.data.data1, data.data(), data.size());
|
||||
} else {
|
||||
memcpy(&session.next_drop_item.data.data1, data.data(), 12);
|
||||
memcpy(&session.next_drop_item.data.data2, data.data() + 12, data.size() - 12);
|
||||
}
|
||||
|
||||
string name = name_for_item(session.next_drop_item.data, true);
|
||||
send_text_message(session.client_channel, u"$C7Next drop:\n" + decode_sjis(name));
|
||||
}
|
||||
|
||||
|
||||
@@ -852,7 +888,7 @@ static const unordered_map<u16string, ChatCommandDefinition> chat_commands({
|
||||
{u"$gc" , {server_command_get_self_card , nullptr , u"Usage:\ngc"}},
|
||||
{u"$infhp" , {server_command_infinite_hp , proxy_command_infinite_hp , u"Usage:\ninfhp"}},
|
||||
{u"$inftp" , {server_command_infinite_tp , proxy_command_infinite_tp , u"Usage:\ninftp"}},
|
||||
{u"$item" , {server_command_item , nullptr , u"Usage:\nitem <item-code>"}},
|
||||
{u"$item" , {server_command_item , proxy_command_item , u"Usage:\nitem <item-code>"}},
|
||||
{u"$kick" , {server_command_kick , nullptr , u"Usage:\nkick <name-or-number>"}},
|
||||
{u"$li" , {server_command_lobby_info , proxy_command_lobby_info , u"Usage:\nli"}},
|
||||
{u"$maxlevel" , {server_command_max_level , nullptr , u"Usage:\nmax_level <level>"}},
|
||||
|
||||
@@ -478,11 +478,11 @@ struct C_MenuItemInfoRequest_09 {
|
||||
// softlocking the game.
|
||||
|
||||
struct S_Unknown_PC_0E {
|
||||
PlayerLobbyDataPC lobby_data[4]; // This type is a guess
|
||||
parray<uint8_t, 0x21> unknown_a1;
|
||||
parray<uint8_t, 0x08> unknown_a1;
|
||||
parray<uint8_t, 0x18> unknown_a2[4];
|
||||
parray<uint8_t, 0x18> unknown_a3;
|
||||
};
|
||||
|
||||
// TODO: Document XB format for this. It's probably the same as the GC format.
|
||||
struct S_Unknown_GC_0E {
|
||||
PlayerLobbyDataGC lobby_data[4]; // This type is a guess
|
||||
struct UnknownA0 {
|
||||
@@ -496,6 +496,10 @@ struct S_Unknown_GC_0E {
|
||||
uint8_t unknown_a3[4];
|
||||
};
|
||||
|
||||
struct S_Unknown_XB_0E {
|
||||
parray<uint8_t, 0xE8> unknown_a1;
|
||||
};
|
||||
|
||||
// 0F: Invalid command
|
||||
|
||||
// 10 (C->S): Menu selection
|
||||
|
||||
+23
-4
@@ -626,6 +626,10 @@ void PlayerLobbyDataBB::clear() {
|
||||
constexpr uint32_t MESETA_IDENTIFIER = 0x00040000;
|
||||
|
||||
ItemData::ItemData() {
|
||||
this->clear();
|
||||
}
|
||||
|
||||
void ItemData::clear() {
|
||||
this->data1d[0] = 0;
|
||||
this->data1d[1] = 0;
|
||||
this->data1d[2] = 0;
|
||||
@@ -643,22 +647,37 @@ uint32_t ItemData::primary_identifier() const {
|
||||
}
|
||||
}
|
||||
|
||||
PlayerInventoryItem::PlayerInventoryItem()
|
||||
: equip_flags(0x0000), tech_flag(0x0000), game_flags(0x00000000), data() { }
|
||||
PlayerInventoryItem::PlayerInventoryItem() {
|
||||
this->clear();
|
||||
}
|
||||
|
||||
PlayerInventoryItem::PlayerInventoryItem(const PlayerBankItem& src)
|
||||
: tech_flag(0x0001), data(src.data) {
|
||||
this->equip_flags = (this->data.data1[0] > 2) ? 0x0044 : 0x0050;
|
||||
}
|
||||
|
||||
PlayerBankItem::PlayerBankItem()
|
||||
: data(), amount(0), show_flags(0) { }
|
||||
void PlayerInventoryItem::clear() {
|
||||
this->equip_flags = 0x0000;
|
||||
this->tech_flag = 0x0000;
|
||||
this->game_flags = 0x00000000;
|
||||
this->data.clear();
|
||||
}
|
||||
|
||||
PlayerBankItem::PlayerBankItem() {
|
||||
this->clear();
|
||||
}
|
||||
|
||||
PlayerBankItem::PlayerBankItem(const PlayerInventoryItem& src)
|
||||
: data(src.data),
|
||||
amount(stack_size_for_item(this->data)),
|
||||
show_flags(1) { }
|
||||
|
||||
void PlayerBankItem::clear() {
|
||||
this->data.clear();
|
||||
this->amount = 0;
|
||||
this->show_flags = 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
PlayerInventory::PlayerInventory()
|
||||
|
||||
@@ -28,6 +28,7 @@ struct ItemData { // 0x14 bytes
|
||||
} __attribute__((packed));
|
||||
|
||||
ItemData();
|
||||
void clear();
|
||||
|
||||
uint32_t primary_identifier() const;
|
||||
} __attribute__((packed));
|
||||
@@ -42,6 +43,7 @@ struct PlayerInventoryItem { // 0x1C bytes
|
||||
|
||||
PlayerInventoryItem();
|
||||
PlayerInventoryItem(const PlayerBankItem&);
|
||||
void clear();
|
||||
} __attribute__((packed));
|
||||
|
||||
struct PlayerBankItem { // 0x18 bytes
|
||||
@@ -51,6 +53,7 @@ struct PlayerBankItem { // 0x18 bytes
|
||||
|
||||
PlayerBankItem();
|
||||
PlayerBankItem(const PlayerInventoryItem&);
|
||||
void clear();
|
||||
} __attribute__((packed));
|
||||
|
||||
struct PlayerInventory { // 0x34C bytes
|
||||
|
||||
+42
-1
@@ -676,6 +676,30 @@ static HandlerResult process_server_60_62_6C_6D_C9_CB(shared_ptr<ServerState>,
|
||||
}
|
||||
}
|
||||
|
||||
if (!data.empty() &&
|
||||
session.next_drop_item.data.data1d[0] &&
|
||||
(session.version != GameVersion::BB)) {
|
||||
if (data[0] == 0x60) {
|
||||
const auto& cmd = check_size_t<G_EnemyDropItemRequest_6x60>(data);
|
||||
session.next_drop_item.data.id = session.next_item_id++;
|
||||
send_drop_item(session.server_channel, session.next_drop_item.data,
|
||||
true, cmd.area, cmd.x, cmd.z, cmd.request_id);
|
||||
send_drop_item(session.client_channel, session.next_drop_item.data,
|
||||
true, cmd.area, cmd.x, cmd.z, cmd.request_id);
|
||||
session.next_drop_item.clear();
|
||||
return HandlerResult::Type::SUPPRESS;
|
||||
} else if (data[0] == -0x5E) { // A2
|
||||
const auto& cmd = check_size_t<G_BoxItemDropRequest_6xA2>(data);
|
||||
session.next_drop_item.data.id = session.next_item_id++;
|
||||
send_drop_item(session.server_channel, session.next_drop_item.data,
|
||||
false, cmd.area, cmd.x, cmd.z, cmd.request_id);
|
||||
send_drop_item(session.client_channel, session.next_drop_item.data,
|
||||
false, cmd.area, cmd.x, cmd.z, cmd.request_id);
|
||||
session.next_drop_item.clear();
|
||||
return HandlerResult::Type::SUPPRESS;
|
||||
}
|
||||
}
|
||||
|
||||
return HandlerResult::Type::FORWARD;
|
||||
}
|
||||
|
||||
@@ -766,6 +790,16 @@ static HandlerResult process_server_gc_B8(shared_ptr<ServerState>,
|
||||
return HandlerResult::Type::FORWARD;
|
||||
}
|
||||
|
||||
static void update_leader_id(ProxyServer::LinkedSession& session, uint8_t leader_id) {
|
||||
if (session.leader_client_id != leader_id) {
|
||||
session.leader_client_id = leader_id;
|
||||
session.log.info("Changed room leader to %zu", session.leader_client_id);
|
||||
if (session.leader_client_id == session.lobby_client_id) {
|
||||
send_text_message(session.client_channel, u"$C6You are now the leader");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename CmdT>
|
||||
static HandlerResult process_server_65_67_68(shared_ptr<ServerState>,
|
||||
ProxyServer::LinkedSession& session, uint16_t command, uint32_t flag, string& data) {
|
||||
@@ -789,6 +823,7 @@ static HandlerResult process_server_65_67_68(shared_ptr<ServerState>,
|
||||
bool modified = false;
|
||||
|
||||
session.lobby_client_id = cmd.client_id;
|
||||
update_leader_id(session, cmd.leader_id);
|
||||
for (size_t x = 0; x < flag; x++) {
|
||||
size_t index = cmd.entries[x].lobby_data.client_id;
|
||||
if (index >= session.lobby_players.size()) {
|
||||
@@ -840,6 +875,7 @@ static HandlerResult process_server_64(shared_ptr<ServerState>,
|
||||
bool modified = false;
|
||||
|
||||
session.lobby_client_id = cmd->client_id;
|
||||
update_leader_id(session, cmd->leader_id);
|
||||
for (size_t x = 0; x < flag; x++) {
|
||||
if (cmd->lobby_data[x].guild_card == session.remote_guild_card_number) {
|
||||
cmd->lobby_data[x].guild_card = session.license->serial_number;
|
||||
@@ -885,6 +921,7 @@ static HandlerResult process_server_66_69(shared_ptr<ServerState>,
|
||||
session.lobby_players[index].name.clear();
|
||||
session.log.info("Removed lobby player (%zu)", index);
|
||||
}
|
||||
update_leader_id(session, cmd.leader_id);
|
||||
return HandlerResult::Type::FORWARD;
|
||||
}
|
||||
|
||||
@@ -974,7 +1011,11 @@ static HandlerResult process_client_60_62_6C_6D_C9_CB(shared_ptr<ServerState> s,
|
||||
if (cmd.guild_card_number == session.license->serial_number) {
|
||||
cmd.guild_card_number = session.remote_guild_card_number;
|
||||
}
|
||||
} else if (data[0] == 0x2F || data[0] == 0x4C) {
|
||||
}
|
||||
}
|
||||
|
||||
if (!data.empty()) {
|
||||
if (data[0] == 0x2F || data[0] == 0x4C) {
|
||||
if (session.infinite_hp) {
|
||||
vector<PSOSubcommand> subs;
|
||||
for (size_t amount = 1020; amount > 0;) {
|
||||
|
||||
+3
-1
@@ -445,11 +445,13 @@ ProxyServer::LinkedSession::LinkedSession(
|
||||
infinite_tp(false),
|
||||
save_files(false),
|
||||
function_call_return_value(-1),
|
||||
next_item_id(0x0F000000),
|
||||
override_section_id(-1),
|
||||
override_lobby_event(-1),
|
||||
override_lobby_number(-1),
|
||||
lobby_players(12),
|
||||
lobby_client_id(0) {
|
||||
lobby_client_id(0),
|
||||
leader_client_id(0) {
|
||||
this->last_switch_enabled_command.subcommand = 0;
|
||||
memset(this->prev_server_command_bytes, 0, sizeof(this->prev_server_command_bytes));
|
||||
}
|
||||
|
||||
@@ -66,6 +66,8 @@ public:
|
||||
bool save_files;
|
||||
int64_t function_call_return_value; // -1 = don't block function calls
|
||||
G_SwitchStateChanged_6x05 last_switch_enabled_command;
|
||||
PlayerInventoryItem next_drop_item;
|
||||
uint32_t next_item_id;
|
||||
int16_t override_section_id;
|
||||
int16_t override_lobby_event;
|
||||
int16_t override_lobby_number;
|
||||
@@ -78,6 +80,7 @@ public:
|
||||
};
|
||||
std::vector<LobbyPlayer> lobby_players;
|
||||
size_t lobby_client_id;
|
||||
size_t leader_client_id;
|
||||
|
||||
std::shared_ptr<PSOBBMultiKeyDetectorEncryption> detector_crypt;
|
||||
|
||||
|
||||
+77
-103
@@ -671,63 +671,91 @@ static void process_subcommand_sort_inventory_bb(shared_ptr<ServerState>,
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// EXP/Drop Item commands
|
||||
|
||||
static void process_subcommand_enemy_drop_item_request(shared_ptr<ServerState>,
|
||||
shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
||||
const string& data) {
|
||||
if (l->version == GameVersion::BB) {
|
||||
const auto* cmd = check_size_sc<G_EnemyDropItemRequest_6x60>(data);
|
||||
static bool drop_item(
|
||||
std::shared_ptr<Lobby> l,
|
||||
int64_t enemy_id,
|
||||
uint8_t area,
|
||||
float x,
|
||||
float z,
|
||||
uint16_t request_id) {
|
||||
|
||||
if (!l->is_game()) {
|
||||
return;
|
||||
}
|
||||
if (!(l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED)) {
|
||||
throw logic_error("item tracking not enabled in BB game");
|
||||
}
|
||||
PlayerInventoryItem item;
|
||||
|
||||
// If there's an override item set (via the $item command), use that item code
|
||||
if (l->next_drop_item.data.data1d[0]) {
|
||||
item = l->next_drop_item;
|
||||
l->next_drop_item.clear();
|
||||
|
||||
// If the game is BB, run the rare + common drop logic
|
||||
} else if (l->version == GameVersion::BB) {
|
||||
if (!l->common_item_creator.get()) {
|
||||
throw runtime_error("received box drop subcommand without item creator present");
|
||||
}
|
||||
|
||||
PlayerInventoryItem item;
|
||||
|
||||
// TODO: Deduplicate this code with the box drop item request handler
|
||||
bool is_rare = false;
|
||||
if (l->next_drop_item.data.data1d[0]) {
|
||||
item = l->next_drop_item;
|
||||
l->next_drop_item.data.data1d[0] = 0;
|
||||
} else {
|
||||
if (l->rare_item_set) {
|
||||
if (cmd->enemy_id <= 0x65) {
|
||||
is_rare = sample_rare_item(*
|
||||
l->random, l->rare_item_set->rares[cmd->enemy_id].probability);
|
||||
}
|
||||
}
|
||||
|
||||
if (is_rare) {
|
||||
const auto& code = l->rare_item_set->rares[cmd->enemy_id].item_code;
|
||||
item.data.data1[0] = code[0];
|
||||
item.data.data1[1] = code[1];
|
||||
item.data.data1[2] = code[2];
|
||||
//RandPercentages();
|
||||
if (item.data.data1d[0] == 0) {
|
||||
item.data.data1[4] |= 0x80; // make it unidentified if it's a weapon
|
||||
const RareItemDrop* rare_drop = nullptr;
|
||||
if (l->rare_item_set) {
|
||||
if (enemy_id < 0) {
|
||||
for (size_t z = 0; z < 30; z++) {
|
||||
if (l->rare_item_set->box_areas[z] != area) {
|
||||
continue;
|
||||
}
|
||||
if (sample_rare_item(
|
||||
*l->random, l->rare_item_set->box_rares[z].probability)) {
|
||||
rare_drop = &l->rare_item_set->box_rares[z];
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
item.data = l->common_item_creator->create_drop_item(false, l->episode,
|
||||
l->difficulty, cmd->area, l->section_id);
|
||||
} catch (const out_of_range&) {
|
||||
// create_common_item throws this when it doesn't want to make an item
|
||||
return;
|
||||
if ((enemy_id <= 0x65) &&
|
||||
sample_rare_item(
|
||||
*l->random, l->rare_item_set->rares[enemy_id].probability)) {
|
||||
rare_drop = &l->rare_item_set->rares[enemy_id];
|
||||
}
|
||||
}
|
||||
}
|
||||
item.data.id = l->generate_item_id(0xFF);
|
||||
|
||||
l->add_item(item, cmd->area, cmd->x, cmd->z);
|
||||
send_drop_item(l, item.data, false, cmd->area, cmd->x, cmd->z,
|
||||
cmd->request_id);
|
||||
if (rare_drop) {
|
||||
item.data.data1[0] = rare_drop->item_code[0];
|
||||
item.data.data1[1] = rare_drop->item_code[1];
|
||||
item.data.data1[2] = rare_drop->item_code[2];
|
||||
// TODO: Add random percentages
|
||||
if (item.data.data1d[0] == 0) {
|
||||
item.data.data1[4] |= 0x80; // make it unidentified if it's a weapon
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
item.data = l->common_item_creator->create_drop_item(
|
||||
false, l->episode, l->difficulty, area, l->section_id);
|
||||
} catch (const out_of_range&) {
|
||||
// create_common_item throws this when it doesn't want to make an item
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// If the game is not BB and there's no override item, forward the request to
|
||||
// the leader instead of generating the item drop command
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
item.data.id = l->generate_item_id(0xFF);
|
||||
|
||||
if (l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED) {
|
||||
l->add_item(item, area, x, z);
|
||||
}
|
||||
send_drop_item(l, item.data, (enemy_id >= 0), area, x, z, request_id);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void process_subcommand_enemy_drop_item_request(shared_ptr<ServerState>,
|
||||
shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
||||
const string& data) {
|
||||
if (!l->is_game()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto* cmd = check_size_sc<G_EnemyDropItemRequest_6x60>(data);
|
||||
if (!drop_item(l, cmd->enemy_id, cmd->area, cmd->x, cmd->z, cmd->request_id)) {
|
||||
forward_subcommand(l, c, command, flag, data);
|
||||
}
|
||||
}
|
||||
@@ -735,66 +763,12 @@ static void process_subcommand_enemy_drop_item_request(shared_ptr<ServerState>,
|
||||
static void process_subcommand_box_drop_item_request(shared_ptr<ServerState>,
|
||||
shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
||||
const string& data) {
|
||||
if (l->version == GameVersion::BB) {
|
||||
const auto* cmd = check_size_sc<G_BoxItemDropRequest_6xA2>(data);
|
||||
if (!l->is_game()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!l->is_game()) {
|
||||
return;
|
||||
}
|
||||
if (!(l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED)) {
|
||||
throw logic_error("item tracking not enabled in BB game");
|
||||
}
|
||||
if (!l->common_item_creator.get()) {
|
||||
throw runtime_error("received box drop subcommand without item creator present");
|
||||
}
|
||||
|
||||
PlayerInventoryItem item;
|
||||
|
||||
bool is_rare = false;
|
||||
if (l->next_drop_item.data.data1d[0]) {
|
||||
item = l->next_drop_item;
|
||||
l->next_drop_item.data.data1d[0] = 0;
|
||||
} else {
|
||||
size_t index;
|
||||
if (l->rare_item_set) {
|
||||
for (index = 0; index < 30; index++) {
|
||||
if (l->rare_item_set->box_areas[index] != cmd->area) {
|
||||
continue;
|
||||
}
|
||||
if (sample_rare_item(
|
||||
*l->random, l->rare_item_set->box_rares[index].probability)) {
|
||||
is_rare = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (is_rare) {
|
||||
const auto& code = l->rare_item_set->box_rares[index].item_code;
|
||||
item.data.data1[0] = code[0];
|
||||
item.data.data1[1] = code[1];
|
||||
item.data.data1[2] = code[2];
|
||||
//RandPercentages();
|
||||
if (item.data.data1d[0] == 0) {
|
||||
item.data.data1[4] |= 0x80; // make it unidentified if it's a weapon
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
item.data = l->common_item_creator->create_drop_item(true, l->episode,
|
||||
l->difficulty, cmd->area, l->section_id);
|
||||
} catch (const out_of_range&) {
|
||||
// create_common_item throws this when it doesn't want to make an item
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
item.data.id = l->generate_item_id(0xFF);
|
||||
|
||||
l->add_item(item, cmd->area, cmd->x, cmd->z);
|
||||
send_drop_item(l, item.data, false, cmd->area, cmd->x, cmd->z,
|
||||
cmd->request_id);
|
||||
|
||||
} else {
|
||||
const auto* cmd = check_size_sc<G_BoxItemDropRequest_6xA2>(data);
|
||||
if (!drop_item(l, -1, cmd->area, cmd->x, cmd->z, cmd->request_id)) {
|
||||
forward_subcommand(l, c, command, flag, data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1267,6 +1267,13 @@ void send_revive_player(shared_ptr<Lobby> l, shared_ptr<Client> c) {
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// BB game commands
|
||||
|
||||
void send_drop_item(Channel& ch, const ItemData& item,
|
||||
bool from_enemy, uint8_t area, float x, float z, uint16_t request_id) {
|
||||
G_DropItem_6x5F cmd = {
|
||||
0x5F, 0x0B, 0x0000, area, from_enemy, request_id, x, z, 0, item, 0};
|
||||
ch.send(0x60, 0x00, &cmd, sizeof(cmd));
|
||||
}
|
||||
|
||||
void send_drop_item(shared_ptr<Lobby> l, const ItemData& item,
|
||||
bool from_enemy, uint8_t area, float x, float z, uint16_t request_id) {
|
||||
G_DropItem_6x5F cmd = {
|
||||
|
||||
@@ -208,6 +208,8 @@ void send_set_player_visibility(std::shared_ptr<Lobby> l,
|
||||
std::shared_ptr<Client> c, bool visible);
|
||||
void send_revive_player(std::shared_ptr<Lobby> l, std::shared_ptr<Client> c);
|
||||
|
||||
void send_drop_item(Channel& ch, const ItemData& item,
|
||||
bool from_enemy, uint8_t area, float x, float z, uint16_t request_id);
|
||||
void send_drop_item(std::shared_ptr<Lobby> l, const ItemData& item,
|
||||
bool from_enemy, uint8_t area, float x, float z, uint16_t request_id);
|
||||
void send_drop_stacked_item(std::shared_ptr<Lobby> l, const ItemData& item,
|
||||
|
||||
Reference in New Issue
Block a user