implement bb shops incompletely
This commit is contained in:
@@ -281,7 +281,7 @@ int32_t CommonItemCreator::decide_item_type(bool is_box) const {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ItemData CommonItemCreator::create_item(bool is_box, uint8_t episode,
|
||||
ItemData CommonItemCreator::create_drop_item(bool is_box, uint8_t episode,
|
||||
uint8_t difficulty, uint8_t area, uint8_t section_id) const {
|
||||
// change the area if it's invalid (data for the bosses are actually in other areas)
|
||||
if (area > 10) {
|
||||
@@ -465,3 +465,124 @@ ItemData CommonItemCreator::create_item(bool is_box, uint8_t episode,
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
|
||||
ItemData CommonItemCreator::create_shop_item(uint8_t difficulty,
|
||||
uint8_t item_type) const {
|
||||
static const uint8_t max_percentages[4] = {20, 35, 45, 50};
|
||||
static const uint8_t max_quantity[4] = { 1, 1, 2, 2};
|
||||
static const uint8_t max_tech_level[4] = { 8, 15, 23, 30};
|
||||
static const uint8_t max_anti_level[4] = { 2, 4, 6, 7};
|
||||
|
||||
ItemData item;
|
||||
memset(&item, 0, sizeof(item));
|
||||
|
||||
item.item_data1[0] = item_type;
|
||||
while (item.item_data1[0] == 2) {
|
||||
item.item_data1[0] = rand() % 3;
|
||||
}
|
||||
switch (item.item_data1[0]) {
|
||||
case 0: { // weapon
|
||||
item.item_data1[1] = (rand() % 12) + 1;
|
||||
if (item.item_data1[1] > 9) {
|
||||
item.item_data1[2] = difficulty;
|
||||
} else {
|
||||
item.item_data1[2] = (rand() & 1) + difficulty;
|
||||
}
|
||||
|
||||
item.item_data1[3] = rand() % 11;
|
||||
item.item_data1[4] = rand() % 11;
|
||||
|
||||
size_t num_percentages = 0;
|
||||
for (size_t x = 0; (x < 5) && (num_percentages < 3); x++) {
|
||||
if ((rand() % 4) == 1) {
|
||||
item.item_data1[(num_percentages * 2) + 6] = x;
|
||||
item.item_data1[(num_percentages * 2) + 7] = rand() % (max_percentages[difficulty] + 1);
|
||||
num_percentages++;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 1: // armor
|
||||
item.item_data1[1] = 0;
|
||||
while (item.item_data1[1] == 0) {
|
||||
item.item_data1[1] = rand() & 3;
|
||||
}
|
||||
switch (item.item_data1[1]) {
|
||||
case 1:
|
||||
item.item_data1[2] = (rand() % 6) + (difficulty * 6);
|
||||
item.item_data1[5] = rand() % 5;
|
||||
break;
|
||||
case 2:
|
||||
item.item_data2[2] = (rand() % 6) + (difficulty * 5);
|
||||
*reinterpret_cast<short*>(&item.item_data1[6]) = (rand() % 9) - 4;
|
||||
*reinterpret_cast<short*>(&item.item_data1[9]) = (rand() % 9) - 4;
|
||||
break;
|
||||
case 3:
|
||||
item.item_data2[2] = rand() % 0x3B;
|
||||
*reinterpret_cast<short*>(&item.item_data1[7]) = (rand() % 5) - 4;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 3: // tool
|
||||
item.item_data1[1] = rand() % 12;
|
||||
switch (item.item_data1[1]) {
|
||||
case 0:
|
||||
case 1:
|
||||
if (difficulty == 0) {
|
||||
item.item_data1[2] = 0;
|
||||
} else if (difficulty == 1) {
|
||||
item.item_data1[2] = rand() % 2;
|
||||
} else if (difficulty == 2) {
|
||||
item.item_data1[2] = (rand() % 2) + 1;
|
||||
} else if (difficulty == 3) {
|
||||
item.item_data1[2] = 2;
|
||||
}
|
||||
break;
|
||||
|
||||
case 6:
|
||||
item.item_data1[2] = rand() % 2;
|
||||
break;
|
||||
|
||||
case 10:
|
||||
item.item_data1[2] = rand() % 3;
|
||||
break;
|
||||
|
||||
case 11:
|
||||
item.item_data1[2] = rand() % 7;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (item.item_data1[1]) {
|
||||
case 2:
|
||||
item.item_data1[4] = rand() % 19;
|
||||
switch (item.item_data1[4]) {
|
||||
case 14:
|
||||
case 17:
|
||||
item.item_data1[2] = 0; // reverser & ryuker always level 1
|
||||
break;
|
||||
case 16:
|
||||
item.item_data1[2] = rand() % max_anti_level[difficulty];
|
||||
break;
|
||||
default:
|
||||
item.item_data1[2] = rand() % max_tech_level[difficulty];
|
||||
}
|
||||
break;
|
||||
case 0:
|
||||
case 1:
|
||||
case 3:
|
||||
case 4:
|
||||
case 5:
|
||||
case 6:
|
||||
case 7:
|
||||
case 8:
|
||||
case 16:
|
||||
item.item_data1[5] = rand() % (max_quantity[difficulty] + 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ struct CommonItemCreator {
|
||||
const std::vector<std::vector<uint8_t>>& unit_types);
|
||||
|
||||
int32_t decide_item_type(bool is_box) const;
|
||||
ItemData create_item(bool is_box, uint8_t episode, uint8_t difficulty,
|
||||
ItemData create_drop_item(bool is_box, uint8_t episode, uint8_t difficulty,
|
||||
uint8_t area, uint8_t section_id) const;
|
||||
ItemData create_shop_item(uint8_t difficulty, uint8_t shop_type) const;
|
||||
};
|
||||
|
||||
@@ -176,7 +176,7 @@ void Lobby::remove_item(uint32_t item_id, PlayerInventoryItem* item) {
|
||||
this->item_id_to_floor_item.erase(item_it);
|
||||
}
|
||||
|
||||
uint32_t Lobby::generate_item_id(uint32_t client_id) {
|
||||
uint32_t Lobby::generate_item_id(uint8_t client_id) {
|
||||
if (client_id < this->max_clients) {
|
||||
return this->next_item_id[client_id]++;
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@ struct Lobby {
|
||||
void add_item(const PlayerInventoryItem& item);
|
||||
void remove_item(uint32_t item_id, PlayerInventoryItem* item);
|
||||
size_t find_item(uint32_t item_id);
|
||||
uint32_t generate_item_id(uint32_t client_id);
|
||||
uint32_t generate_item_id(uint8_t client_id);
|
||||
|
||||
void assign_item_ids_for_player(uint32_t client_id, PlayerInventory& inv);
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <stddef.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "Version.hh"
|
||||
|
||||
@@ -329,9 +330,9 @@ struct PlayerBB {
|
||||
uint8_t quest_data1[0x0208]; // 04F0 // player
|
||||
PlayerBank bank; // 06F8 // player
|
||||
uint32_t serial_number; // 19C0 // player
|
||||
char16_t name[0x18]; // 19C4 // player
|
||||
char16_t team_name[0x10]; // 19C4 // player
|
||||
char16_t guild_card_desc[0x58]; // 1A14 // player
|
||||
char16_t name[0x18]; // 19C4 // player
|
||||
char16_t team_name[0x10]; // 19C4 // player
|
||||
char16_t guild_card_desc[0x58]; // 1A14 // player
|
||||
uint8_t reserved1; // 1AC4 // player
|
||||
uint8_t reserved2; // 1AC5 // player
|
||||
uint8_t section_id; // 1AC6 // player
|
||||
@@ -339,8 +340,8 @@ struct PlayerBB {
|
||||
uint32_t unknown3; // 1AC8 //
|
||||
uint8_t symbol_chats[0x04E0]; // 1ACC // account
|
||||
uint8_t shortcuts[0x0A40]; // 1FAC // account
|
||||
char16_t auto_reply[0x00AC]; // 29EC // player
|
||||
char16_t info_board[0x00AC]; // 2B44 // player
|
||||
char16_t auto_reply[0x00AC]; // 29EC // player
|
||||
char16_t info_board[0x00AC]; // 2B44 // player
|
||||
uint8_t unknown5[0x001C]; // 2C9C //
|
||||
uint8_t challenge_data[0x0140]; // 2CB8 // player
|
||||
uint8_t tech_menu_config[0x0028]; // 2DF8 // player
|
||||
@@ -399,6 +400,7 @@ struct Player {
|
||||
uint8_t quest_data1[0x0208]; // player
|
||||
uint8_t quest_data2[0x0058]; // player
|
||||
uint32_t serial_number;
|
||||
std::vector<ItemData> current_shop_contents;
|
||||
uint8_t shortcuts[0x0A40]; // account
|
||||
uint8_t symbol_chats[0x04E0]; // account
|
||||
char16_t team_name[0x0010]; // account
|
||||
|
||||
+35
-6
@@ -357,6 +357,35 @@ static void process_subcommand_use_item(shared_ptr<ServerState> s,
|
||||
forward_subcommand(l, c, command, flag, p, count);
|
||||
}
|
||||
|
||||
static void process_subcommand_open_shop(shared_ptr<ServerState> s,
|
||||
shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
||||
const PSOSubcommand* p, size_t count) {
|
||||
check_size(count, 2);
|
||||
uint32_t shop_type = p[1].dword;
|
||||
|
||||
if ((l->version == GameVersion::BB) && l->is_game()) {
|
||||
size_t num_items = (rand() % 4) + 9;
|
||||
c->player.current_shop_contents.clear();
|
||||
while (c->player.current_shop_contents.size() < num_items) {
|
||||
ItemData item_data;
|
||||
if (shop_type == 0) { // tool shop
|
||||
item_data = s->common_item_creator->create_shop_item(l->difficulty, 3);
|
||||
} else if (shop_type == 1) { // weapon shop
|
||||
item_data = s->common_item_creator->create_shop_item(l->difficulty, 0);
|
||||
} else if (shop_type == 2) { // guards shop
|
||||
item_data = s->common_item_creator->create_shop_item(l->difficulty, 1);
|
||||
} else { // unknown shop... just leave it blank I guess
|
||||
break;
|
||||
}
|
||||
|
||||
item_data.item_id = l->generate_item_id(c->lobby_client_id);
|
||||
c->player.current_shop_contents.emplace_back(item_data);
|
||||
}
|
||||
|
||||
send_shop(c, shop_type);
|
||||
}
|
||||
}
|
||||
|
||||
static void process_subcommand_open_bank(shared_ptr<ServerState> s,
|
||||
shared_ptr<Lobby> l, shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
||||
const PSOSubcommand* p, size_t count) {
|
||||
@@ -418,7 +447,7 @@ static void process_subcommand_bank_action(shared_ptr<ServerState> s,
|
||||
PlayerBankItem bank_item;
|
||||
c->player.bank.remove_item(cmd->item_id, cmd->item_amount, &bank_item);
|
||||
PlayerInventoryItem item = bank_item.to_inventory_item();
|
||||
item.data.item_id = l->generate_item_id(0xFFFFFFFF);
|
||||
item.data.item_id = l->generate_item_id(0xFF);
|
||||
c->player.add_item(item);
|
||||
send_create_inventory_item(l, c, item.data);
|
||||
}
|
||||
@@ -514,7 +543,7 @@ static void process_subcommand_enemy_drop_item(shared_ptr<ServerState> s,
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
item.data = s->common_item_creator->create_item(false, l->episode,
|
||||
item.data = s->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
|
||||
@@ -522,7 +551,7 @@ static void process_subcommand_enemy_drop_item(shared_ptr<ServerState> s,
|
||||
}
|
||||
}
|
||||
}
|
||||
item.data.item_id = l->generate_item_id(0xFFFFFFFF);
|
||||
item.data.item_id = l->generate_item_id(0xFF);
|
||||
|
||||
l->add_item(item);
|
||||
send_drop_item(l, item.data, false, cmd->area, cmd->x, cmd->y,
|
||||
@@ -586,7 +615,7 @@ static void process_subcommand_box_drop_item(shared_ptr<ServerState> s,
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
item.data = s->common_item_creator->create_item(true, l->episode,
|
||||
item.data = s->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
|
||||
@@ -594,7 +623,7 @@ static void process_subcommand_box_drop_item(shared_ptr<ServerState> s,
|
||||
}
|
||||
}
|
||||
}
|
||||
item.data.item_id = l->generate_item_id(0xFFFFFFFF);
|
||||
item.data.item_id = l->generate_item_id(0xFF);
|
||||
|
||||
l->add_item(item);
|
||||
send_drop_item(l, item.data, false, cmd->area, cmd->x, cmd->y,
|
||||
@@ -1067,7 +1096,7 @@ subcommand_handler_t subcommand_handlers[0x100] = {
|
||||
process_subcommand_unimplemented,
|
||||
process_subcommand_unimplemented,
|
||||
process_subcommand_unimplemented,
|
||||
process_subcommand_unimplemented,
|
||||
process_subcommand_open_shop,
|
||||
process_subcommand_unimplemented,
|
||||
process_subcommand_unimplemented,
|
||||
process_subcommand_identify_item,
|
||||
|
||||
@@ -1936,6 +1936,37 @@ void send_bank(shared_ptr<Client> c) {
|
||||
send_command(c, 0x6C, 0x00, cmd, items);
|
||||
}
|
||||
|
||||
// sends the player a shop's contents
|
||||
void send_shop(shared_ptr<Client> c, uint8_t shop_type) {
|
||||
struct {
|
||||
uint8_t subcommand; // B6
|
||||
uint8_t size; // 2C regardless of the number of items??
|
||||
uint16_t params; // 037F
|
||||
uint8_t shop_type;
|
||||
uint8_t num_items;
|
||||
uint16_t unused;
|
||||
ItemData entries[20];
|
||||
} cmd = {
|
||||
0xB6,
|
||||
0x2C,
|
||||
0x037F,
|
||||
shop_type,
|
||||
static_cast<uint8_t>(c->player.current_shop_contents.size()),
|
||||
0,
|
||||
};
|
||||
|
||||
size_t count = c->player.current_shop_contents.size();
|
||||
if (count > sizeof(cmd.entries) / sizeof(cmd.entries[0])) {
|
||||
throw logic_error("too many items in shop");
|
||||
}
|
||||
|
||||
for (size_t x = 0; x < count; x++) {
|
||||
cmd.entries[x] = c->player.current_shop_contents[x];
|
||||
}
|
||||
|
||||
send_command(c, 0x6C, 0x00, &cmd, sizeof(cmd) - sizeof(cmd.entries[0]) * (20 - count));
|
||||
}
|
||||
|
||||
// notifies players about a level up
|
||||
void send_level_up(shared_ptr<Lobby> l, shared_ptr<Client> c) {
|
||||
PlayerStats stats = c->player.disp.stats;
|
||||
|
||||
@@ -166,6 +166,7 @@ void send_create_inventory_item(std::shared_ptr<Lobby> l, std::shared_ptr<Client
|
||||
void send_destroy_item(std::shared_ptr<Lobby> l, std::shared_ptr<Client> c,
|
||||
uint32_t item_id, uint32_t amount);
|
||||
void send_bank(std::shared_ptr<Client> c);
|
||||
void send_shop(std::shared_ptr<Client> c, uint8_t shop_type);
|
||||
void send_level_up(std::shared_ptr<Lobby> l, std::shared_ptr<Client> c);
|
||||
void send_give_experience(std::shared_ptr<Lobby> l, std::shared_ptr<Client> c,
|
||||
uint32_t amount);
|
||||
|
||||
Reference in New Issue
Block a user