implement bb shops incompletely

This commit is contained in:
Martin Michelsen
2020-02-21 17:29:30 -08:00
parent e6f991a880
commit 5bcc36a627
8 changed files with 200 additions and 15 deletions
+122 -1
View File
@@ -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;
}
+2 -1
View File
@@ -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;
};
+1 -1
View File
@@ -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]++;
}
+1 -1
View File
@@ -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);
+7 -5
View File
@@ -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
View File
@@ -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,
+31
View File
@@ -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;
+1
View File
@@ -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);