1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
use std::num::ParseIntError;
use crate::DLMap;
use crate::cs::{BlockId, ChrAsmEquipment, FaceDataBuffer, MultiplayType, SummonParamType};
use crate::fd4::FD4Time;
use crate::{DLVector, stl::DLList};
use shared::F32Vector3;
use shared::OwnedPtr;
#[repr(C)]
/// Source of name: RTTI
pub struct SosSignMan {
vftable: usize,
/// Tree of the sign entries
pub signs: DLMap<i32, OwnedPtr<SosSignData>>,
/// Tree of sfx's for signs
pub sign_sfx: DLMap<i32, usize>,
/// List of signs that were requested to be summoned
/// Inserting values here will not do anything unless you also have data in `join_data`
pub summon_requests: DLList<i32>,
/// Type of multiplayer for summoning
pub summon_param_type: SummonParamType,
unk54: [u8; 4],
/// List of data for join push notifications
pub join_data: DLList<OwnedPtr<PhantomJoinData>>,
/// Completely unused, no reads or writes other then initialization and destruction
unk70: DLList<[u8; 0x28]>,
unk88: u8,
/// Whether the player is currently in the rescue by red hunter or not.
pub is_in_resque: bool,
display_ghost: usize,
timer: FD4Time,
/// Param ID for [crate::param::WHITE_SIGN_COOL_TIME_PARAM_ST], incremented with each level and capped at 10
pub white_sign_cool_time_param_id: u8,
// _pada9: [u8; 3],
unkac: u32,
/// Vector of sign cooldowns from [crate::param::WHITE_SIGN_COOL_TIME_PARAM_ST]
/// Each time your coop player dies and you have someone in your world
/// you will get a cooldown depending on [crate::param::WHITE_SIGN_COOL_TIME_PARAM_ST].
/// All this cooldown timers are stored in this vector.
pub signs_cooldown: DLVector<f32>,
/// Leftover from Dark Souls 3, never set to true or changed
/// Source of names: Sekiro debug menu
pub override_guardian_of_rosalia_count_enabled: bool,
// _padd1: [u8; 3],
pub override_guardian_of_rosalia_count: u32,
pub override_map_guardian_count_enabled: bool,
// _padd9: [u8; 3],
pub override_map_guardian_count: u32,
pub override_force_join_black_count_enabled: bool,
// _pade1: [u8; 3],
pub override_force_join_black_count: u32,
pub override_sinner_hunter_count_enabled: bool,
// _pade9: [u8; 3],
pub override_sinner_hunter_count: u32,
pub override_berserker_white_count_enabled: bool,
// _padf1: [u8; 3],
pub override_berserker_white_count: u32,
pub override_sinner_hero_count_enabled: bool,
// _padf9: [u8; 3],
pub override_sinner_hero_count: u32,
pub override_cult_white_summon_count_enabled: bool,
// _pad101: [u8; 3],
pub override_cult_white_summon_count: u32,
pub override_normal_white_count_enabled: bool,
// _pad109: [u8; 3],
pub override_normal_white_count: u32,
pub override_red_summon_type_count_enabled: bool,
// _pad111: [u8; 3],
pub override_red_summon_type_count: u32,
}
#[repr(C)]
pub struct DisplayGhostData {
/// Param ID for the equipment
/// See [crate::cs::ChrAsmSlot] for the indexing
pub equipment_param_ids: [i32; 12],
/// Param ID for the armor
/// in order: head, chest, arms, legs, unsued
pub armor_param_ids: [i32; 5],
unk44: [u8; 4],
/// Character gender
pub gender: u8,
unk49: [u8; 11],
/// Info about selected slots and one/two handing
pub asm_equipment: ChrAsmEquipment,
/// Face data for the ghost
pub face_data: FaceDataBuffer,
}
#[repr(C)]
pub struct SosSignData {
pub sign_id: i32,
// _pad4: [u8; 0x4],
/// Server-assigned identifier for the sign
pub sign_identifier: ObjectIdentifier,
/// Block ID where the sign was placed
pub block_id: BlockId,
/// Position of the sign (in physics space)
pub pos: F32Vector3,
/// Rotation of the sign
pub yaw: f32,
pub play_region_id: u32,
unk26: [u8; 0x2],
/// Covenant level of the sign owner
pub vow_type: u8,
unk2b: [u8; 3],
/// Type of multiplayer
pub multiplay_type: MultiplayType,
/// Whether the sign is in the sign pool
pub is_sign_puddle: bool,
unk30: u8,
/// Whether the sign is from the player using same group password
pub from_group_password: bool,
/// Whether to apply multiplayer rules for summoning frame check
/// if true, the game will check if the player is allowed to see the signs in the area,
/// has special effects for the sign, etc.
pub apply_multiplayer_rules: bool,
unk33: u8,
/// Steam ID of the sign owner as a hex string
/// 0 if the sign is NPC
pub steam_id: SteamIdStr,
// _pad56: [u8; 2],
/// Id of the FMG text entry for npc name
pub fmg_name_id: i32,
/// Param ID of the NPC
pub npc_param_id: i32,
unk60: [u8; 0x44],
/// Data for ghost shown when you near the sign
pub display_ghost: DisplayGhostData,
/// Entity ID of the NPC
/// 0 if the sign is not an NPC
pub summoned_npc_entity_id: u32,
/// ID of the event flag that will be set when the NPC is summoned
/// 0 if the sign is not an NPC
pub summon_event_flag_id: u32,
/// ID of the event flag that will be set when the NPC sign is dismissed
/// 0 if the sign is not an NPC
pub dismissal_event_flag_id: u32,
/// Player id of the sign owner from the server
/// 1 if the sign is NPC
pub summonee_player_id: u32,
unk244: [u8; 0x4],
/// Character ID for player-like NPC data
pub character_id: i32,
unk2c4: [u8; 4],
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct ObjectIdentifier(pub i64);
#[repr(u32)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum PhantomJoinState {
/// Push notification sent to other player, awaiting for response
Waiting = 0,
/// Player has accepted the join request
Joining = 1,
}
#[repr(i32)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum SummonJobErrorCode {
/// Default no error state
Default = -70,
/// Sign data not found for the given sign ID
SignDataNotFound = -84,
/// Sign coordinates are invalid and can't be converted to physics space
InvalidCoordinates = -83,
}
#[repr(C)]
/// Data used for join push notification
/// This could be a sign, invasion or something else
pub struct PhantomJoinData {
/// Sign ID if phantom is joining by a sign
/// -1 if it's invasion or something else
pub sign_id: i32,
// _pad4: u32,
/// Server-assigned identifier for the sign
pub sign_identifier: ObjectIdentifier,
/// Time since phantom started joining
/// if exceeds 55 seconds in `Waiting` state or 180 in `Joining` state,
/// the join request will be cancelled
pub join_time: f32,
/// Type of multiplayer player is joining as
pub multiplay_type: MultiplayType,
/// Whether the sign is in the sign pool
/// IMPORTANT: can be anything if multiplay_type is not a sign type
/// because FromSoftware forgot to zero it out in the constructor
pub is_sign_puddle: bool,
// _pad16: [u8; 2],
/// State of the joining player
/// 0 - waiting for response
/// 1 - joining
pub state: u32,
/// Steam ID encoded as hex wide char string with null terminator
pub steam_id: SteamIdStr,
// _pad3e: [u8; 0x2],
/// Entity ID of the NPC
/// 0 if it's not an NPC
pub npc_entity_id: u32,
/// ID of the event flag that will be set when the NPC is summoned
pub summon_event_flag_id: u32,
/// ID of the event flag that will be set when the NPC sign is dismissed
pub dismissal_event_flag_id: u32,
/// Position where phantom will be summoned (in physics space)
pub pos: F32Vector3,
/// Rotation for the phantom
/// This is the same as the sign's rotation if phantom is joining by a sign
pub rotation: F32Vector3,
pub block_id: BlockId,
/// Player id of the sign owner from the server
/// 1 if the sign is NPC
pub summonee_player_id: u32,
/// Error code in case of failure to join
pub summon_job_error_code: SummonJobErrorCode,
/// Whether to apply multiplayer rules for summoning frame check
/// if true, the game will check if the player is allowed to see the signs in the area,
/// has special effects for the sign, etc.
pub apply_multiplayer_rules: bool,
// _pad71: [u8; 0x7],
}
#[repr(C)]
#[derive(Clone, Copy)]
/// SteamID as a hex wchar string with null terminator
pub struct SteamIdStr(pub [u16; 17]);
impl SteamIdStr {
pub fn to_u64(&self) -> Result<u64, ParseIntError> {
let len = self.0.iter().position(|&c| c == 0).unwrap_or(self.0.len());
let s = String::from_utf16_lossy(&self.0[..len]);
u64::from_str_radix(&s, 16)
}
}
impl From<SteamIdStr> for u64 {
fn from(val: SteamIdStr) -> Self {
val.to_u64().unwrap_or_default()
}
}