document most quest opcodes
This commit is contained in:
@@ -13,6 +13,7 @@ See TODO.md for a list of known issues and future work I've curated, or go to th
|
||||
* [History](#history)
|
||||
* [Other server projects](#other-server-projects)
|
||||
* [Using newserv in other projects](#using-newserv-in-other-projects)
|
||||
* [Developer information](#developer-information)
|
||||
* [Compatibility](#compatibility)
|
||||
* Setup
|
||||
* [Server setup](#server-setup)
|
||||
@@ -65,6 +66,20 @@ Independently of this project, there are many other PSO servers out there. Those
|
||||
* (2021) **[Phantasmal World](https://github.com/DaanVandenBosch/phantasmal-world)**: A set of PSO tools, including a web-based model viewer and quest builder, and a PSO server, written by Daan Vanden Bosch.
|
||||
* (2021) **[Elseware](http://git.sharnoth.com/jake/elseware)**: A PSOBB server written in Rust by Jake.
|
||||
|
||||
## Developer information
|
||||
|
||||
There is a lot of code in this project that could be useful as a reference. Some of the more notable files are:
|
||||
* **src/CommandFormats.hh**: Complete listing of all network commands used in all known versions of the game, and their formats
|
||||
* **src/DCSerialNumbers.hh/cc**: PSO DC serial number validation algorithm and serial number generator
|
||||
* **src/ItemData.hh**: Item format reference
|
||||
* **src/ItemCreator.hh/cc**: Reverse-engineered item generator from Episodes 1&2 (used for all versions)
|
||||
* **src/ItemParameterTable.hh**: Format of many structures in ItemPMT.prs
|
||||
* **src/Map.hh/cc**: Map file (.dat) structure and reverse-engineered Challenge Mode random enemy generation algorithm
|
||||
* **src/QuestScript.cc**: Complete listing of all quest opcodes on all versions, along with their arguments and behavior
|
||||
* **src/SaveFileFormats.hh**: Definitions of save file structures for all versions
|
||||
* **src/Episode3/DataIndexes.hh**: Episode 3 file structures, including card definition format and map/quest format
|
||||
* **system/item-tables/names-v4.json**: Names of all items, indexed by the first 3 bytes of data1
|
||||
|
||||
## Using newserv in other projects
|
||||
|
||||
There is a fair amount of code in this project that could potentially be useful to other projects. You are free to use code from newserv in your own open-source projects; the only condition is that the contents of the LICENSE file must be included in your project if you use code from newserv. Your project does not also have to use the MIT license; you can use any license you want.
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
## PSO DC
|
||||
|
||||
- Investigate if https://crates.io/crates/blaze-ssl-async can be used to implement the HL check server
|
||||
- Does DCv1 support battle mode? The flag exists on the client; try this out
|
||||
|
||||
## Episode 3
|
||||
|
||||
|
||||
+81
-55
@@ -882,7 +882,7 @@ struct SC_GameGuardCheck_BB_0022 {
|
||||
struct S_ExchangeSecretLotteryTicketResult_BB_24 {
|
||||
// These fields map to unknown_a1 and unknown_a2 in the 6xDE command (but
|
||||
// their order is swapped here).
|
||||
le_uint16_t function_id = 0;
|
||||
le_uint16_t label = 0;
|
||||
uint8_t start_index = 0;
|
||||
uint8_t unused = 0;
|
||||
parray<le_uint32_t, 8> unknown_a3;
|
||||
@@ -892,7 +892,7 @@ struct S_ExchangeSecretLotteryTicketResult_BB_24 {
|
||||
// Sent in response to a 6xE1 command from the client.
|
||||
|
||||
struct S_GallonPlanResult_BB_25 {
|
||||
le_uint16_t function_id = 0;
|
||||
le_uint16_t label = 0;
|
||||
uint8_t offset1 = 0;
|
||||
uint8_t offset2 = 0;
|
||||
uint8_t value1 = 0;
|
||||
@@ -1227,20 +1227,21 @@ struct S_JoinGameT_DC_PC {
|
||||
// field when sending this command to start an Episode 3 tournament game. This
|
||||
// can be misleading when reading old logs from those days, but the Episode 3
|
||||
// client really does ignore it.
|
||||
parray<le_uint32_t, 0x20> variations;
|
||||
/* 0004 */ parray<le_uint32_t, 0x20> variations;
|
||||
// Unlike lobby join commands, these are filled in in their slot positions.
|
||||
// That is, if there's only one player in a game with ID 2, then the first two
|
||||
// of these are blank and the player's data is in the third entry here.
|
||||
parray<LobbyDataT, 4> lobby_data;
|
||||
uint8_t client_id = 0;
|
||||
uint8_t leader_id = 0;
|
||||
uint8_t disable_udp = 1;
|
||||
uint8_t difficulty = 0;
|
||||
uint8_t battle_mode = 0;
|
||||
uint8_t event = 0;
|
||||
uint8_t section_id = 0;
|
||||
uint8_t challenge_mode = 0;
|
||||
le_uint32_t rare_seed = 0;
|
||||
/* 0084 */ parray<LobbyDataT, 4> lobby_data;
|
||||
/* 0104 */ uint8_t client_id = 0;
|
||||
/* 0105 */ uint8_t leader_id = 0;
|
||||
/* 0106 */ uint8_t disable_udp = 1;
|
||||
/* 0107 */ uint8_t difficulty = 0;
|
||||
/* 0108 */ uint8_t battle_mode = 0;
|
||||
/* 0109 */ uint8_t event = 0;
|
||||
/* 010A */ uint8_t section_id = 0;
|
||||
/* 010B */ uint8_t challenge_mode = 0;
|
||||
/* 010C */ le_uint32_t rare_seed = 0;
|
||||
/* 0110 */
|
||||
} __packed__;
|
||||
|
||||
struct S_JoinGame_DCNTE_64 {
|
||||
@@ -1454,6 +1455,8 @@ struct S_GenerateID_DC_PC_V3_80 {
|
||||
// On GC (and probably other versions too) the unused space after the text
|
||||
// contains uninitialized memory when the client sends this command. newserv
|
||||
// clears the uninitialized data for security reasons before forwarding.
|
||||
// The maximum length of the message is 170 characters, despite the field being
|
||||
// able to hold triple that amount.
|
||||
|
||||
struct SC_SimpleMail_PC_81 {
|
||||
// If player_tag and from_guild_card_number are zero, the message cannot be
|
||||
@@ -2131,10 +2134,10 @@ struct S_QuestMenuEntry_BB_A2_A4 {
|
||||
// because the following command (AB) is definitely not valid on that version.
|
||||
|
||||
struct C_SendQuestStatistic_V3_BB_AA {
|
||||
le_uint16_t stat_id1 = 0;
|
||||
le_uint16_t stat_id = 0;
|
||||
le_uint16_t unused = 0;
|
||||
le_uint16_t function_id1 = 0;
|
||||
le_uint16_t function_id2 = 0;
|
||||
le_uint16_t label1 = 0;
|
||||
le_uint16_t label2 = 0;
|
||||
parray<le_uint32_t, 8> params;
|
||||
} __packed_ws__(C_SendQuestStatistic_V3_BB_AA, 0x28);
|
||||
|
||||
@@ -2146,7 +2149,7 @@ struct C_SendQuestStatistic_V3_BB_AA {
|
||||
// presumably use this command to call any function at any time during a quest.
|
||||
|
||||
struct S_CallQuestFunction_V3_BB_AB {
|
||||
le_uint16_t function_id = 0;
|
||||
le_uint16_t label = 0;
|
||||
parray<uint8_t, 2> unused;
|
||||
} __packed_ws__(S_CallQuestFunction_V3_BB_AB, 4);
|
||||
|
||||
@@ -4105,17 +4108,17 @@ struct G_DarkFalzActions_6x19 {
|
||||
|
||||
// 6x1A: Invalid subcommand
|
||||
|
||||
// 6x1B: Unknown (not valid on Episode 3) (protected on V3/V4)
|
||||
// 6x1B: Enable PK mode for player (not valid on Episode 3) (protected on V3/V4)
|
||||
|
||||
struct G_Unknown_6x1B {
|
||||
struct G_EnablePKModeForPlayer_6x1B {
|
||||
G_ClientIDHeader header;
|
||||
} __packed_ws__(G_Unknown_6x1B, 4);
|
||||
} __packed_ws__(G_EnablePKModeForPlayer_6x1B, 4);
|
||||
|
||||
// 6x1C: Destroy NPC (protected on V3/V4)
|
||||
// 6x1C: Disable PK mode for player (protected on V3/V4)
|
||||
|
||||
struct G_DestroyNPC_6x1C {
|
||||
struct G_DisablePKModeForPlayer_6x1C {
|
||||
G_ClientIDHeader header;
|
||||
} __packed_ws__(G_DestroyNPC_6x1C, 4);
|
||||
} __packed_ws__(G_DisablePKModeForPlayer_6x1C, 4);
|
||||
|
||||
// 6x1D: Invalid subcommand
|
||||
// 6x1E: Invalid subcommand
|
||||
@@ -4753,6 +4756,24 @@ struct G_SetTelepipeState_6x68 {
|
||||
// 6x69: NPC control
|
||||
// Note: NPCs cannot be destroyed with 6x69; 6x1C is used instead for that.
|
||||
|
||||
// For commands 0 and 3, the template indexes are:
|
||||
// 0: NOL 1: CICIL 2: CICIL 3: MARACA 4: ELLY
|
||||
// 5: SHINO 6: DONOPH 7: MOME 8: ALICIA 9: ASH
|
||||
// 10: ASH 11: SUE 12: KIREEK 13: BERNIE 14: GILLIAM
|
||||
// 15: ELENOR 16: ALICIA 17: MONTAGUE 18: RUPIKA 19: MATHA
|
||||
// 20: ANNA 21: TONZLAR 22: TOBOKKE 23: GEKIGASKY 24: TYPE:O
|
||||
// 25: TYPE:W 26: GIZEL 27: DACCI 28: HOPKINS 29: DORONBO
|
||||
// 30: KROE 31: MUJO 32: RACTON 33: LIONEL 34: ZOKE
|
||||
// 35: SUE 36: NADJA 37: ELENOR 38: KIREEK 39: BERNIE
|
||||
// 40: CHRIS 41: RENEE 42: KAREN 43: BEIRON 44: NAKA
|
||||
// 45: LEO 46: HOUND 47: MADELEINE 48: VALLETTA 49: BOGARDE
|
||||
// 50: ULT 51: TYPE:I 52: TYPE:V 53: TACHIBANA 54: OSMAN
|
||||
// 55: VIVIENNE 56: BP 57: SHINTARO 58: KEN 59: TAKUYA
|
||||
// 60: SOKON 61: UKON 62: CANTONA 63: HASE
|
||||
// Created NPCs have a base level according to their template index. On Hard,
|
||||
// 25 is added to their base level; on Very Hard, 50 is added; on Ultimate, 150
|
||||
// is added. In all cases the NPC's level is clamped to [1, 199].
|
||||
|
||||
struct G_NPCControl_6x69 {
|
||||
G_UnusedHeader header;
|
||||
le_uint16_t param1; // Commands 0/3: state; command 1: npc_entity_id; command 2: unknown
|
||||
@@ -5219,17 +5240,17 @@ struct G_TriggerTrap_6x80 {
|
||||
le_uint16_t unknown_a2 = 0;
|
||||
} __packed_ws__(G_TriggerTrap_6x80, 8);
|
||||
|
||||
// 6x81: Set drop weapon on death flag (protected on V3/V4)
|
||||
// 6x81: Disable drop weapon on death (protected on V3/V4)
|
||||
|
||||
struct G_SetDropWeaponOnDeathFlag_6x81 {
|
||||
struct G_DisableDropWeaponOnDeath_6x81 {
|
||||
G_ClientIDHeader header;
|
||||
} __packed_ws__(G_SetDropWeaponOnDeathFlag_6x81, 4);
|
||||
} __packed_ws__(G_DisableDropWeaponOnDeath_6x81, 4);
|
||||
|
||||
// 6x82: Clear drop weapon on death flag (protected on V3/V4)
|
||||
// 6x82: Enable drop weapon on death (protected on V3/V4)
|
||||
|
||||
struct G_ClearDropWeaponOnDeathFlag_6x82 {
|
||||
struct G_EnableDropWeaponOnDeath_6x82 {
|
||||
G_ClientIDHeader header;
|
||||
} __packed_ws__(G_ClearDropWeaponOnDeathFlag_6x82, 4);
|
||||
} __packed_ws__(G_EnableDropWeaponOnDeath_6x82, 4);
|
||||
|
||||
// 6x83: Place trap (protected on V3/V4)
|
||||
|
||||
@@ -5445,7 +5466,7 @@ struct G_Unknown_6x9C {
|
||||
|
||||
struct G_Unknown_6x9D {
|
||||
G_UnusedHeader header;
|
||||
le_uint32_t client_id2 = 0;
|
||||
le_uint32_t client_id = 0;
|
||||
} __packed_ws__(G_Unknown_6x9D, 8);
|
||||
|
||||
// 6x9E: Play camera shutter sound
|
||||
@@ -6084,8 +6105,8 @@ struct G_ExchangeItemInQuest_BB_6xD5 {
|
||||
G_ClientIDHeader header;
|
||||
ItemData find_item; // Only data1[0]-[2] are used
|
||||
ItemData replace_item; // Only data1[0]-[2] are used
|
||||
le_uint16_t success_function_id = 0;
|
||||
le_uint16_t failure_function_id = 0;
|
||||
le_uint16_t success_label = 0;
|
||||
le_uint16_t failure_label = 0;
|
||||
} __packed_ws__(G_ExchangeItemInQuest_BB_6xD5, 0x30);
|
||||
|
||||
// 6xD6: Wrap item (BB; handled by server)
|
||||
@@ -6093,7 +6114,7 @@ struct G_ExchangeItemInQuest_BB_6xD5 {
|
||||
struct G_WrapItem_BB_6xD6 {
|
||||
G_ClientIDHeader header;
|
||||
ItemData item;
|
||||
uint8_t unknown_a1 = 0;
|
||||
uint8_t present_color = 0; // 00-0F
|
||||
parray<uint8_t, 3> unused;
|
||||
} __packed_ws__(G_WrapItem_BB_6xD6, 0x1C);
|
||||
|
||||
@@ -6103,8 +6124,8 @@ struct G_WrapItem_BB_6xD6 {
|
||||
struct G_PaganiniPhotonDropExchange_BB_6xD7 {
|
||||
G_ClientIDHeader header;
|
||||
ItemData new_item; // Only data1[0]-[2] are used
|
||||
le_uint16_t success_function_id = 0;
|
||||
le_uint16_t failure_function_id = 0;
|
||||
le_uint16_t success_label = 0;
|
||||
le_uint16_t failure_label = 0;
|
||||
} __packed_ws__(G_PaganiniPhotonDropExchange_BB_6xD7, 0x1C);
|
||||
|
||||
// 6xD8: Add S-rank weapon special (BB; handled by server)
|
||||
@@ -6115,8 +6136,8 @@ struct G_AddSRankWeaponSpecial_BB_6xD8 {
|
||||
ItemData unknown_a1; // Only data1[0]-[2] are used
|
||||
le_uint32_t item_id = 0;
|
||||
le_uint32_t special_type = 0;
|
||||
le_uint16_t success_function_id = 0;
|
||||
le_uint16_t failure_function_id = 0;
|
||||
le_uint16_t success_label = 0;
|
||||
le_uint16_t failure_label = 0;
|
||||
} __packed_ws__(G_AddSRankWeaponSpecial_BB_6xD8, 0x24);
|
||||
|
||||
// 6xD9: Momoka item exchange (BB; handled by server)
|
||||
@@ -6128,8 +6149,8 @@ struct G_MomokaItemExchange_BB_6xD9 {
|
||||
ItemData replace_item; // Only data1[0]-[2] are used
|
||||
le_uint32_t unknown_a3 = 0;
|
||||
le_uint32_t unknown_a4 = 0;
|
||||
le_uint16_t unknown_a5 = 0;
|
||||
le_uint16_t unknown_a6 = 0;
|
||||
le_uint16_t success_label = 0;
|
||||
le_uint16_t failure_label = 0;
|
||||
} __packed_ws__(G_MomokaItemExchange_BB_6xD9, 0x38);
|
||||
|
||||
// 6xDA: Upgrade weapon attribute (BB; handled by server)
|
||||
@@ -6137,13 +6158,13 @@ struct G_MomokaItemExchange_BB_6xD9 {
|
||||
|
||||
struct G_UpgradeWeaponAttribute_BB_6xDA {
|
||||
G_ClientIDHeader header;
|
||||
ItemData item; // Only data1[0-2] are used (argsA[1-3])
|
||||
le_uint32_t item_id = 0; // argsA[0]
|
||||
le_uint32_t attribute = 0; // argsA[4]
|
||||
le_uint32_t payment_count = 0; // Number of PD or PS (argsA[5])
|
||||
ItemData item; // Only data1[0-2] are used (valueB-valueD)
|
||||
le_uint32_t item_id = 0; // valueA
|
||||
le_uint32_t attribute = 0; // valueE
|
||||
le_uint32_t payment_count = 0; // Number of PD or PS (valueF)
|
||||
le_uint32_t payment_type = 0; // 0 = Photon Drops, 1 = Photon Spheres
|
||||
le_uint16_t success_function_id = 0; // argsA[6]
|
||||
le_uint16_t failure_function_id = 0; // argsA[7]
|
||||
le_uint16_t success_label = 0; // labelG
|
||||
le_uint16_t failure_label = 0; // labelH
|
||||
} __packed_ws__(G_UpgradeWeaponAttribute_BB_6xDA, 0x2C);
|
||||
|
||||
// 6xDB: Exchange item in quest (BB)
|
||||
@@ -6173,12 +6194,17 @@ struct G_SetEXPMultiplier_BB_6xDD {
|
||||
|
||||
// 6xDE: Exchange Secret Lottery Ticket (BB; handled by server)
|
||||
// The client sends this when it executes an F95C quest opcode.
|
||||
// There appears to be a bug in the client here: it sets the subcommand size to
|
||||
// 2 instead of 3, so the last relevant field (failure_label) is not sent to
|
||||
// the server.
|
||||
|
||||
struct G_ExchangeSecretLotteryTicket_BB_6xDE {
|
||||
G_ClientIDHeader header;
|
||||
uint8_t index = 0;
|
||||
uint8_t function_id1 = 0;
|
||||
le_uint16_t function_id2 = 0;
|
||||
uint8_t unknown_a1 = 0;
|
||||
le_uint16_t success_label = 0;
|
||||
// le_uint16_t failure_label = 0;
|
||||
// parray<uint8_t, 2> unused;
|
||||
} __packed_ws__(G_ExchangeSecretLotteryTicket_BB_6xDE, 8);
|
||||
|
||||
// 6xDF: Exchange Photon Crystals (BB; handled by server)
|
||||
@@ -6194,11 +6220,11 @@ struct G_ExchangePhotonCrystals_BB_6xDF {
|
||||
struct G_RequestItemDropFromQuest_BB_6xE0 {
|
||||
G_ClientIDHeader header;
|
||||
uint8_t floor = 0;
|
||||
uint8_t type = 0; // argsA[0]
|
||||
uint8_t type = 0; // valueA
|
||||
uint8_t unknown_a3 = 0;
|
||||
uint8_t unused = 0;
|
||||
le_float x = 0.0f; // argsA[1]
|
||||
le_float z = 0.0f; // argsA[2]
|
||||
le_float x = 0.0f; // valueB
|
||||
le_float z = 0.0f; // valueC
|
||||
} __packed_ws__(G_RequestItemDropFromQuest_BB_6xE0, 0x10);
|
||||
|
||||
// 6xE1: Exchange Photon Tickets (BB; handled by server)
|
||||
@@ -6206,12 +6232,12 @@ struct G_RequestItemDropFromQuest_BB_6xE0 {
|
||||
|
||||
struct G_ExchangePhotonTickets_BB_6xE1 {
|
||||
G_ClientIDHeader header;
|
||||
uint8_t unknown_a1 = 0; // argsA[0]
|
||||
uint8_t unknown_a2 = 0; // argsA[1]
|
||||
uint8_t result_index = 0; // argsA[2]
|
||||
uint8_t unknown_a1 = 0; // valueA
|
||||
uint8_t unknown_a2 = 0; // valueB
|
||||
uint8_t result_index = 0; // valueC
|
||||
uint8_t unused = 0;
|
||||
le_uint16_t function_id1 = 0; // argsA[3]
|
||||
le_uint16_t unknown_a5 = 0; // argsA[4]
|
||||
le_uint16_t success_label = 0; // valueD
|
||||
le_uint16_t failure_label = 0; // valueE
|
||||
} __packed_ws__(G_ExchangePhotonTickets_BB_6xE1, 0x0C);
|
||||
|
||||
// 6xE2: Get Meseta slot prize (BB)
|
||||
|
||||
+10
-6
@@ -142,18 +142,22 @@ bool ItemData::is_wrapped(const StackLimits& limits) const {
|
||||
}
|
||||
}
|
||||
|
||||
void ItemData::wrap(const StackLimits& limits) {
|
||||
void ItemData::wrap(const StackLimits& limits, uint8_t present_color) {
|
||||
switch (this->data1[0]) {
|
||||
case 0:
|
||||
case 1:
|
||||
this->data1[4] |= 0x40;
|
||||
this->data1[5] = (this->data1[5] & 0xF0) | (present_color & 0x0F);
|
||||
break;
|
||||
case 1:
|
||||
this->data1[4] = (this->data1[4] & 0xF0) | 0x40 | (present_color & 0x0F);
|
||||
break;
|
||||
case 2:
|
||||
// Mags cannot have custom present colors
|
||||
this->data2[2] |= 0x40;
|
||||
break;
|
||||
case 3:
|
||||
if (!this->is_stackable(limits)) {
|
||||
this->data1[3] |= 0x40;
|
||||
this->data1[3] = (this->data1[3] & 0xF0) | 0x40 | (present_color & 0x0F);
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
@@ -167,14 +171,14 @@ void ItemData::unwrap(const StackLimits& limits) {
|
||||
switch (this->data1[0]) {
|
||||
case 0:
|
||||
case 1:
|
||||
this->data1[4] &= 0xBF;
|
||||
this->data1[4] &= 0xB0;
|
||||
break;
|
||||
case 2:
|
||||
this->data2[2] &= 0xBF;
|
||||
this->data2[2] &= 0xB0;
|
||||
break;
|
||||
case 3:
|
||||
if (!this->is_stackable(limits)) {
|
||||
this->data1[3] &= 0xBF;
|
||||
this->data1[3] &= 0xB0;
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
|
||||
+6
-4
@@ -77,19 +77,19 @@ struct ItemData {
|
||||
|
||||
// QUICK ITEM FORMAT REFERENCE
|
||||
// data1/0 data1/4 data1/8 data2
|
||||
// Weapon: 00ZZZZGG SS00AABB AABBAABB 00000000
|
||||
// Weapon: 00ZZZZGG SSNNAABB AABBAABB 00000000
|
||||
// Armor: 0101ZZ00 FFTTDDDD EEEE0000 00000000
|
||||
// Shield: 0102ZZ00 FFTTDDDD EEEE0000 00000000
|
||||
// Unit: 0103ZZ00 FF00RRRR 00000000 00000000
|
||||
// Mag: 02ZZLLWW HHHHIIII JJJJKKKK YYQQPPVV
|
||||
// Tool: 03ZZZZFF 00CC0000 00000000 00000000
|
||||
// Tool: 03ZZZZUU 00CC0000 00000000 00000000
|
||||
// Meseta: 04000000 00000000 00000000 MMMMMMMM
|
||||
// A = attribute type (for S-ranks, custom name)
|
||||
// B = attribute amount (for S-ranks, custom name)
|
||||
// C = stack size (for tools)
|
||||
// D = DEF bonus
|
||||
// E = EVP bonus
|
||||
// F = flags (40=present; for tools, unused if item is stackable)
|
||||
// F = armor/shield/unit flags (40=present; low 16 bits are present color)
|
||||
// G = weapon grind
|
||||
// H = mag DEF
|
||||
// I = mag POW
|
||||
@@ -97,11 +97,13 @@ struct ItemData {
|
||||
// K = mag MIND
|
||||
// L = mag level
|
||||
// M = meseta amount
|
||||
// N = present color (weapon only; for other types this is in the flags field)
|
||||
// P = mag flags (40=present, 04=has left pb, 02=has right pb, 01=has center pb)
|
||||
// Q = mag IQ
|
||||
// R = unit modifier (little-endian)
|
||||
// S = weapon flags (80=unidentified, 40=present) and special (low 6 bits)
|
||||
// T = slot count
|
||||
// U = tool flags (40=present; unused if item is stackable)
|
||||
// V = mag color
|
||||
// W = photon blasts
|
||||
// Y = mag synchro
|
||||
@@ -150,7 +152,7 @@ struct ItemData {
|
||||
uint32_t primary_identifier() const;
|
||||
|
||||
bool is_wrapped(const StackLimits& limits) const;
|
||||
void wrap(const StackLimits& limits);
|
||||
void wrap(const StackLimits& limits, uint8_t present_color);
|
||||
void unwrap(const StackLimits& limits);
|
||||
|
||||
bool is_stackable(const StackLimits& limits) const;
|
||||
|
||||
@@ -135,7 +135,7 @@ std::string ItemNameIndex::describe_item(const ItemData& item, bool include_colo
|
||||
// Armors, shields, and units (0x01) can be wrapped, as can mags (0x02) and
|
||||
// non-stackable tools (0x03). However, each of these item classes has its
|
||||
// flags in a different location.
|
||||
if (((item.data1[1] == 0x01) && (item.data1[4] & 0x40)) ||
|
||||
if (((item.data1[0] == 0x01) && (item.data1[4] & 0x40)) ||
|
||||
((item.data1[0] == 0x02) && (item.data2[2] & 0x40)) ||
|
||||
((item.data1[0] == 0x03) && !item.is_stackable(*this->limits) && (item.data1[3] & 0x40))) {
|
||||
ret_tokens.emplace_back("Wrapped");
|
||||
|
||||
+23
-47
@@ -881,78 +881,54 @@ struct BattleRules {
|
||||
LIMIT_LIVES = 2,
|
||||
};
|
||||
|
||||
// Set by quest opcode F812, but values are remapped.
|
||||
// F812 00 => FORBID_ALL
|
||||
// F812 01 => ALLOW
|
||||
// F812 02 => LIMIT_LEVEL
|
||||
// Set by quest opcode F812, but values are remapped
|
||||
/* 00 */ TechDiskMode tech_disk_mode = TechDiskMode::ALLOW;
|
||||
// Set by quest opcode F813, but values are remapped.
|
||||
// F813 00 => FORBID_ALL
|
||||
// F813 01 => ALLOW
|
||||
// F813 02 => CLEAR_AND_ALLOW
|
||||
// F813 03 => FORBID_RARES
|
||||
// Set by quest opcode F813, but values are remapped
|
||||
/* 01 */ WeaponAndArmorMode weapon_and_armor_mode = WeaponAndArmorMode::ALLOW;
|
||||
// Set by quest opcode F814, but values are remapped.
|
||||
// F814 00 => FORBID_ALL
|
||||
// F814 01 => ALLOW
|
||||
// Set by quest opcode F814, but values are remapped
|
||||
/* 02 */ MagMode mag_mode = MagMode::ALLOW;
|
||||
// Set by quest opcode F815, but values are remapped.
|
||||
// F815 00 => FORBID_ALL
|
||||
// F815 01 => ALLOW
|
||||
// F815 02 => CLEAR_AND_ALLOW
|
||||
// Set by quest opcode F815, but values are remapped
|
||||
/* 03 */ ToolMode tool_mode = ToolMode::ALLOW;
|
||||
// Set by quest opcode F816. Values are not remapped.
|
||||
// F816 00 => DEFAULT
|
||||
// F816 01 => ALL_PLAYERS
|
||||
// Set by quest opcode F816. Values are not remapped
|
||||
/* 04 */ TrapMode trap_mode = TrapMode::DEFAULT;
|
||||
// Set by quest opcode F817. Value appears to be unused in all PSO versions.
|
||||
/* 05 */ uint8_t unused_F817 = 0;
|
||||
// Set by quest opcode F818, but values are remapped.
|
||||
// F818 00 => 01
|
||||
// F818 01 => 00
|
||||
// F818 02 => 02
|
||||
// Set by quest opcode F818, but values are remapped
|
||||
/* 06 */ RespawnMode respawn_mode = RespawnMode::ALLOW;
|
||||
// Set by quest opcode F819.
|
||||
// Set by quest opcode F819
|
||||
/* 07 */ uint8_t replace_char = 0;
|
||||
// Set by quest opcode F81A, but value is inverted.
|
||||
// Set by quest opcode F81A, but value is inverted
|
||||
/* 08 */ uint8_t drop_weapon = 0;
|
||||
// Set by quest opcode F81B.
|
||||
// Set by quest opcode F81B
|
||||
/* 09 */ uint8_t is_teams = 0;
|
||||
// Set by quest opcode F852.
|
||||
// Set by quest opcode F852
|
||||
/* 0A */ uint8_t hide_target_reticle = 0;
|
||||
// Set by quest opcode F81E. Values are not remapped.
|
||||
// F81E 00 => ALLOW
|
||||
// F81E 01 => FORBID_ALL
|
||||
// F81E 02 => CLEAR_AND_ALLOW
|
||||
// Set by quest opcode F81E. Values are not remapped
|
||||
/* 0B */ MesetaMode meseta_mode = MesetaMode::ALLOW;
|
||||
// Set by quest opcode F81D.
|
||||
// Set by quest opcode F81D
|
||||
/* 0C */ uint8_t death_level_up = 0;
|
||||
// Set by quest opcode F851. The trap type is remapped:
|
||||
// F851 00 XX => set count to XX for trap type 00
|
||||
// F851 01 XX => set count to XX for trap type 02
|
||||
// F851 02 XX => set count to XX for trap type 03
|
||||
// F851 03 XX => set count to XX for trap type 01
|
||||
// Set by quest opcode F851. The trap type is remapped
|
||||
/* 0D */ parray<uint8_t, 4> trap_counts;
|
||||
// Set by quest opcode F85E.
|
||||
// Set by quest opcode F85E
|
||||
/* 11 */ uint8_t enable_sonar = 0;
|
||||
// Set by quest opcode F85F.
|
||||
// Set by quest opcode F85F
|
||||
/* 12 */ uint8_t sonar_count = 0;
|
||||
// Set by quest opcode F89E.
|
||||
// Set by quest opcode F89E
|
||||
/* 13 */ uint8_t forbid_scape_dolls = 0;
|
||||
// This value does not appear to be set by any quest opcode.
|
||||
// This value does not appear to be set by any quest opcode
|
||||
/* 14 */ le_uint32_t unknown_a1 = 0;
|
||||
// Set by quest opcode F86F.
|
||||
// Set by quest opcode F86F
|
||||
/* 18 */ le_uint32_t lives = 0;
|
||||
// Set by quest opcode F870.
|
||||
// Set by quest opcode F870
|
||||
/* 1C */ le_uint32_t max_tech_level = 0;
|
||||
// Set by quest opcode F871.
|
||||
// Set by quest opcode F871
|
||||
/* 20 */ le_uint32_t char_level = 0;
|
||||
// Set by quest opcode F872.
|
||||
// Set by quest opcode F872
|
||||
/* 24 */ le_uint32_t time_limit = 0;
|
||||
// Set by quest opcode F8A8.
|
||||
// Set by quest opcode F8A8
|
||||
/* 28 */ le_uint16_t death_tech_level_up = 0;
|
||||
/* 2A */ parray<uint8_t, 2> unused;
|
||||
// Set by quest opcode F86B.
|
||||
// Set by quest opcode F86B
|
||||
/* 2C */ le_uint32_t box_drop_area = 0;
|
||||
/* 30 */
|
||||
|
||||
|
||||
+1890
-495
File diff suppressed because it is too large
Load Diff
@@ -3031,8 +3031,8 @@ static void on_AA(shared_ptr<Client> c, uint16_t, uint32_t, string& data) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Send the right value here. (When should we send function_id2?)
|
||||
send_quest_function_call(c, cmd.function_id1);
|
||||
// TODO: Send the right value here. (When should we send label2?)
|
||||
send_quest_function_call(c, cmd.label1);
|
||||
}
|
||||
|
||||
static void on_D7_GC(shared_ptr<Client> c, uint16_t, uint32_t, string& data) {
|
||||
|
||||
+11
-11
@@ -4214,11 +4214,11 @@ static void on_quest_exchange_item_bb(shared_ptr<Client> c, uint8_t, uint8_t, vo
|
||||
p->add_item(new_item, limits);
|
||||
send_create_inventory_item_to_lobby(c, c->lobby_client_id, new_item);
|
||||
|
||||
send_quest_function_call(c, cmd.success_function_id);
|
||||
send_quest_function_call(c, cmd.success_label);
|
||||
|
||||
} catch (const exception& e) {
|
||||
c->log.warning("Quest item exchange failed: %s", e.what());
|
||||
send_quest_function_call(c, cmd.failure_function_id);
|
||||
send_quest_function_call(c, cmd.failure_label);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4232,7 +4232,7 @@ static void on_wrap_item_bb(shared_ptr<Client> c, uint8_t, uint8_t, void* data,
|
||||
auto p = c->character();
|
||||
auto item = p->remove_item(cmd.item.id, 1, *s->item_stack_limits(c->version()));
|
||||
send_destroy_item_to_lobby(c, item.id, 1);
|
||||
item.wrap(*s->item_stack_limits(c->version()));
|
||||
item.wrap(*s->item_stack_limits(c->version()), cmd.present_color);
|
||||
p->add_item(item, *s->item_stack_limits(c->version()));
|
||||
send_create_inventory_item_to_lobby(c, c->lobby_client_id, item);
|
||||
}
|
||||
@@ -4260,11 +4260,11 @@ static void on_photon_drop_exchange_for_item_bb(shared_ptr<Client> c, uint8_t, u
|
||||
p->add_item(new_item, limits);
|
||||
send_create_inventory_item_to_lobby(c, c->lobby_client_id, new_item);
|
||||
|
||||
send_quest_function_call(c, cmd.success_function_id);
|
||||
send_quest_function_call(c, cmd.success_label);
|
||||
|
||||
} catch (const exception& e) {
|
||||
c->log.warning("Quest Photon Drop exchange for item failed: %s", e.what());
|
||||
send_quest_function_call(c, cmd.failure_function_id);
|
||||
send_quest_function_call(c, cmd.failure_label);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4296,11 +4296,11 @@ static void on_photon_drop_exchange_for_s_rank_special_bb(shared_ptr<Client> c,
|
||||
p->add_item(item, limits);
|
||||
send_create_inventory_item_to_lobby(c, c->lobby_client_id, item);
|
||||
|
||||
send_quest_function_call(c, cmd.success_function_id);
|
||||
send_quest_function_call(c, cmd.success_label);
|
||||
|
||||
} catch (const exception& e) {
|
||||
c->log.warning("Quest Photon Drop exchange for S-rank special failed: %s", e.what());
|
||||
send_quest_function_call(c, cmd.failure_function_id);
|
||||
send_quest_function_call(c, cmd.failure_label);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4350,7 +4350,7 @@ static void on_secret_lottery_ticket_exchange_bb(shared_ptr<Client> c, uint8_t,
|
||||
|
||||
S_ExchangeSecretLotteryTicketResult_BB_24 out_cmd;
|
||||
out_cmd.start_index = cmd.index;
|
||||
out_cmd.function_id = cmd.function_id1;
|
||||
out_cmd.label = cmd.success_label;
|
||||
if (s->secret_lottery_results.empty()) {
|
||||
out_cmd.unknown_a3.clear(0);
|
||||
} else if (s->secret_lottery_results.size() == 1) {
|
||||
@@ -4448,7 +4448,7 @@ static void on_quest_F95F_result_bb(shared_ptr<Client> c, uint8_t, uint8_t, void
|
||||
send_create_inventory_item_to_lobby(c, c->lobby_client_id, new_item);
|
||||
|
||||
S_GallonPlanResult_BB_25 out_cmd;
|
||||
out_cmd.function_id = cmd.function_id1;
|
||||
out_cmd.label = cmd.success_label;
|
||||
out_cmd.offset1 = 0x3C;
|
||||
out_cmd.offset2 = 0x08;
|
||||
out_cmd.value1 = 0x00;
|
||||
@@ -4605,11 +4605,11 @@ static void on_upgrade_weapon_attribute_bb(shared_ptr<Client> c, uint8_t, uint8_
|
||||
|
||||
send_destroy_item_to_lobby(c, item.id, 1);
|
||||
send_create_inventory_item_to_lobby(c, c->lobby_client_id, item);
|
||||
send_quest_function_call(c, cmd.success_function_id);
|
||||
send_quest_function_call(c, cmd.success_label);
|
||||
|
||||
} catch (const exception& e) {
|
||||
c->log.warning("Weapon attribute upgrade failed: %s", e.what());
|
||||
send_quest_function_call(c, cmd.failure_function_id);
|
||||
send_quest_function_call(c, cmd.failure_label);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+4
-4
@@ -3132,14 +3132,14 @@ void send_rare_enemy_index_list(shared_ptr<Client> c, const vector<size_t>& inde
|
||||
send_command_t(c, 0xDE, 0x00, cmd);
|
||||
}
|
||||
|
||||
void send_quest_function_call(Channel& ch, uint16_t function_id) {
|
||||
void send_quest_function_call(Channel& ch, uint16_t label) {
|
||||
S_CallQuestFunction_V3_BB_AB cmd;
|
||||
cmd.function_id = function_id;
|
||||
cmd.label = label;
|
||||
ch.send(0xAB, 0x00, &cmd, sizeof(cmd));
|
||||
}
|
||||
|
||||
void send_quest_function_call(shared_ptr<Client> c, uint16_t function_id) {
|
||||
send_quest_function_call(c->channel, function_id);
|
||||
void send_quest_function_call(shared_ptr<Client> c, uint16_t label) {
|
||||
send_quest_function_call(c->channel, label);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
+2
-2
@@ -373,8 +373,8 @@ void send_give_experience(std::shared_ptr<Client> c, uint32_t amount);
|
||||
void send_set_exp_multiplier(std::shared_ptr<Lobby> l);
|
||||
void send_rare_enemy_index_list(std::shared_ptr<Client> c, const std::vector<size_t>& indexes);
|
||||
|
||||
void send_quest_function_call(Channel& ch, uint16_t function_id);
|
||||
void send_quest_function_call(std::shared_ptr<Client> c, uint16_t function_id);
|
||||
void send_quest_function_call(Channel& ch, uint16_t label);
|
||||
void send_quest_function_call(std::shared_ptr<Client> c, uint16_t label);
|
||||
|
||||
void send_ep3_card_list_update(std::shared_ptr<Client> c);
|
||||
void send_ep3_media_update(
|
||||
|
||||
Reference in New Issue
Block a user