handle inventory extension data properly
This commit is contained in:
@@ -1,6 +1,5 @@
|
|||||||
## General
|
## General
|
||||||
|
|
||||||
- Extension data in inventories is not handled properly
|
|
||||||
- Test PSOX (blocked on Insignia private server support)
|
- Test PSOX (blocked on Insignia private server support)
|
||||||
- Implement server-side drops on non-BB game versions
|
- Implement server-side drops on non-BB game versions
|
||||||
- Find a way to silence audio in RunDOL.s
|
- Find a way to silence audio in RunDOL.s
|
||||||
|
|||||||
+13
-15
@@ -835,7 +835,7 @@ static void server_command_edit(shared_ptr<Client> c, const std::u16string& args
|
|||||||
uint8_t level = stoul(tokens.at(2)) - 1;
|
uint8_t level = stoul(tokens.at(2)) - 1;
|
||||||
if (tokens.at(1) == "all") {
|
if (tokens.at(1) == "all") {
|
||||||
for (size_t x = 0; x < 0x14; x++) {
|
for (size_t x = 0; x < 0x14; x++) {
|
||||||
c->game_data.player()->disp.technique_levels.data()[x] = level;
|
c->game_data.player()->set_technique_level(x, level);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
uint8_t tech_id = technique_for_name(decode_sjis(tokens.at(1)));
|
uint8_t tech_id = technique_for_name(decode_sjis(tokens.at(1)));
|
||||||
@@ -844,7 +844,7 @@ static void server_command_edit(shared_ptr<Client> c, const std::u16string& args
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
c->game_data.player()->disp.technique_levels[tech_id] = level;
|
c->game_data.player()->set_technique_level(tech_id, level);
|
||||||
} catch (const out_of_range&) {
|
} catch (const out_of_range&) {
|
||||||
send_text_message(c, u"$C6Invalid technique");
|
send_text_message(c, u"$C6Invalid technique");
|
||||||
return;
|
return;
|
||||||
@@ -1133,7 +1133,7 @@ static void server_command_what(shared_ptr<Client> c, const std::u16string&) {
|
|||||||
send_text_message(c, u"$C4No items are near you");
|
send_text_message(c, u"$C4No items are near you");
|
||||||
} else {
|
} else {
|
||||||
const auto& item = l->item_id_to_floor_item.at(nearest_item_id);
|
const auto& item = l->item_id_to_floor_item.at(nearest_item_id);
|
||||||
string name = item.inv_item.data.name(true);
|
string name = item.data.name(true);
|
||||||
send_text_message(c, decode_sjis(name));
|
send_text_message(c, decode_sjis(name));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1226,14 +1226,13 @@ static void server_command_item(shared_ptr<Client> c, const std::u16string& args
|
|||||||
check_is_game(l, true);
|
check_is_game(l, true);
|
||||||
check_cheats_enabled(s, l);
|
check_cheats_enabled(s, l);
|
||||||
|
|
||||||
PlayerInventoryItem item;
|
ItemData item(encode_sjis(args));
|
||||||
item.data = ItemData(encode_sjis(args));
|
item.id = l->generate_item_id(c->lobby_client_id);
|
||||||
item.data.id = l->generate_item_id(c->lobby_client_id);
|
|
||||||
|
|
||||||
l->add_item(item, c->area, c->x, c->z);
|
l->add_item(item, c->area, c->x, c->z);
|
||||||
send_drop_stacked_item(l, item.data, c->area, c->x, c->z);
|
send_drop_stacked_item(l, item, c->area, c->x, c->z);
|
||||||
|
|
||||||
string name = item.data.name(true);
|
string name = item.name(true);
|
||||||
send_text_message(c, u"$C7Item created:\n" + decode_sjis(name));
|
send_text_message(c, u"$C7Item created:\n" + decode_sjis(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1258,21 +1257,20 @@ static void proxy_command_item(shared_ptr<ProxyServer::LinkedSession> ses, const
|
|||||||
|
|
||||||
bool set_drop = (!args.empty() && (args[0] == u'!'));
|
bool set_drop = (!args.empty() && (args[0] == u'!'));
|
||||||
|
|
||||||
PlayerInventoryItem item;
|
ItemData item(encode_sjis(set_drop ? args.substr(1) : args));
|
||||||
item.data = ItemData(encode_sjis(set_drop ? args.substr(1) : args));
|
item.id = random_object<uint32_t>();
|
||||||
item.data.id = random_object<uint32_t>();
|
|
||||||
|
|
||||||
if (set_drop) {
|
if (set_drop) {
|
||||||
ses->next_drop_item = item;
|
ses->next_drop_item = item;
|
||||||
|
|
||||||
string name = ses->next_drop_item.data.name(true);
|
string name = ses->next_drop_item.name(true);
|
||||||
send_text_message(ses->client_channel, u"$C7Next drop:\n" + decode_sjis(name));
|
send_text_message(ses->client_channel, u"$C7Next drop:\n" + decode_sjis(name));
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
send_drop_stacked_item(ses->client_channel, item.data, ses->area, ses->x, ses->z);
|
send_drop_stacked_item(ses->client_channel, item, ses->area, ses->x, ses->z);
|
||||||
send_drop_stacked_item(ses->server_channel, item.data, ses->area, ses->x, ses->z);
|
send_drop_stacked_item(ses->server_channel, item, ses->area, ses->x, ses->z);
|
||||||
|
|
||||||
string name = item.data.name(true);
|
string name = item.name(true);
|
||||||
send_text_message(ses->client_channel, u"$C7Item created:\n" + decode_sjis(name));
|
send_text_message(ses->client_channel, u"$C7Item created:\n" + decode_sjis(name));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4570,7 +4570,7 @@ struct G_SyncPlayerDispAndInventory_V3_6x70 {
|
|||||||
/* 00A4 */ parray<uint8_t, 0x14> unknown_a9;
|
/* 00A4 */ parray<uint8_t, 0x14> unknown_a9;
|
||||||
/* 00B8 */ le_uint32_t unknown_a10;
|
/* 00B8 */ le_uint32_t unknown_a10;
|
||||||
/* 00BC */ le_uint32_t unknown_a11;
|
/* 00BC */ le_uint32_t unknown_a11;
|
||||||
/* 00C0 */ parray<uint8_t, 0x14> technique_levels; // Last byte is uninitialized
|
/* 00C0 */ parray<uint8_t, 0x14> technique_levels_v1; // Last byte is uninitialized
|
||||||
/* 00D4 */ PlayerVisualConfig visual;
|
/* 00D4 */ PlayerVisualConfig visual;
|
||||||
/* 0124 */ PlayerStats stats;
|
/* 0124 */ PlayerStats stats;
|
||||||
/* 0148 */ struct {
|
/* 0148 */ struct {
|
||||||
@@ -5305,7 +5305,7 @@ struct G_CardCounts_GC_Ep3_6xBC {
|
|||||||
struct G_BankContentsHeader_BB_6xBC {
|
struct G_BankContentsHeader_BB_6xBC {
|
||||||
G_ExtendedHeader<G_UnusedHeader> header;
|
G_ExtendedHeader<G_UnusedHeader> header;
|
||||||
le_uint32_t checksum; // can be random; client won't notice
|
le_uint32_t checksum; // can be random; client won't notice
|
||||||
le_uint32_t numItems;
|
le_uint32_t num_items;
|
||||||
le_uint32_t meseta;
|
le_uint32_t meseta;
|
||||||
// Item data follows
|
// Item data follows
|
||||||
} __packed__;
|
} __packed__;
|
||||||
|
|||||||
+18
-12
@@ -28,45 +28,51 @@ void player_use_item(shared_ptr<Client> c, size_t item_index) {
|
|||||||
if (item.data.data1[2] > max_level) {
|
if (item.data.data1[2] > max_level) {
|
||||||
throw runtime_error("technique level too high");
|
throw runtime_error("technique level too high");
|
||||||
}
|
}
|
||||||
player->disp.technique_levels.data()[item.data.data1[4]] = item.data.data1[2];
|
player->set_technique_level(item.data.data1[4], item.data.data1[2]);
|
||||||
|
|
||||||
} else if ((item_identifier & 0xFFFF00) == 0x030A00) { // Grinder
|
} else if ((item_identifier & 0xFFFF00) == 0x030A00) { // Grinder
|
||||||
if (item.data.data1[2] > 2) {
|
if (item.data.data1[2] > 2) {
|
||||||
throw invalid_argument("incorrect grinder value");
|
throw runtime_error("incorrect grinder value");
|
||||||
}
|
}
|
||||||
auto& weapon = player->inventory.items[player->inventory.find_equipped_weapon()];
|
auto& weapon = player->inventory.items[player->inventory.find_equipped_weapon()];
|
||||||
auto weapon_def = s->item_parameter_table->get_weapon(
|
auto weapon_def = s->item_parameter_table->get_weapon(weapon.data.data1[1], weapon.data.data1[2]);
|
||||||
weapon.data.data1[1], weapon.data.data1[2]);
|
|
||||||
if (weapon.data.data1[3] >= weapon_def.max_grind) {
|
if (weapon.data.data1[3] >= weapon_def.max_grind) {
|
||||||
throw runtime_error("weapon already at maximum grind");
|
throw runtime_error("weapon already at maximum grind");
|
||||||
}
|
}
|
||||||
weapon.data.data1[3] += (item.data.data1[2] + 1);
|
weapon.data.data1[3] += (item.data.data1[2] + 1);
|
||||||
|
|
||||||
} else if ((item_identifier & 0xFFFF00) == 0x030B00) { // Material
|
} else if ((item_identifier & 0xFFFF00) == 0x030B00) { // Material
|
||||||
|
auto p = c->game_data.player();
|
||||||
|
using Type = SavedPlayerDataBB::MaterialType;
|
||||||
switch (item.data.data1[2]) {
|
switch (item.data.data1[2]) {
|
||||||
case 0: // Power Material
|
case 0: // Power Material
|
||||||
c->game_data.player()->disp.stats.char_stats.atp += 2;
|
p->set_material_usage(Type::POWER, p->get_material_usage(Type::POWER) + 1);
|
||||||
|
p->disp.stats.char_stats.atp += 2;
|
||||||
break;
|
break;
|
||||||
case 1: // Mind Material
|
case 1: // Mind Material
|
||||||
c->game_data.player()->disp.stats.char_stats.mst += 2;
|
p->set_material_usage(Type::MIND, p->get_material_usage(Type::MIND) + 1);
|
||||||
|
p->disp.stats.char_stats.mst += 2;
|
||||||
break;
|
break;
|
||||||
case 2: // Evade Material
|
case 2: // Evade Material
|
||||||
c->game_data.player()->disp.stats.char_stats.evp += 2;
|
p->set_material_usage(Type::EVADE, p->get_material_usage(Type::EVADE) + 1);
|
||||||
|
p->disp.stats.char_stats.evp += 2;
|
||||||
break;
|
break;
|
||||||
case 3: // HP Material
|
case 3: // HP Material
|
||||||
c->game_data.player()->inventory.hp_materials_used += 2;
|
p->set_material_usage(Type::HP, p->get_material_usage(Type::HP) + 1);
|
||||||
break;
|
break;
|
||||||
case 4: // TP Material
|
case 4: // TP Material
|
||||||
c->game_data.player()->inventory.tp_materials_used += 2;
|
p->set_material_usage(Type::TP, p->get_material_usage(Type::TP) + 1);
|
||||||
break;
|
break;
|
||||||
case 5: // Def Material
|
case 5: // Def Material
|
||||||
c->game_data.player()->disp.stats.char_stats.dfp += 2;
|
p->set_material_usage(Type::DEF, p->get_material_usage(Type::DEF) + 1);
|
||||||
|
p->disp.stats.char_stats.dfp += 2;
|
||||||
break;
|
break;
|
||||||
case 6: // Luck Material
|
case 6: // Luck Material
|
||||||
c->game_data.player()->disp.stats.char_stats.lck += 2;
|
p->set_material_usage(Type::LUCK, p->get_material_usage(Type::LUCK) + 1);
|
||||||
|
p->disp.stats.char_stats.lck += 2;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw invalid_argument("unknown material used");
|
throw runtime_error("unknown material used");
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if ((item_identifier & 0xFFFF00) == 0x030F00) { // AddSlot
|
} else if ((item_identifier & 0xFFFF00) == 0x030F00) { // AddSlot
|
||||||
|
|||||||
+5
-5
@@ -257,20 +257,20 @@ uint8_t Lobby::game_event_for_lobby_event(uint8_t lobby_event) {
|
|||||||
return lobby_event;
|
return lobby_event;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Lobby::add_item(const PlayerInventoryItem& item, uint8_t area, float x, float z) {
|
void Lobby::add_item(const ItemData& data, uint8_t area, float x, float z) {
|
||||||
auto& fi = this->item_id_to_floor_item[item.data.id];
|
auto& fi = this->item_id_to_floor_item[data.id];
|
||||||
fi.inv_item = item;
|
fi.data = data;
|
||||||
fi.area = area;
|
fi.area = area;
|
||||||
fi.x = x;
|
fi.x = x;
|
||||||
fi.z = z;
|
fi.z = z;
|
||||||
}
|
}
|
||||||
|
|
||||||
PlayerInventoryItem Lobby::remove_item(uint32_t item_id) {
|
ItemData Lobby::remove_item(uint32_t item_id) {
|
||||||
auto item_it = this->item_id_to_floor_item.find(item_id);
|
auto item_it = this->item_id_to_floor_item.find(item_id);
|
||||||
if (item_it == this->item_id_to_floor_item.end()) {
|
if (item_it == this->item_id_to_floor_item.end()) {
|
||||||
throw out_of_range("item not present");
|
throw out_of_range("item not present");
|
||||||
}
|
}
|
||||||
PlayerInventoryItem ret = std::move(item_it->second.inv_item);
|
ItemData ret = item_it->second.data;
|
||||||
this->item_id_to_floor_item.erase(item_it);
|
this->item_id_to_floor_item.erase(item_it);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-3
@@ -57,7 +57,7 @@ struct Lobby : public std::enable_shared_from_this<Lobby> {
|
|||||||
|
|
||||||
// Item info
|
// Item info
|
||||||
struct FloorItem {
|
struct FloorItem {
|
||||||
PlayerInventoryItem inv_item;
|
ItemData data;
|
||||||
float x;
|
float x;
|
||||||
float z;
|
float z;
|
||||||
uint8_t area;
|
uint8_t area;
|
||||||
@@ -143,8 +143,8 @@ struct Lobby : public std::enable_shared_from_this<Lobby> {
|
|||||||
const std::u16string* identifier = nullptr,
|
const std::u16string* identifier = nullptr,
|
||||||
uint64_t serial_number = 0);
|
uint64_t serial_number = 0);
|
||||||
|
|
||||||
void add_item(const PlayerInventoryItem& item, uint8_t area, float x, float z);
|
void add_item(const ItemData& item, uint8_t area, float x, float z);
|
||||||
PlayerInventoryItem remove_item(uint32_t item_id);
|
ItemData remove_item(uint32_t item_id);
|
||||||
size_t find_item(uint32_t item_id);
|
size_t find_item(uint32_t item_id);
|
||||||
uint32_t generate_item_id(uint8_t client_id);
|
uint32_t generate_item_id(uint8_t client_id);
|
||||||
|
|
||||||
|
|||||||
+78
-17
@@ -213,31 +213,31 @@ void SavedPlayerDataBB::update_to_latest_version() {
|
|||||||
|
|
||||||
// TODO: Eliminate duplication between this function and the parallel function
|
// TODO: Eliminate duplication between this function and the parallel function
|
||||||
// in PlayerBank
|
// in PlayerBank
|
||||||
void SavedPlayerDataBB::add_item(const PlayerInventoryItem& item) {
|
void SavedPlayerDataBB::add_item(const ItemData& item) {
|
||||||
uint32_t pid = item.data.primary_identifier();
|
uint32_t pid = item.primary_identifier();
|
||||||
|
|
||||||
// Annoyingly, meseta is in the disp data, not in the inventory struct. If the
|
// Annoyingly, meseta is in the disp data, not in the inventory struct. If the
|
||||||
// item is meseta, we have to modify disp instead.
|
// item is meseta, we have to modify disp instead.
|
||||||
if (pid == MESETA_IDENTIFIER) {
|
if (pid == MESETA_IDENTIFIER) {
|
||||||
this->add_meseta(item.data.data2d);
|
this->add_meseta(item.data2d);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle combinable items
|
// Handle combinable items
|
||||||
size_t combine_max = item.data.max_stack_size();
|
size_t combine_max = item.max_stack_size();
|
||||||
if (combine_max > 1) {
|
if (combine_max > 1) {
|
||||||
// Get the item index if there's already a stack of the same item in the
|
// Get the item index if there's already a stack of the same item in the
|
||||||
// player's inventory
|
// player's inventory
|
||||||
size_t y;
|
size_t y;
|
||||||
for (y = 0; y < this->inventory.num_items; y++) {
|
for (y = 0; y < this->inventory.num_items; y++) {
|
||||||
if (this->inventory.items[y].data.primary_identifier() == item.data.primary_identifier()) {
|
if (this->inventory.items[y].data.primary_identifier() == item.primary_identifier()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we found an existing stack, add it to the total and return
|
// If we found an existing stack, add it to the total and return
|
||||||
if (y < this->inventory.num_items) {
|
if (y < this->inventory.num_items) {
|
||||||
this->inventory.items[y].data.data1[5] += item.data.data1[5];
|
this->inventory.items[y].data.data1[5] += item.data1[5];
|
||||||
if (this->inventory.items[y].data.data1[5] > combine_max) {
|
if (this->inventory.items[y].data.data1[5] > combine_max) {
|
||||||
this->inventory.items[y].data.data1[5] = combine_max;
|
this->inventory.items[y].data.data1[5] = combine_max;
|
||||||
}
|
}
|
||||||
@@ -250,22 +250,24 @@ void SavedPlayerDataBB::add_item(const PlayerInventoryItem& item) {
|
|||||||
if (this->inventory.num_items >= 30) {
|
if (this->inventory.num_items >= 30) {
|
||||||
throw runtime_error("inventory is full");
|
throw runtime_error("inventory is full");
|
||||||
}
|
}
|
||||||
this->inventory.items[this->inventory.num_items] = item;
|
auto& inv_item = this->inventory.items[this->inventory.num_items];
|
||||||
|
inv_item.present = 1;
|
||||||
|
inv_item.flags = 0;
|
||||||
|
inv_item.data = item;
|
||||||
this->inventory.num_items++;
|
this->inventory.num_items++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Eliminate code duplication between this function and the parallel
|
// TODO: Eliminate code duplication between this function and the parallel
|
||||||
// function in PlayerBank
|
// function in PlayerBank
|
||||||
PlayerInventoryItem SavedPlayerDataBB::remove_item(
|
ItemData SavedPlayerDataBB::remove_item(uint32_t item_id, uint32_t amount, bool allow_meseta_overdraft) {
|
||||||
uint32_t item_id, uint32_t amount, bool allow_meseta_overdraft) {
|
ItemData ret;
|
||||||
PlayerInventoryItem ret;
|
|
||||||
|
|
||||||
// If we're removing meseta (signaled by an invalid item ID), then create a
|
// If we're removing meseta (signaled by an invalid item ID), then create a
|
||||||
// meseta item.
|
// meseta item.
|
||||||
if (item_id == 0xFFFFFFFF) {
|
if (item_id == 0xFFFFFFFF) {
|
||||||
this->remove_meseta(amount, allow_meseta_overdraft);
|
this->remove_meseta(amount, allow_meseta_overdraft);
|
||||||
ret.data.data1[0] = 0x04;
|
ret.data1[0] = 0x04;
|
||||||
ret.data.data2d = amount;
|
ret.data2d = amount;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -278,9 +280,9 @@ PlayerInventoryItem SavedPlayerDataBB::remove_item(
|
|||||||
// applies if amount is nonzero.
|
// applies if amount is nonzero.
|
||||||
if (amount && (inventory_item.data.stack_size() > 1) &&
|
if (amount && (inventory_item.data.stack_size() > 1) &&
|
||||||
(amount < inventory_item.data.data1[5])) {
|
(amount < inventory_item.data.data1[5])) {
|
||||||
ret = inventory_item;
|
ret = inventory_item.data;
|
||||||
ret.data.data1[5] = amount;
|
ret.data1[5] = amount;
|
||||||
ret.data.id = 0xFFFFFFFF;
|
ret.id = 0xFFFFFFFF;
|
||||||
inventory_item.data.data1[5] -= amount;
|
inventory_item.data.data1[5] -= amount;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -288,12 +290,15 @@ PlayerInventoryItem SavedPlayerDataBB::remove_item(
|
|||||||
// If we get here, then it's not meseta, and either it's not a combine item or
|
// If we get here, then it's not meseta, and either it's not a combine item or
|
||||||
// we're removing the entire stack. Delete the item from the inventory slot
|
// we're removing the entire stack. Delete the item from the inventory slot
|
||||||
// and return the deleted item.
|
// and return the deleted item.
|
||||||
ret = inventory_item;
|
ret = inventory_item.data;
|
||||||
this->inventory.num_items--;
|
this->inventory.num_items--;
|
||||||
for (size_t x = index; x < this->inventory.num_items; x++) {
|
for (size_t x = index; x < this->inventory.num_items; x++) {
|
||||||
this->inventory.items[x] = this->inventory.items[x + 1];
|
this->inventory.items[x] = this->inventory.items[x + 1];
|
||||||
}
|
}
|
||||||
this->inventory.items[this->inventory.num_items] = PlayerInventoryItem();
|
auto& last_item = this->inventory.items[this->inventory.num_items];
|
||||||
|
last_item.present = 0;
|
||||||
|
last_item.flags = 0;
|
||||||
|
last_item.data.clear();
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -310,3 +315,59 @@ void SavedPlayerDataBB::remove_meseta(uint32_t amount, bool allow_overdraft) {
|
|||||||
throw out_of_range("player does not have enough meseta");
|
throw out_of_range("player does not have enough meseta");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint8_t SavedPlayerDataBB::get_technique_level(uint8_t which) const {
|
||||||
|
return (this->disp.technique_levels_v1[which] == 0xFF)
|
||||||
|
? 0xFF
|
||||||
|
: (this->disp.technique_levels_v1[which] + this->inventory.items[which].extension_data1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SavedPlayerDataBB::set_technique_level(uint8_t which, uint8_t level) {
|
||||||
|
if (level == 0xFF) {
|
||||||
|
this->disp.technique_levels_v1[which] = 0xFF;
|
||||||
|
this->inventory.items[which].extension_data1 = 0x00;
|
||||||
|
} else if (level <= 0x0E) {
|
||||||
|
this->disp.technique_levels_v1[which] = level;
|
||||||
|
this->inventory.items[which].extension_data1 = 0x00;
|
||||||
|
} else {
|
||||||
|
this->disp.technique_levels_v1[which] = 0x0E;
|
||||||
|
this->inventory.items[which].extension_data1 = level - 0x0E;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t SavedPlayerDataBB::get_material_usage(MaterialType which) const {
|
||||||
|
switch (which) {
|
||||||
|
case MaterialType::HP:
|
||||||
|
return this->inventory.hp_materials_used;
|
||||||
|
case MaterialType::TP:
|
||||||
|
return this->inventory.tp_materials_used;
|
||||||
|
case MaterialType::POWER:
|
||||||
|
case MaterialType::MIND:
|
||||||
|
case MaterialType::EVADE:
|
||||||
|
case MaterialType::DEF:
|
||||||
|
case MaterialType::LUCK:
|
||||||
|
return this->inventory.items[8 + static_cast<uint8_t>(which)].extension_data2;
|
||||||
|
default:
|
||||||
|
throw logic_error("invalid material type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SavedPlayerDataBB::set_material_usage(MaterialType which, uint8_t usage) {
|
||||||
|
switch (which) {
|
||||||
|
case MaterialType::HP:
|
||||||
|
this->inventory.hp_materials_used = usage;
|
||||||
|
break;
|
||||||
|
case MaterialType::TP:
|
||||||
|
this->inventory.tp_materials_used = usage;
|
||||||
|
break;
|
||||||
|
case MaterialType::POWER:
|
||||||
|
case MaterialType::MIND:
|
||||||
|
case MaterialType::EVADE:
|
||||||
|
case MaterialType::DEF:
|
||||||
|
case MaterialType::LUCK:
|
||||||
|
this->inventory.items[8 + static_cast<uint8_t>(which)].extension_data2 = usage;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw logic_error("invalid material type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+19
-4
@@ -48,12 +48,27 @@ struct SavedPlayerDataBB { // .nsc file format
|
|||||||
|
|
||||||
void update_to_latest_version();
|
void update_to_latest_version();
|
||||||
|
|
||||||
void add_item(const PlayerInventoryItem& item);
|
void add_item(const ItemData& item);
|
||||||
PlayerInventoryItem remove_item(
|
ItemData remove_item(uint32_t item_id, uint32_t amount, bool allow_meseta_overdraft);
|
||||||
uint32_t item_id, uint32_t amount, bool allow_meseta_overdraft);
|
|
||||||
void add_meseta(uint32_t amount);
|
void add_meseta(uint32_t amount);
|
||||||
void remove_meseta(uint32_t amount, bool allow_overdraft);
|
void remove_meseta(uint32_t amount, bool allow_overdraft);
|
||||||
|
|
||||||
|
uint8_t get_technique_level(uint8_t which) const; // Returns FF or 00-1D
|
||||||
|
void set_technique_level(uint8_t which, uint8_t level);
|
||||||
|
|
||||||
|
enum class MaterialType : int8_t {
|
||||||
|
HP = -2,
|
||||||
|
TP = -1,
|
||||||
|
POWER = 0,
|
||||||
|
MIND = 1,
|
||||||
|
EVADE = 2,
|
||||||
|
DEF = 3,
|
||||||
|
LUCK = 4,
|
||||||
|
};
|
||||||
|
|
||||||
|
uint8_t get_material_usage(MaterialType which) const;
|
||||||
|
void set_material_usage(MaterialType which, uint8_t usage);
|
||||||
|
|
||||||
void print_inventory(FILE* stream) const;
|
void print_inventory(FILE* stream) const;
|
||||||
} __attribute__((packed));
|
} __attribute__((packed));
|
||||||
|
|
||||||
@@ -95,7 +110,7 @@ public:
|
|||||||
// These are only used if the client is BB
|
// These are only used if the client is BB
|
||||||
std::string bb_username;
|
std::string bb_username;
|
||||||
size_t bb_player_index;
|
size_t bb_player_index;
|
||||||
PlayerInventoryItem identify_result;
|
ItemData identify_result;
|
||||||
std::array<std::vector<ItemData>, 3> shop_contents;
|
std::array<std::vector<ItemData>, 3> shop_contents;
|
||||||
bool should_save;
|
bool should_save;
|
||||||
|
|
||||||
|
|||||||
+24
-54
@@ -43,7 +43,7 @@ PlayerDispDataBB PlayerDispDataDCPCV3::to_bb() const {
|
|||||||
bb.visual.name = " 0";
|
bb.visual.name = " 0";
|
||||||
bb.name = this->visual.name;
|
bb.name = this->visual.name;
|
||||||
bb.config = this->config;
|
bb.config = this->config;
|
||||||
bb.technique_levels = this->v1_technique_levels;
|
bb.technique_levels_v1 = this->technique_levels_v1;
|
||||||
return bb;
|
return bb;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,7 +54,7 @@ PlayerDispDataDCPCV3 PlayerDispDataBB::to_dcpcv3() const {
|
|||||||
ret.visual.name = this->name;
|
ret.visual.name = this->name;
|
||||||
remove_language_marker_inplace(ret.visual.name);
|
remove_language_marker_inplace(ret.visual.name);
|
||||||
ret.config = this->config;
|
ret.config = this->config;
|
||||||
ret.v1_technique_levels = this->technique_levels;
|
ret.technique_levels_v1 = this->technique_levels_v1;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -278,68 +278,34 @@ PlayerRecordsBB_Challenge::operator PlayerRecordsV3_Challenge<false>() const {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
PlayerInventoryItem::PlayerInventoryItem() {
|
|
||||||
this->clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
PlayerInventoryItem::PlayerInventoryItem(const PlayerBankItem& src)
|
|
||||||
: present(1),
|
|
||||||
extension_data1(0),
|
|
||||||
extension_data2(0),
|
|
||||||
flags(0),
|
|
||||||
data(src.data) {}
|
|
||||||
|
|
||||||
void PlayerInventoryItem::clear() {
|
|
||||||
this->present = 0x0000;
|
|
||||||
this->extension_data1 = 0x00;
|
|
||||||
this->extension_data2 = 0x00;
|
|
||||||
this->flags = 0x00000000;
|
|
||||||
this->data.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
PlayerBankItem::PlayerBankItem() {
|
|
||||||
this->clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
PlayerBankItem::PlayerBankItem(const PlayerInventoryItem& src)
|
|
||||||
: data(src.data),
|
|
||||||
amount(this->data.stack_size()),
|
|
||||||
show_flags(1) {}
|
|
||||||
|
|
||||||
void PlayerBankItem::clear() {
|
|
||||||
this->data.clear();
|
|
||||||
this->amount = 0;
|
|
||||||
this->show_flags = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
PlayerInventory::PlayerInventory()
|
PlayerInventory::PlayerInventory()
|
||||||
: num_items(0),
|
: num_items(0),
|
||||||
hp_materials_used(0),
|
hp_materials_used(0),
|
||||||
tp_materials_used(0),
|
tp_materials_used(0),
|
||||||
language(0) {}
|
language(0) {}
|
||||||
|
|
||||||
void PlayerBank::add_item(const PlayerBankItem& item) {
|
void PlayerBank::add_item(const ItemData& item) {
|
||||||
uint32_t pid = item.data.primary_identifier();
|
uint32_t pid = item.primary_identifier();
|
||||||
|
|
||||||
if (pid == MESETA_IDENTIFIER) {
|
if (pid == MESETA_IDENTIFIER) {
|
||||||
this->meseta += item.data.data2d;
|
this->meseta += item.data2d;
|
||||||
if (this->meseta > 999999) {
|
if (this->meseta > 999999) {
|
||||||
this->meseta = 999999;
|
this->meseta = 999999;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t combine_max = item.data.max_stack_size();
|
size_t combine_max = item.max_stack_size();
|
||||||
if (combine_max > 1) {
|
if (combine_max > 1) {
|
||||||
size_t y;
|
size_t y;
|
||||||
for (y = 0; y < this->num_items; y++) {
|
for (y = 0; y < this->num_items; y++) {
|
||||||
if (this->items[y].data.primary_identifier() == item.data.primary_identifier()) {
|
if (this->items[y].data.primary_identifier() == item.primary_identifier()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (y < this->num_items) {
|
if (y < this->num_items) {
|
||||||
this->items[y].data.data1[5] += item.data.data1[5];
|
this->items[y].data.data1[5] += item.data1[5];
|
||||||
if (this->items[y].data.data1[5] > combine_max) {
|
if (this->items[y].data.data1[5] > combine_max) {
|
||||||
this->items[y].data.data1[5] = combine_max;
|
this->items[y].data.data1[5] = combine_max;
|
||||||
}
|
}
|
||||||
@@ -351,19 +317,22 @@ void PlayerBank::add_item(const PlayerBankItem& item) {
|
|||||||
if (this->num_items >= 200) {
|
if (this->num_items >= 200) {
|
||||||
throw runtime_error("bank is full");
|
throw runtime_error("bank is full");
|
||||||
}
|
}
|
||||||
this->items[this->num_items] = item;
|
auto& last_item = this->items[this->num_items];
|
||||||
|
last_item.data = item;
|
||||||
|
last_item.amount = (item.max_stack_size() > 1) ? item.data1[5] : 1;
|
||||||
|
last_item.present = 1;
|
||||||
this->num_items++;
|
this->num_items++;
|
||||||
}
|
}
|
||||||
|
|
||||||
PlayerBankItem PlayerBank::remove_item(uint32_t item_id, uint32_t amount) {
|
ItemData PlayerBank::remove_item(uint32_t item_id, uint32_t amount) {
|
||||||
PlayerBankItem ret;
|
ItemData ret;
|
||||||
|
|
||||||
if (item_id == 0xFFFFFFFF) {
|
if (item_id == 0xFFFFFFFF) {
|
||||||
if (amount > this->meseta) {
|
if (amount > this->meseta) {
|
||||||
throw out_of_range("player does not have enough meseta");
|
throw out_of_range("player does not have enough meseta");
|
||||||
}
|
}
|
||||||
ret.data.data1[0] = 0x04;
|
ret.data1[0] = 0x04;
|
||||||
ret.data.data2d = amount;
|
ret.data2d = amount;
|
||||||
this->meseta -= amount;
|
this->meseta -= amount;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -371,22 +340,23 @@ PlayerBankItem PlayerBank::remove_item(uint32_t item_id, uint32_t amount) {
|
|||||||
size_t index = this->find_item(item_id);
|
size_t index = this->find_item(item_id);
|
||||||
auto& bank_item = this->items[index];
|
auto& bank_item = this->items[index];
|
||||||
|
|
||||||
if (amount && (bank_item.data.stack_size() > 1) &&
|
if (amount && (bank_item.data.stack_size() > 1) && (amount < bank_item.data.data1[5])) {
|
||||||
(amount < bank_item.data.data1[5])) {
|
ret = bank_item.data;
|
||||||
ret = bank_item;
|
ret.data1[5] = amount;
|
||||||
ret.data.data1[5] = amount;
|
|
||||||
ret.amount = amount;
|
|
||||||
bank_item.data.data1[5] -= amount;
|
bank_item.data.data1[5] -= amount;
|
||||||
bank_item.amount -= amount;
|
bank_item.amount -= amount;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = bank_item;
|
ret = bank_item.data;
|
||||||
this->num_items--;
|
this->num_items--;
|
||||||
for (size_t x = index; x < this->num_items; x++) {
|
for (size_t x = index; x < this->num_items; x++) {
|
||||||
this->items[x] = this->items[x + 1];
|
this->items[x] = this->items[x + 1];
|
||||||
}
|
}
|
||||||
this->items[this->num_items] = PlayerBankItem();
|
auto& last_item = this->items[this->num_items];
|
||||||
|
last_item.amount = 0;
|
||||||
|
last_item.present = 0;
|
||||||
|
last_item.data.clear();
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+30
-36
@@ -23,10 +23,10 @@ extern FileContentsCache player_files_cache;
|
|||||||
// stores arrays of bytes striped across these structures. In newserv, we call
|
// stores arrays of bytes striped across these structures. In newserv, we call
|
||||||
// those fields extension_data. They contain:
|
// those fields extension_data. They contain:
|
||||||
// items[0].extension_data1 through items[19].extension_data1:
|
// items[0].extension_data1 through items[19].extension_data1:
|
||||||
// Extended technique levels. The values in the v1_technique_levels array
|
// Extended technique levels. The values in the technique_levels_v1 array
|
||||||
// only go up to 14 (tech level 15); if the player has a technique above
|
// only go up to 14 (tech level 15); if the player has a technique above
|
||||||
// level 15, the corresponding extension_data1 field holds the remaining
|
// level 15, the corresponding extension_data1 field holds the remaining
|
||||||
// levels (so a level 20 tech would have 14 in v1_technique_levels and 5
|
// levels (so a level 20 tech would have 14 in technique_levels_v1 and 5
|
||||||
// in the corresponding item's extension_data1 field).
|
// in the corresponding item's extension_data1 field).
|
||||||
// items[0].extension_data2 through items[3].extension_data2:
|
// items[0].extension_data2 through items[3].extension_data2:
|
||||||
// The value known as unknown_a1 in the PSOGCCharacterFile::Character
|
// The value known as unknown_a1 in the PSOGCCharacterFile::Character
|
||||||
@@ -40,37 +40,30 @@ extern FileContentsCache player_files_cache;
|
|||||||
// items[13].extension_data2 through items[15].extension_data2:
|
// items[13].extension_data2 through items[15].extension_data2:
|
||||||
// Unknown. These are not an array, but do appear to be related.
|
// Unknown. These are not an array, but do appear to be related.
|
||||||
|
|
||||||
struct PlayerBankItem;
|
struct PlayerInventoryItem {
|
||||||
|
/* 00 */ le_uint16_t present = 0;
|
||||||
struct PlayerInventoryItem { // 0x1C bytes
|
|
||||||
le_uint16_t present;
|
|
||||||
// See note above about these fields
|
// See note above about these fields
|
||||||
uint8_t extension_data1;
|
/* 02 */ uint8_t extension_data1 = 0;
|
||||||
uint8_t extension_data2;
|
/* 03 */ uint8_t extension_data2 = 0;
|
||||||
le_uint32_t flags; // 8 = equipped
|
/* 04 */ le_uint32_t flags = 0; // 8 = equipped
|
||||||
ItemData data;
|
/* 08 */ ItemData data;
|
||||||
|
/* 1C */
|
||||||
PlayerInventoryItem();
|
|
||||||
PlayerInventoryItem(const PlayerBankItem&);
|
|
||||||
void clear();
|
|
||||||
} __attribute__((packed));
|
} __attribute__((packed));
|
||||||
|
|
||||||
struct PlayerBankItem { // 0x18 bytes
|
struct PlayerBankItem {
|
||||||
ItemData data;
|
/* 00 */ ItemData data;
|
||||||
le_uint16_t amount;
|
/* 14 */ le_uint16_t amount = 0;
|
||||||
le_uint16_t show_flags;
|
/* 16 */ le_uint16_t present = 0;
|
||||||
|
/* 18 */
|
||||||
PlayerBankItem();
|
|
||||||
PlayerBankItem(const PlayerInventoryItem&);
|
|
||||||
void clear();
|
|
||||||
} __attribute__((packed));
|
} __attribute__((packed));
|
||||||
|
|
||||||
struct PlayerInventory { // 0x34C bytes
|
struct PlayerInventory {
|
||||||
uint8_t num_items;
|
/* 0000 */ uint8_t num_items = 0;
|
||||||
uint8_t hp_materials_used;
|
/* 0001 */ uint8_t hp_materials_used = 0;
|
||||||
uint8_t tp_materials_used;
|
/* 0002 */ uint8_t tp_materials_used = 0;
|
||||||
uint8_t language;
|
/* 0003 */ uint8_t language = 1; // English
|
||||||
PlayerInventoryItem items[30];
|
/* 0004 */ parray<PlayerInventoryItem, 30> items;
|
||||||
|
/* 034C */
|
||||||
|
|
||||||
PlayerInventory();
|
PlayerInventory();
|
||||||
|
|
||||||
@@ -81,10 +74,11 @@ struct PlayerInventory { // 0x34C bytes
|
|||||||
size_t find_equipped_mag() const;
|
size_t find_equipped_mag() const;
|
||||||
} __attribute__((packed));
|
} __attribute__((packed));
|
||||||
|
|
||||||
struct PlayerBank { // 0x12C8 bytes
|
struct PlayerBank {
|
||||||
le_uint32_t num_items;
|
/* 0000 */ le_uint32_t num_items = 0;
|
||||||
le_uint32_t meseta;
|
/* 0004 */ le_uint32_t meseta = 0;
|
||||||
PlayerBankItem items[200];
|
/* 0008 */ parray<PlayerBankItem, 200> items;
|
||||||
|
/* 12C8 */
|
||||||
|
|
||||||
void load(const std::string& filename);
|
void load(const std::string& filename);
|
||||||
void save(const std::string& filename, bool save_to_filesystem) const;
|
void save(const std::string& filename, bool save_to_filesystem) const;
|
||||||
@@ -92,8 +86,8 @@ struct PlayerBank { // 0x12C8 bytes
|
|||||||
bool switch_with_file(const std::string& save_filename,
|
bool switch_with_file(const std::string& save_filename,
|
||||||
const std::string& load_filename);
|
const std::string& load_filename);
|
||||||
|
|
||||||
void add_item(const PlayerBankItem& item);
|
void add_item(const ItemData& item);
|
||||||
PlayerBankItem remove_item(uint32_t item_id, uint32_t amount);
|
ItemData remove_item(uint32_t item_id, uint32_t amount);
|
||||||
size_t find_item(uint32_t item_id);
|
size_t find_item(uint32_t item_id);
|
||||||
} __attribute__((packed));
|
} __attribute__((packed));
|
||||||
|
|
||||||
@@ -139,7 +133,7 @@ struct PlayerDispDataDCPCV3 {
|
|||||||
/* 00 */ PlayerStats stats;
|
/* 00 */ PlayerStats stats;
|
||||||
/* 24 */ PlayerVisualConfig visual;
|
/* 24 */ PlayerVisualConfig visual;
|
||||||
/* 74 */ parray<uint8_t, 0x48> config;
|
/* 74 */ parray<uint8_t, 0x48> config;
|
||||||
/* BC */ parray<uint8_t, 0x14> v1_technique_levels;
|
/* BC */ parray<uint8_t, 0x14> technique_levels_v1;
|
||||||
/* D0 */
|
/* D0 */
|
||||||
void enforce_v2_limits();
|
void enforce_v2_limits();
|
||||||
PlayerDispDataBB to_bb() const;
|
PlayerDispDataBB to_bb() const;
|
||||||
@@ -164,7 +158,7 @@ struct PlayerDispDataBB {
|
|||||||
/* 008C */ le_uint32_t play_time = 0;
|
/* 008C */ le_uint32_t play_time = 0;
|
||||||
/* 0090 */ uint32_t unknown_a3 = 0;
|
/* 0090 */ uint32_t unknown_a3 = 0;
|
||||||
/* 0094 */ parray<uint8_t, 0xE8> config;
|
/* 0094 */ parray<uint8_t, 0xE8> config;
|
||||||
/* 017C */ parray<uint8_t, 0x14> technique_levels;
|
/* 017C */ parray<uint8_t, 0x14> technique_levels_v1;
|
||||||
/* 0190 */
|
/* 0190 */
|
||||||
|
|
||||||
inline void enforce_v2_limits() {}
|
inline void enforce_v2_limits() {}
|
||||||
|
|||||||
+8
-16
@@ -946,16 +946,12 @@ static HandlerResult S_6x(shared_ptr<ProxyServer::LinkedSession> ses, uint16_t,
|
|||||||
return HandlerResult::Type::SUPPRESS;
|
return HandlerResult::Type::SUPPRESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if ((data[0] == 0x60) &&
|
} else if ((data[0] == 0x60) && ses->next_drop_item.data1d[0] && (ses->version != GameVersion::BB)) {
|
||||||
ses->next_drop_item.data.data1d[0] &&
|
|
||||||
(ses->version != GameVersion::BB)) {
|
|
||||||
const auto& cmd = check_size_t<G_StandardDropItemRequest_DC_6x60>(
|
const auto& cmd = check_size_t<G_StandardDropItemRequest_DC_6x60>(
|
||||||
data, sizeof(G_StandardDropItemRequest_PC_V3_BB_6x60));
|
data, sizeof(G_StandardDropItemRequest_PC_V3_BB_6x60));
|
||||||
ses->next_drop_item.data.id = ses->next_item_id++;
|
ses->next_drop_item.id = ses->next_item_id++;
|
||||||
send_drop_item(ses->server_channel, ses->next_drop_item.data,
|
send_drop_item(ses->server_channel, ses->next_drop_item, true, cmd.area, cmd.x, cmd.z, cmd.entity_id);
|
||||||
true, cmd.area, cmd.x, cmd.z, cmd.entity_id);
|
send_drop_item(ses->client_channel, ses->next_drop_item, true, cmd.area, cmd.x, cmd.z, cmd.entity_id);
|
||||||
send_drop_item(ses->client_channel, ses->next_drop_item.data,
|
|
||||||
true, cmd.area, cmd.x, cmd.z, cmd.entity_id);
|
|
||||||
ses->next_drop_item.clear();
|
ses->next_drop_item.clear();
|
||||||
return HandlerResult::Type::SUPPRESS;
|
return HandlerResult::Type::SUPPRESS;
|
||||||
|
|
||||||
@@ -963,15 +959,11 @@ static HandlerResult S_6x(shared_ptr<ProxyServer::LinkedSession> ses, uint16_t,
|
|||||||
// the comparison is always false (which even happens in some environments
|
// the comparison is always false (which even happens in some environments
|
||||||
// if we use -0x5E... apparently char is unsigned on some systems, or
|
// if we use -0x5E... apparently char is unsigned on some systems, or
|
||||||
// std::string's char_type isn't char??)
|
// std::string's char_type isn't char??)
|
||||||
} else if ((static_cast<uint8_t>(data[0]) == 0xA2) &&
|
} else if ((static_cast<uint8_t>(data[0]) == 0xA2) && ses->next_drop_item.data1d[0] && (ses->version != GameVersion::BB)) {
|
||||||
ses->next_drop_item.data.data1d[0] &&
|
|
||||||
(ses->version != GameVersion::BB)) {
|
|
||||||
const auto& cmd = check_size_t<G_SpecializableItemDropRequest_6xA2>(data);
|
const auto& cmd = check_size_t<G_SpecializableItemDropRequest_6xA2>(data);
|
||||||
ses->next_drop_item.data.id = ses->next_item_id++;
|
ses->next_drop_item.id = ses->next_item_id++;
|
||||||
send_drop_item(ses->server_channel, ses->next_drop_item.data,
|
send_drop_item(ses->server_channel, ses->next_drop_item, false, cmd.area, cmd.x, cmd.z, cmd.entity_id);
|
||||||
false, cmd.area, cmd.x, cmd.z, cmd.entity_id);
|
send_drop_item(ses->client_channel, ses->next_drop_item, false, cmd.area, cmd.x, cmd.z, cmd.entity_id);
|
||||||
send_drop_item(ses->client_channel, ses->next_drop_item.data,
|
|
||||||
false, cmd.area, cmd.x, cmd.z, cmd.entity_id);
|
|
||||||
ses->next_drop_item.clear();
|
ses->next_drop_item.clear();
|
||||||
return HandlerResult::Type::SUPPRESS;
|
return HandlerResult::Type::SUPPRESS;
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -73,7 +73,7 @@ public:
|
|||||||
// A null handler in here means to forward the response to the remote server
|
// A null handler in here means to forward the response to the remote server
|
||||||
std::deque<std::function<void(uint32_t return_value, uint32_t checksum)>> function_call_return_handler_queue;
|
std::deque<std::function<void(uint32_t return_value, uint32_t checksum)>> function_call_return_handler_queue;
|
||||||
G_SwitchStateChanged_6x05 last_switch_enabled_command;
|
G_SwitchStateChanged_6x05 last_switch_enabled_command;
|
||||||
PlayerInventoryItem next_drop_item;
|
ItemData next_drop_item;
|
||||||
uint32_t next_item_id;
|
uint32_t next_item_id;
|
||||||
|
|
||||||
struct LobbyPlayer {
|
struct LobbyPlayer {
|
||||||
|
|||||||
+95
-109
@@ -606,12 +606,12 @@ static void on_player_drop_item(shared_ptr<Client> c, uint8_t command, uint8_t f
|
|||||||
auto item = c->game_data.player()->remove_item(cmd.item_id, 0, c->version() != GameVersion::BB);
|
auto item = c->game_data.player()->remove_item(cmd.item_id, 0, c->version() != GameVersion::BB);
|
||||||
l->add_item(item, cmd.area, cmd.x, cmd.z);
|
l->add_item(item, cmd.area, cmd.x, cmd.z);
|
||||||
|
|
||||||
auto name = item.data.name(false);
|
auto name = item.name(false);
|
||||||
l->log.info("Player %hu dropped item %08" PRIX32 " (%s) at %hu:(%g, %g)",
|
l->log.info("Player %hu dropped item %08" PRIX32 " (%s) at %hu:(%g, %g)",
|
||||||
cmd.header.client_id.load(), cmd.item_id.load(), name.c_str(),
|
cmd.header.client_id.load(), cmd.item_id.load(), name.c_str(),
|
||||||
cmd.area.load(), cmd.x.load(), cmd.z.load());
|
cmd.area.load(), cmd.x.load(), cmd.z.load());
|
||||||
if (c->options.debug) {
|
if (c->options.debug) {
|
||||||
string name = item.data.name(true);
|
string name = item.name(true);
|
||||||
send_text_message_printf(c, "$C5DROP %08" PRIX32 "\n%s",
|
send_text_message_printf(c, "$C5DROP %08" PRIX32 "\n%s",
|
||||||
cmd.item_id.load(), name.c_str());
|
cmd.item_id.load(), name.c_str());
|
||||||
}
|
}
|
||||||
@@ -657,22 +657,20 @@ static void on_create_inventory_item_t(shared_ptr<Client> c, uint8_t command, ui
|
|||||||
|
|
||||||
auto l = c->require_lobby();
|
auto l = c->require_lobby();
|
||||||
if (l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED) {
|
if (l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED) {
|
||||||
PlayerInventoryItem item;
|
{
|
||||||
item.present = 1;
|
ItemData item = cmd.item_data;
|
||||||
item.flags = 0;
|
if (c->version() == GameVersion::GC) {
|
||||||
item.data = cmd.item_data;
|
item.bswap_data2_if_mag();
|
||||||
if (c->version() == GameVersion::GC) {
|
}
|
||||||
item.data.bswap_data2_if_mag();
|
c->game_data.player()->add_item(item);
|
||||||
}
|
}
|
||||||
c->game_data.player()->add_item(item);
|
|
||||||
|
|
||||||
auto name = item.data.name(false);
|
auto name = cmd.item_data.name(false);
|
||||||
l->log.info("Player %hu created inventory item %08" PRIX32 " (%s)",
|
l->log.info("Player %hu created inventory item %08" PRIX32 " (%s)",
|
||||||
cmd.header.client_id.load(), cmd.item_data.id.load(), name.c_str());
|
cmd.header.client_id.load(), cmd.item_data.id.load(), name.c_str());
|
||||||
if (c->options.debug) {
|
if (c->options.debug) {
|
||||||
string name = item.data.name(true);
|
string name = cmd.item_data.name(true);
|
||||||
send_text_message_printf(c, "$C5CREATE %08" PRIX32 "\n%s",
|
send_text_message_printf(c, "$C5CREATE %08" PRIX32 "\n%s", cmd.item_data.id.load(), name.c_str());
|
||||||
cmd.item_data.id.load(), name.c_str());
|
|
||||||
}
|
}
|
||||||
c->game_data.player()->print_inventory(stderr);
|
c->game_data.player()->print_inventory(stderr);
|
||||||
}
|
}
|
||||||
@@ -706,23 +704,21 @@ static void on_drop_partial_stack_t(shared_ptr<Client> c, uint8_t command, uint8
|
|||||||
if (l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED) {
|
if (l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED) {
|
||||||
// TODO: Should we delete anything from the inventory here? Does the client
|
// TODO: Should we delete anything from the inventory here? Does the client
|
||||||
// send an appropriate 6x29 alongside this?
|
// send an appropriate 6x29 alongside this?
|
||||||
PlayerInventoryItem item;
|
{
|
||||||
item.present = 1;
|
ItemData item = cmd.item_data;
|
||||||
item.flags = 0;
|
if (c->version() == GameVersion::GC) {
|
||||||
item.data = cmd.item_data;
|
item.bswap_data2_if_mag();
|
||||||
if (c->version() == GameVersion::GC) {
|
}
|
||||||
item.data.bswap_data2_if_mag();
|
l->add_item(item, cmd.area, cmd.x, cmd.z);
|
||||||
}
|
}
|
||||||
l->add_item(item, cmd.area, cmd.x, cmd.z);
|
|
||||||
|
|
||||||
auto name = item.data.name(false);
|
auto name = cmd.item_data.name(false);
|
||||||
l->log.info("Player %hu split stack to create ground item %08" PRIX32 " (%s) at %hu:(%g, %g)",
|
l->log.info("Player %hu split stack to create ground item %08" PRIX32 " (%s) at %hu:(%g, %g)",
|
||||||
cmd.header.client_id.load(), item.data.id.load(), name.c_str(),
|
cmd.header.client_id.load(), cmd.item_data.id.load(), name.c_str(),
|
||||||
cmd.area.load(), cmd.x.load(), cmd.z.load());
|
cmd.area.load(), cmd.x.load(), cmd.z.load());
|
||||||
if (c->options.debug) {
|
if (c->options.debug) {
|
||||||
string name = item.data.name(true);
|
string name = cmd.item_data.name(true);
|
||||||
send_text_message_printf(c, "$C5SPLIT %08" PRIX32 "\n%s",
|
send_text_message_printf(c, "$C5SPLIT %08" PRIX32 "\n%s", cmd.item_data.id.load(), name.c_str());
|
||||||
item.data.id.load(), name.c_str());
|
|
||||||
}
|
}
|
||||||
c->game_data.player()->print_inventory(stderr);
|
c->game_data.player()->print_inventory(stderr);
|
||||||
}
|
}
|
||||||
@@ -758,8 +754,8 @@ static void on_drop_partial_stack_bb(shared_ptr<Client> c, uint8_t command, uint
|
|||||||
|
|
||||||
// if a stack was split, the original item still exists, so the dropped item
|
// if a stack was split, the original item still exists, so the dropped item
|
||||||
// needs a new ID. remove_item signals this by returning an item with id=-1
|
// needs a new ID. remove_item signals this by returning an item with id=-1
|
||||||
if (item.data.id == 0xFFFFFFFF) {
|
if (item.id == 0xFFFFFFFF) {
|
||||||
item.data.id = l->generate_item_id(c->lobby_client_id);
|
item.id = l->generate_item_id(c->lobby_client_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
// PSOBB sends a 6x29 command after it receives the 6x5D, so we need to add
|
// PSOBB sends a 6x29 command after it receives the 6x5D, so we need to add
|
||||||
@@ -769,18 +765,18 @@ static void on_drop_partial_stack_bb(shared_ptr<Client> c, uint8_t command, uint
|
|||||||
|
|
||||||
l->add_item(item, cmd.area, cmd.x, cmd.z);
|
l->add_item(item, cmd.area, cmd.x, cmd.z);
|
||||||
|
|
||||||
auto name = item.data.name(false);
|
auto name = item.name(false);
|
||||||
l->log.info("Player %hu split stack %08" PRIX32 " (removed: %s) at %hu:(%g, %g)",
|
l->log.info("Player %hu split stack %08" PRIX32 " (removed: %s) at %hu:(%g, %g)",
|
||||||
cmd.header.client_id.load(), cmd.item_id.load(), name.c_str(),
|
cmd.header.client_id.load(), cmd.item_id.load(), name.c_str(),
|
||||||
cmd.area.load(), cmd.x.load(), cmd.z.load());
|
cmd.area.load(), cmd.x.load(), cmd.z.load());
|
||||||
if (c->options.debug) {
|
if (c->options.debug) {
|
||||||
string name = item.data.name(true);
|
string name = item.name(true);
|
||||||
send_text_message_printf(c, "$C5SPLIT/BB %08" PRIX32 "\n%s",
|
send_text_message_printf(c, "$C5SPLIT/BB %08" PRIX32 "\n%s",
|
||||||
cmd.item_id.load(), name.c_str());
|
cmd.item_id.load(), name.c_str());
|
||||||
}
|
}
|
||||||
c->game_data.player()->print_inventory(stderr);
|
c->game_data.player()->print_inventory(stderr);
|
||||||
|
|
||||||
send_drop_stacked_item(l, item.data, cmd.area, cmd.x, cmd.z);
|
send_drop_stacked_item(l, item, cmd.area, cmd.x, cmd.z);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
forward_subcommand(c, command, flag, data, size);
|
forward_subcommand(c, command, flag, data, size);
|
||||||
@@ -800,25 +796,22 @@ static void on_buy_shop_item(shared_ptr<Client> c, uint8_t command, uint8_t flag
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED) {
|
if (l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED) {
|
||||||
PlayerInventoryItem item;
|
auto p = c->game_data.player();
|
||||||
item.present = 1;
|
{
|
||||||
item.flags = 0;
|
ItemData item = cmd.item_data;
|
||||||
item.data = cmd.item_data;
|
if (c->version() == GameVersion::GC) {
|
||||||
if (c->version() == GameVersion::GC) {
|
item.bswap_data2_if_mag();
|
||||||
item.data.bswap_data2_if_mag();
|
}
|
||||||
|
p->add_item(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto p = c->game_data.player();
|
size_t price = s->item_parameter_table->price_for_item(cmd.item_data);
|
||||||
p->add_item(item);
|
auto name = cmd.item_data.name(false);
|
||||||
|
|
||||||
size_t price = s->item_parameter_table->price_for_item(item.data);
|
|
||||||
auto name = item.data.name(false);
|
|
||||||
l->log.info("Player %hu bought item %08" PRIX32 " (%s) from shop (%zu Meseta)",
|
l->log.info("Player %hu bought item %08" PRIX32 " (%s) from shop (%zu Meseta)",
|
||||||
cmd.header.client_id.load(), item.data.id.load(), name.c_str(), price);
|
cmd.header.client_id.load(), cmd.item_data.id.load(), name.c_str(), price);
|
||||||
if (c->options.debug) {
|
if (c->options.debug) {
|
||||||
string name = item.data.name(true);
|
string name = cmd.item_data.name(true);
|
||||||
send_text_message_printf(c, "$C5BUY %08" PRIX32 "\n%s",
|
send_text_message_printf(c, "$C5BUY %08" PRIX32 "\n%s", cmd.item_data.id.load(), name.c_str());
|
||||||
item.data.id.load(), name.c_str());
|
|
||||||
}
|
}
|
||||||
p->remove_meseta(price, c->version() != GameVersion::BB);
|
p->remove_meseta(price, c->version() != GameVersion::BB);
|
||||||
p->print_inventory(stderr);
|
p->print_inventory(stderr);
|
||||||
@@ -840,22 +833,20 @@ static void on_box_or_enemy_item_drop_t(shared_ptr<Client> c, uint8_t command, u
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED) {
|
if (l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED) {
|
||||||
PlayerInventoryItem item;
|
{
|
||||||
item.present = 1;
|
ItemData item = cmd.item_data;
|
||||||
item.flags = 0;
|
if (c->version() == GameVersion::GC) {
|
||||||
item.data = cmd.item_data;
|
item.bswap_data2_if_mag();
|
||||||
if (c->version() == GameVersion::GC) {
|
}
|
||||||
item.data.bswap_data2_if_mag();
|
l->add_item(item, cmd.area, cmd.x, cmd.z);
|
||||||
}
|
}
|
||||||
l->add_item(item, cmd.area, cmd.x, cmd.z);
|
|
||||||
|
|
||||||
auto name = item.data.name(false);
|
auto name = cmd.item_data.name(false);
|
||||||
l->log.info("Leader created ground item %08" PRIX32 " (%s) at %hhu:(%g, %g)",
|
l->log.info("Leader created ground item %08" PRIX32 " (%s) at %hhu:(%g, %g)",
|
||||||
item.data.id.load(), name.c_str(), cmd.area, cmd.x.load(), cmd.z.load());
|
cmd.item_data.id.load(), name.c_str(), cmd.area, cmd.x.load(), cmd.z.load());
|
||||||
if (c->options.debug) {
|
if (c->options.debug) {
|
||||||
string name = item.data.name(true);
|
string name = cmd.item_data.name(true);
|
||||||
send_text_message_printf(c, "$C5DROP %08" PRIX32 "\n%s",
|
send_text_message_printf(c, "$C5DROP %08" PRIX32 "\n%s", cmd.item_data.id.load(), name.c_str());
|
||||||
item.data.id.load(), name.c_str());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -893,13 +884,12 @@ static void on_pick_up_item(shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
|||||||
auto item = l->remove_item(cmd.item_id);
|
auto item = l->remove_item(cmd.item_id);
|
||||||
effective_c->game_data.player()->add_item(item);
|
effective_c->game_data.player()->add_item(item);
|
||||||
|
|
||||||
auto name = item.data.name(false);
|
auto name = item.name(false);
|
||||||
l->log.info("Player %hu picked up %08" PRIX32 " (%s)",
|
l->log.info("Player %hu picked up %08" PRIX32 " (%s)",
|
||||||
cmd.header.client_id.load(), cmd.item_id.load(), name.c_str());
|
cmd.header.client_id.load(), cmd.item_id.load(), name.c_str());
|
||||||
if (c->options.debug) {
|
if (c->options.debug) {
|
||||||
string name = item.data.name(true);
|
string name = item.name(true);
|
||||||
send_text_message_printf(c, "$C5PICK %08" PRIX32 "\n%s",
|
send_text_message_printf(c, "$C5PICK %08" PRIX32 "\n%s", cmd.item_id.load(), name.c_str());
|
||||||
cmd.item_id.load(), name.c_str());
|
|
||||||
}
|
}
|
||||||
effective_c->game_data.player()->print_inventory(stderr);
|
effective_c->game_data.player()->print_inventory(stderr);
|
||||||
}
|
}
|
||||||
@@ -924,11 +914,11 @@ static void on_pick_up_item_request(shared_ptr<Client> c, uint8_t command, uint8
|
|||||||
auto item = l->remove_item(cmd.item_id);
|
auto item = l->remove_item(cmd.item_id);
|
||||||
c->game_data.player()->add_item(item);
|
c->game_data.player()->add_item(item);
|
||||||
|
|
||||||
auto name = item.data.name(false);
|
auto name = item.name(false);
|
||||||
l->log.info("Player %hu picked up %08" PRIX32 " (%s)",
|
l->log.info("Player %hu picked up %08" PRIX32 " (%s)",
|
||||||
cmd.header.client_id.load(), cmd.item_id.load(), name.c_str());
|
cmd.header.client_id.load(), cmd.item_id.load(), name.c_str());
|
||||||
if (c->options.debug) {
|
if (c->options.debug) {
|
||||||
string name = item.data.name(true);
|
string name = item.name(true);
|
||||||
send_text_message_printf(c, "$C5PICK/BB %08" PRIX32 "\n%s",
|
send_text_message_printf(c, "$C5PICK/BB %08" PRIX32 "\n%s",
|
||||||
cmd.item_id.load(), name.c_str());
|
cmd.item_id.load(), name.c_str());
|
||||||
}
|
}
|
||||||
@@ -1122,8 +1112,7 @@ static void on_ep3_private_word_select_bb_bank_action(shared_ptr<Client> c, uint
|
|||||||
c->game_data.player()->bank.meseta += cmd.meseta_amount;
|
c->game_data.player()->bank.meseta += cmd.meseta_amount;
|
||||||
c->game_data.player()->disp.stats.meseta -= cmd.meseta_amount;
|
c->game_data.player()->disp.stats.meseta -= cmd.meseta_amount;
|
||||||
} else { // item
|
} else { // item
|
||||||
auto item = c->game_data.player()->remove_item(
|
auto item = c->game_data.player()->remove_item(cmd.item_id, cmd.item_amount, c->version() != GameVersion::BB);
|
||||||
cmd.item_id, cmd.item_amount, c->version() != GameVersion::BB);
|
|
||||||
c->game_data.player()->bank.add_item(item);
|
c->game_data.player()->bank.add_item(item);
|
||||||
send_destroy_item(c, cmd.item_id, cmd.item_amount);
|
send_destroy_item(c, cmd.item_id, cmd.item_amount);
|
||||||
}
|
}
|
||||||
@@ -1138,11 +1127,10 @@ static void on_ep3_private_word_select_bb_bank_action(shared_ptr<Client> c, uint
|
|||||||
c->game_data.player()->bank.meseta -= cmd.meseta_amount;
|
c->game_data.player()->bank.meseta -= cmd.meseta_amount;
|
||||||
c->game_data.player()->disp.stats.meseta += cmd.meseta_amount;
|
c->game_data.player()->disp.stats.meseta += cmd.meseta_amount;
|
||||||
} else { // item
|
} else { // item
|
||||||
auto bank_item = c->game_data.player()->bank.remove_item(cmd.item_id, cmd.item_amount);
|
auto item = c->game_data.player()->bank.remove_item(cmd.item_id, cmd.item_amount);
|
||||||
PlayerInventoryItem item = bank_item;
|
item.id = l->generate_item_id(0xFF);
|
||||||
item.data.id = l->generate_item_id(0xFF);
|
|
||||||
c->game_data.player()->add_item(item);
|
c->game_data.player()->add_item(item);
|
||||||
send_create_inventory_item(c, item.data);
|
send_create_inventory_item(c, item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1211,17 +1199,16 @@ static void on_entity_drop_item_request(shared_ptr<Client> c, uint8_t command, u
|
|||||||
cmd.ignore_def = true;
|
cmd.ignore_def = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
PlayerInventoryItem item;
|
|
||||||
if (!l->item_creator.get()) {
|
if (!l->item_creator.get()) {
|
||||||
throw runtime_error("received box drop subcommand without item creator present");
|
throw runtime_error("received box drop subcommand without item creator present");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ItemData item;
|
||||||
if (cmd.rt_index == 0x30) {
|
if (cmd.rt_index == 0x30) {
|
||||||
if (cmd.ignore_def) {
|
if (cmd.ignore_def) {
|
||||||
item.data = l->item_creator->on_box_item_drop(cmd.area);
|
item = l->item_creator->on_box_item_drop(cmd.area);
|
||||||
} else {
|
} else {
|
||||||
item.data = l->item_creator->on_specialized_box_item_drop(
|
item = l->item_creator->on_specialized_box_item_drop(cmd.def[0], cmd.def[1], cmd.def[2]);
|
||||||
cmd.def[0], cmd.def[1], cmd.def[2]);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!l->map) {
|
if (!l->map) {
|
||||||
@@ -1233,14 +1220,14 @@ static void on_entity_drop_item_request(shared_ptr<Client> c, uint8_t command, u
|
|||||||
c->log.warning("rt_index %02hhX from command does not match entity\'s expected index %02" PRIX32,
|
c->log.warning("rt_index %02hhX from command does not match entity\'s expected index %02" PRIX32,
|
||||||
cmd.rt_index, expected_rt_index);
|
cmd.rt_index, expected_rt_index);
|
||||||
}
|
}
|
||||||
item.data = l->item_creator->on_monster_item_drop(expected_rt_index, cmd.area);
|
item = l->item_creator->on_monster_item_drop(expected_rt_index, cmd.area);
|
||||||
}
|
}
|
||||||
item.data.id = l->generate_item_id(0xFF);
|
item.id = l->generate_item_id(0xFF);
|
||||||
|
|
||||||
if (l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED) {
|
if (l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED) {
|
||||||
l->add_item(item, cmd.area, cmd.x, cmd.z);
|
l->add_item(item, cmd.area, cmd.x, cmd.z);
|
||||||
}
|
}
|
||||||
send_drop_item(l, item.data, cmd.rt_index != 0x30, cmd.area, cmd.x, cmd.z, cmd.entity_id);
|
send_drop_item(l, item, cmd.rt_index != 0x30, cmd.area, cmd.x, cmd.z, cmd.entity_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void on_set_quest_flag(shared_ptr<Client> c, uint8_t command, uint8_t flag, const void* data, size_t size) {
|
static void on_set_quest_flag(shared_ptr<Client> c, uint8_t command, uint8_t flag, const void* data, size_t size) {
|
||||||
@@ -1516,12 +1503,12 @@ void on_meseta_reward_request_bb(shared_ptr<Client> c, uint8_t, uint8_t, const v
|
|||||||
} else if (cmd.amount > 0) {
|
} else if (cmd.amount > 0) {
|
||||||
auto l = c->require_lobby();
|
auto l = c->require_lobby();
|
||||||
|
|
||||||
PlayerInventoryItem item;
|
ItemData item;
|
||||||
item.data.data1[0] = 0x04;
|
item.data1[0] = 0x04;
|
||||||
item.data.data2d = cmd.amount.load();
|
item.data2d = cmd.amount.load();
|
||||||
item.data.id = l->generate_item_id(0xFF);
|
item.id = l->generate_item_id(0xFF);
|
||||||
c->game_data.player()->add_item(item);
|
c->game_data.player()->add_item(item);
|
||||||
send_create_inventory_item(c, item.data);
|
send_create_inventory_item(c, item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1529,11 +1516,11 @@ void on_item_reward_request_bb(shared_ptr<Client> c, uint8_t, uint8_t, const voi
|
|||||||
const auto& cmd = check_size_t<G_ItemRewardRequest_BB_6xCA>(data, size);
|
const auto& cmd = check_size_t<G_ItemRewardRequest_BB_6xCA>(data, size);
|
||||||
auto l = c->require_lobby();
|
auto l = c->require_lobby();
|
||||||
|
|
||||||
PlayerInventoryItem item;
|
ItemData item;
|
||||||
item.data = cmd.item_data;
|
item = cmd.item_data;
|
||||||
item.data.id = l->generate_item_id(0xFF);
|
item.id = l->generate_item_id(0xFF);
|
||||||
c->game_data.player()->add_item(item);
|
c->game_data.player()->add_item(item);
|
||||||
send_create_inventory_item(c, item.data);
|
send_create_inventory_item(c, item);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void on_destroy_inventory_item(shared_ptr<Client> c, uint8_t command, uint8_t flag, const void* data, size_t size) {
|
static void on_destroy_inventory_item(shared_ptr<Client> c, uint8_t command, uint8_t flag, const void* data, size_t size) {
|
||||||
@@ -1550,11 +1537,11 @@ static void on_destroy_inventory_item(shared_ptr<Client> c, uint8_t command, uin
|
|||||||
if (l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED) {
|
if (l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED) {
|
||||||
auto item = c->game_data.player()->remove_item(
|
auto item = c->game_data.player()->remove_item(
|
||||||
cmd.item_id, cmd.amount, c->version() != GameVersion::BB);
|
cmd.item_id, cmd.amount, c->version() != GameVersion::BB);
|
||||||
auto name = item.data.name(false);
|
auto name = item.name(false);
|
||||||
l->log.info("Inventory item %hu:%08" PRIX32 " destroyed (%s)",
|
l->log.info("Inventory item %hu:%08" PRIX32 " destroyed (%s)",
|
||||||
cmd.header.client_id.load(), cmd.item_id.load(), name.c_str());
|
cmd.header.client_id.load(), cmd.item_id.load(), name.c_str());
|
||||||
if (c->options.debug) {
|
if (c->options.debug) {
|
||||||
string name = item.data.name(true);
|
string name = item.name(true);
|
||||||
send_text_message_printf(c, "$C5DESTROY %08" PRIX32 "\n%s",
|
send_text_message_printf(c, "$C5DESTROY %08" PRIX32 "\n%s",
|
||||||
cmd.item_id.load(), name.c_str());
|
cmd.item_id.load(), name.c_str());
|
||||||
}
|
}
|
||||||
@@ -1573,11 +1560,11 @@ static void on_destroy_ground_item(shared_ptr<Client> c, uint8_t command, uint8_
|
|||||||
|
|
||||||
if (l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED) {
|
if (l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED) {
|
||||||
auto item = l->remove_item(cmd.item_id);
|
auto item = l->remove_item(cmd.item_id);
|
||||||
auto name = item.data.name(false);
|
auto name = item.name(false);
|
||||||
l->log.info("Ground item %08" PRIX32 " destroyed (%s)", cmd.item_id.load(),
|
l->log.info("Ground item %08" PRIX32 " destroyed (%s)", cmd.item_id.load(),
|
||||||
name.c_str());
|
name.c_str());
|
||||||
if (c->options.debug) {
|
if (c->options.debug) {
|
||||||
string name = item.data.name(true);
|
string name = item.name(true);
|
||||||
send_text_message_printf(c, "$C5DESTROY/GND %08" PRIX32 "\n%s",
|
send_text_message_printf(c, "$C5DESTROY/GND %08" PRIX32 "\n%s",
|
||||||
cmd.item_id.load(), name.c_str());
|
cmd.item_id.load(), name.c_str());
|
||||||
}
|
}
|
||||||
@@ -1606,10 +1593,9 @@ static void on_identify_item_bb(shared_ptr<Client> c, uint8_t command, uint8_t f
|
|||||||
|
|
||||||
auto p = c->game_data.player();
|
auto p = c->game_data.player();
|
||||||
p->disp.stats.meseta -= 100;
|
p->disp.stats.meseta -= 100;
|
||||||
c->game_data.identify_result = c->game_data.player()->inventory.items[x];
|
c->game_data.identify_result = c->game_data.player()->inventory.items[x].data;
|
||||||
c->game_data.identify_result.data.data1[4] &= 0x7F;
|
c->game_data.identify_result.data1[4] &= 0x7F;
|
||||||
l->item_creator->apply_tekker_deltas(
|
l->item_creator->apply_tekker_deltas(c->game_data.identify_result, p->disp.visual.section_id);
|
||||||
c->game_data.identify_result.data, p->disp.visual.section_id);
|
|
||||||
send_item_identify_result(c);
|
send_item_identify_result(c);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
@@ -1626,14 +1612,14 @@ static void on_accept_identify_item_bb(shared_ptr<Client> c, uint8_t command, ui
|
|||||||
throw logic_error("item tracking not enabled in BB game");
|
throw logic_error("item tracking not enabled in BB game");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!c->game_data.identify_result.data.id) {
|
if (!c->game_data.identify_result.id || (c->game_data.identify_result.id == 0xFFFFFFFF)) {
|
||||||
throw runtime_error("no identify result present");
|
throw runtime_error("no identify result present");
|
||||||
}
|
}
|
||||||
if (c->game_data.identify_result.data.id != cmd.item_id) {
|
if (c->game_data.identify_result.id != cmd.item_id) {
|
||||||
throw runtime_error("accepted item ID does not match previous identify request");
|
throw runtime_error("accepted item ID does not match previous identify request");
|
||||||
}
|
}
|
||||||
c->game_data.player()->add_item(c->game_data.identify_result);
|
c->game_data.player()->add_item(c->game_data.identify_result);
|
||||||
send_create_inventory_item(c, c->game_data.identify_result.data);
|
send_create_inventory_item(c, c->game_data.identify_result);
|
||||||
c->game_data.identify_result.clear();
|
c->game_data.identify_result.clear();
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
@@ -1653,15 +1639,15 @@ static void on_sell_item_at_shop_bb(shared_ptr<Client> c, uint8_t command, uint8
|
|||||||
|
|
||||||
auto p = c->game_data.player();
|
auto p = c->game_data.player();
|
||||||
auto item = p->remove_item(cmd.item_id, cmd.amount, c->version() != GameVersion::BB);
|
auto item = p->remove_item(cmd.item_id, cmd.amount, c->version() != GameVersion::BB);
|
||||||
size_t price = (s->item_parameter_table->price_for_item(item.data) >> 3) * cmd.amount;
|
size_t price = (s->item_parameter_table->price_for_item(item) >> 3) * cmd.amount;
|
||||||
p->add_meseta(price);
|
p->add_meseta(price);
|
||||||
|
|
||||||
auto name = item.data.name(false);
|
auto name = item.name(false);
|
||||||
l->log.info("Inventory item %hu:%08" PRIX32 " (%s) destroyed via sale (%zu Meseta)",
|
l->log.info("Inventory item %hu:%08" PRIX32 " (%s) destroyed via sale (%zu Meseta)",
|
||||||
c->lobby_client_id, cmd.item_id.load(), name.c_str(), price);
|
c->lobby_client_id, cmd.item_id.load(), name.c_str(), price);
|
||||||
c->game_data.player()->print_inventory(stderr);
|
c->game_data.player()->print_inventory(stderr);
|
||||||
if (c->options.debug) {
|
if (c->options.debug) {
|
||||||
string name = item.data.name(true);
|
string name = item.name(true);
|
||||||
send_text_message_printf(c, "$C5DESTROY/SELL %08" PRIX32 "\n+%zu Meseta\n%s",
|
send_text_message_printf(c, "$C5DESTROY/SELL %08" PRIX32 "\n+%zu Meseta\n%s",
|
||||||
cmd.item_id.load(), price, name.c_str());
|
cmd.item_id.load(), price, name.c_str());
|
||||||
}
|
}
|
||||||
@@ -1678,29 +1664,29 @@ static void on_buy_shop_item_bb(shared_ptr<Client> c, uint8_t, uint8_t, const vo
|
|||||||
throw logic_error("item tracking not enabled in BB game");
|
throw logic_error("item tracking not enabled in BB game");
|
||||||
}
|
}
|
||||||
|
|
||||||
PlayerInventoryItem item;
|
ItemData item;
|
||||||
item.data = c->game_data.shop_contents.at(cmd.shop_type).at(cmd.item_index);
|
item = c->game_data.shop_contents.at(cmd.shop_type).at(cmd.item_index);
|
||||||
if (item.data.is_stackable()) {
|
if (item.is_stackable()) {
|
||||||
item.data.data1[5] = cmd.amount;
|
item.data1[5] = cmd.amount;
|
||||||
} else if (cmd.amount != 1) {
|
} else if (cmd.amount != 1) {
|
||||||
throw runtime_error("item is not stackable");
|
throw runtime_error("item is not stackable");
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t price = item.data.data2d * cmd.amount;
|
size_t price = item.data2d * cmd.amount;
|
||||||
item.data.data2d = 0;
|
item.data2d = 0;
|
||||||
auto p = c->game_data.player();
|
auto p = c->game_data.player();
|
||||||
p->remove_meseta(price, false);
|
p->remove_meseta(price, false);
|
||||||
|
|
||||||
item.data.id = cmd.inventory_item_id;
|
item.id = cmd.inventory_item_id;
|
||||||
p->add_item(item);
|
p->add_item(item);
|
||||||
send_create_inventory_item(c, item.data);
|
send_create_inventory_item(c, item);
|
||||||
|
|
||||||
auto name = item.data.name(false);
|
auto name = item.name(false);
|
||||||
l->log.info("Inventory item %hu:%08" PRIX32 " created via purchase (%s) for %zu meseta",
|
l->log.info("Inventory item %hu:%08" PRIX32 " created via purchase (%s) for %zu meseta",
|
||||||
c->lobby_client_id, cmd.inventory_item_id.load(), name.c_str(), price);
|
c->lobby_client_id, cmd.inventory_item_id.load(), name.c_str(), price);
|
||||||
p->print_inventory(stderr);
|
p->print_inventory(stderr);
|
||||||
if (c->options.debug) {
|
if (c->options.debug) {
|
||||||
string name = item.data.name(true);
|
string name = item.name(true);
|
||||||
send_text_message_printf(c, "$C5CREATE/BUY %08" PRIX32 "\n-%zu Meseta\n%s",
|
send_text_message_printf(c, "$C5CREATE/BUY %08" PRIX32 "\n-%zu Meseta\n%s",
|
||||||
cmd.inventory_item_id.load(), price, name.c_str());
|
cmd.inventory_item_id.load(), price, name.c_str());
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-8
@@ -2100,7 +2100,7 @@ void send_item_identify_result(shared_ptr<Client> c) {
|
|||||||
res.header.subcommand = 0xB9;
|
res.header.subcommand = 0xB9;
|
||||||
res.header.size = sizeof(res) / 4;
|
res.header.size = sizeof(res) / 4;
|
||||||
res.header.client_id = c->lobby_client_id;
|
res.header.client_id = c->lobby_client_id;
|
||||||
res.item_data = c->game_data.identify_result.data;
|
res.item_data = c->game_data.identify_result;
|
||||||
send_command_t(l, 0x60, 0x00, res);
|
send_command_t(l, 0x60, 0x00, res);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2109,15 +2109,14 @@ void send_bank(shared_ptr<Client> c) {
|
|||||||
throw logic_error("6xBC can only be sent to BB clients");
|
throw logic_error("6xBC can only be sent to BB clients");
|
||||||
}
|
}
|
||||||
|
|
||||||
vector<PlayerBankItem> items(c->game_data.player()->bank.items,
|
const auto* items_it = c->game_data.player()->bank.items.data();
|
||||||
&c->game_data.player()->bank.items[c->game_data.player()->bank.num_items]);
|
vector<PlayerBankItem> items(items_it, items_it + c->game_data.player()->bank.num_items);
|
||||||
|
|
||||||
uint32_t checksum = random_object<uint32_t>();
|
|
||||||
G_BankContentsHeader_BB_6xBC cmd = {
|
G_BankContentsHeader_BB_6xBC cmd = {
|
||||||
{{0xBC, 0, 0}, 0}, checksum, c->game_data.player()->bank.num_items, c->game_data.player()->bank.meseta};
|
{{0xBC, 0, 0}, sizeof(G_BankContentsHeader_BB_6xBC) + items.size() * sizeof(PlayerBankItem)},
|
||||||
|
random_object<uint32_t>(),
|
||||||
size_t size = 8 + sizeof(cmd) + items.size() * sizeof(PlayerBankItem);
|
c->game_data.player()->bank.num_items,
|
||||||
cmd.header.size = size;
|
c->game_data.player()->bank.meseta};
|
||||||
|
|
||||||
send_command_t_vt(c, 0x6C, 0x00, cmd, items);
|
send_command_t_vt(c, 0x6C, 0x00, cmd, items);
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-7
@@ -819,21 +819,20 @@ Proxy session commands:\n\
|
|||||||
throw runtime_error("proxy session is not game leader");
|
throw runtime_error("proxy session is not game leader");
|
||||||
}
|
}
|
||||||
|
|
||||||
PlayerInventoryItem item;
|
ItemData item(command_args);
|
||||||
item.data = ItemData(command_args);
|
item.id = random_object<uint32_t>();
|
||||||
item.data.id = random_object<uint32_t>();
|
|
||||||
|
|
||||||
if (command_name == "set-next-item") {
|
if (command_name == "set-next-item") {
|
||||||
session->next_drop_item = item;
|
session->next_drop_item = item;
|
||||||
|
|
||||||
string name = session->next_drop_item.data.name(true);
|
string name = session->next_drop_item.name(true);
|
||||||
send_text_message(session->client_channel, u"$C7Next drop:\n" + decode_sjis(name));
|
send_text_message(session->client_channel, u"$C7Next drop:\n" + decode_sjis(name));
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
send_drop_stacked_item(session->client_channel, item.data, session->area, session->x, session->z);
|
send_drop_stacked_item(session->client_channel, item, session->area, session->x, session->z);
|
||||||
send_drop_stacked_item(session->server_channel, item.data, session->area, session->x, session->z);
|
send_drop_stacked_item(session->server_channel, item, session->area, session->x, session->z);
|
||||||
|
|
||||||
string name = item.data.name(true);
|
string name = item.name(true);
|
||||||
send_text_message(session->client_channel, u"$C7Item created:\n" + decode_sjis(name));
|
send_text_message(session->client_channel, u"$C7Item created:\n" + decode_sjis(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user