eldenring 0.14.0

Structures, bindings, and utilities for From Software's title Elden Ring
Documentation
use std::ptr::NonNull;

use crate::{
    DLVector,
    cs::{MultiplayRole, MultiplayType, SummonParamType},
    dltx::DLString,
    fd4::{FD4StepBase, FD4StepBaseInterface, FD4Time},
    from_net::{FNString, FNVector},
    position::BlockPosition,
    stl::DLList,
};
use shared::{OwnedPtr, StepperStates};

use super::{BlockId, CSEzTask, CSEzUpdateTask};

#[repr(C)]
#[shared::singleton("CSNetMan")]
pub struct CSNetMan {
    vftable: usize,
    unk8: u32,
    unkc: u32,
    unk10: [u8; 5],
    freeze_game: bool,
    unk16: bool,
    unk17: bool,
    // True if fps is low, prevents you from online play.
    pub low_fps_penalty: bool,
    pub server_connection_lost: bool,
    unk1a: bool,
    unk1b: u8,
    pub block_id: BlockId,
    unk20: BlockId,
    pub play_region_id: u32,
    unk28: [u8; 0x40],
    sos_db: usize,
    wandering_ghost_db: usize,
    /// Keeps track of all all bloodmessages in the world as well as any rating and created
    /// bloodmessages.
    pub blood_message_db: OwnedPtr<CSNetBloodMessageDb>,
    bloodstain_db: usize,
    bonfire_db: usize,
    spiritual_statue_db: usize,
    unk98: usize,
    unka0: usize,
    pub breakin_manager: OwnedPtr<BreakInManager>,
    /// Keeps track of quickmatch gamemode state.
    pub quickmatch_manager: OwnedPtr<QuickmatchManager>,
    visitor_db: usize,
    penalty_manager: usize,
    /// Task that updates the structure (pulls in new data from server, spawn received signs,
    /// stains and messages, spawns ghost replays, etc)
    pub update_task: CSEzUpdateTask<CSEzTask, Self>,
    unkf0: u32,
    unkf4: u32, // Probably padding
    unkf8: usize,
}

#[repr(C)]
pub struct CSNetBloodMessageDb {
    vftable: usize,
    // Contains all CSNetBloodMessageDbItem?
    pub entries: DLList<OwnedPtr<CSNetBloodMessageDbItem>>,
    unk20: usize,
    /// Seemingly contains message data for messages created by local user
    pub created_data: DLList<usize>,
    // Contains ???
    unk40: DLList<usize>,
    unk58: usize,
    blood_message_ins_man_1: usize,
    blood_message_ins_man_2: usize,
    pub discovered_messages: DLList<OwnedPtr<OwnedPtr<CSNetBloodMessageDbItem>>>,
    unk88: [u8; 0xD0],
    /// Hosts any ongoing jobs for evaluations.
    evaluate_job: usize,
    unk160: usize,
}

#[repr(C)]
pub struct CSNetBloodMessageDbItem {
    vftable: usize,
    unk8: u32,
    unkc: u32,
    unk10: u32,
    pub block_id: BlockId,
    unk18: u32,
    pub position_x: f32,
    pub position_y: f32,
    pub position_z: f32,
    pub angle: f32,
    pub template1: u16,
    pub gesture_param: u16,
    pub part1: u16,
    pub infix: u16,
    pub template2: u16,
    pub part2: u16,
    unk38: u16,
    unk3a: u16,
    unk3c: u16,
    unk3e: u16,
    pub message_id: u64,
    unk48: u32,
}

#[repr(C)]
pub struct BreakInData {
    pub block_id: BlockId,
    pub block_pos: BlockPosition,
    pub entryfilelist_id: i32,
    pub summon_param_type: SummonParamType,
    pub multiplay_role: MultiplayRole,
    pub has_password: bool,
    unk1e: u8,
    pub join_data: FNVector<u8>,
}

#[repr(C)]
pub struct BreakInPointManager {
    breakin_points: DLList<()>,
    unk18: [u8; 0x10],
}

#[repr(C)]
pub struct BreakInAreaList {
    pub areas: DLVector<u32>,
    pub count: u32,
}

#[repr(u32)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum BreakInSearchState {
    Idle = 0,
    InSearch = 1,
    ConnectionAttempt = 2,
    CheckingResponse = 3,
}

#[repr(C)]
pub struct BreakInTarget {
    pub player_id: u32,
    pub external_id: FNString,
    pub play_region: u32,
}

#[repr(C)]
pub struct BreakInManager {
    pub multiplay_type: MultiplayType,
    pub targets: FNVector<BreakInTarget>,
    unk20: FNVector<()>,
    /// Data from breakin push
    pub data: BreakInData,
    pub point_manager: BreakInPointManager,
    rebreakin_pos_step: usize,
    pub error_code: i32,
    pub areas: BreakInAreaList,
    unkd0: usize,
    unkd8: usize,
    pub invasion_search_state: BreakInSearchState,
    pub last_update_invasion_search_state: BreakInSearchState,
    pub attempt_interval_timer: FD4Time,
    pub time_out_timer: FD4Time,
    pub is_yellow_costume_region: bool,
    pub is_multi_region: bool,
}

#[repr(C)]
pub struct QuickmatchManager {
    /// Stepper that updates the games quickmatch state.
    pub quickmatching_ctrl: OwnedPtr<CSQuickMatchingCtrl>,
    /// Keeps track of quickmatch settings as well as any participants.
    pub battle_royal_context: OwnedPtr<CSBattleRoyalContext>,
    /// Populated during creation of the QM lobby locally. Either by joining or creating a room.
    active_battle_royal_context: Option<NonNull<CSBattleRoyalContext>>,
    unk18: u32,
    /// List of speffects applied to the players during battle.
    /// Source of names: debug strings
    ///
    /// ```text
    /// 1110 Team A Summon/Respawn                            チームA用召喚・リスポン時
    /// 1111 Team B Summon/Respawn                            チームB用召喚・リスポン時
    /// 1130 Death                                            死亡時
    /// 1100 Kill                                             撃破時
    /// 1140 Crown for 1st Place                              一位時王冠
    /// 1150 Crown for Tied 1st Place                         同率一位時王冠
    /// 1160 Notification to remove 1st place special effects 一位時の特殊効果を消す通知用
    /// 1200 Heal when killing 1st place player               一位者殺害時回復
    /// 1300 Heal when killing tied 1st place player          同率一位者殺害時回復
    /// 1210 Heal when in 1st place                           一位時回復
    /// 1310 Heal when in tied 1st place                      同率一位時回復
    /// ```
    pub utility_sp_effects: [u32; 11],
    // TODO: more fields up to 0xd8
}

#[repr(i32)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, StepperStates)]
pub enum CSQuickMatchingCtrlState {
    /// Stepper is not running.
    NotExecuting = -1,
    /// No quickmatch is active.
    None = 0,
    /// Looking up existing rooms that match the quickmatch settings.
    SearchRegister = 1,
    /// Waiting for a response for the SearchRegister request.
    SearchRegisterWait = 2,
    GuestInviteWait = 3,
    GuestWaitSession = 4,
    GuestReadyWait = 5,
    GuestMoveMap = 6,
    /// People are loaded into the map and match is running.
    GuestInGame = 7,
    HostWaitSession = 8,
    HostInvite = 9,
    HostReadyWait = 10,
    HostReadyWaitBlockList = 11,
    HostMoveMap = 12,
    /// People are loaded into the map and match is running.
    HostInGame = 13,
    /// Match has ended either by completion or error.
    Unregister = 14,
}

/// Source of name: RTTI
#[repr(C)]
pub struct CSQuickMatchingCtrl {
    pub stepper: FD4StepBase<Self, FD4StepBaseInterface, CSQuickMatchingCtrlState>,
    pub context: NonNull<CSBattleRoyalContext>,
    menu_job: usize,
    unkb8: FD4Time,
    unkc8: bool,
    unkc9: bool,
    unkca: bool,
    unkcb: bool,
    unkcc: bool,
    unkcd: bool,
    unkce: [u8; 5],
    unkd3: bool,
    /// Set to true if the client doesn't send the QM "ready" packet in time.
    pub move_map_timed_out: bool,
}

/// Source of name: RTTI
#[repr(C)]
pub struct CSBattleRoyalContext {
    pub quickmatch_context: CSQuickMatchContext,
    /// Required players to be in lobby before quickmatch can kick-off.
    pub match_player_count: u32,
    pub setting: QuickMatchSettings,
    /// Current number of players in the quickmatch lobby.
    pub current_player_count: u32,
    /// Selected arena enum.
    pub venue: QuickMatchVenue,
    /// Password used for the quickmatch lobby.
    pub password: DLString,
    /// Whether or not the quickmatch uses a fixed map instead of random.
    pub is_fixed_map: bool,
    unkf1: u8,
    unkf2: u8,
    unkf3: u8,
    unkf4: u32,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum QuickMatchSize {
    /// Special case for Duel.
    Duel,
    /// One player vs one player in brawl or team mode with no additional allies.
    OneVsOne,
    TwoVsTwo,
    ThreeVsThree,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct QuickMatchSettings(pub u32);

impl QuickMatchSettings {
    /// Whether or not this gamemode allows spirit ashes summoning.
    pub const fn spirit_ashes_allowed(self) -> bool {
        self.0 >= 10
    }
    /// Whether or not this gamemode is brawl mode.
    pub const fn is_brawl_mode(self) -> bool {
        matches!(self.0 % 10, 1..=3)
    }
    /// Whether or not this gamemode is team-based.
    pub const fn is_team_mode(self) -> bool {
        matches!(self.0 % 10, 4..=9)
    }
    /// Whether or not this gamemode uses password for match you with your allies.
    /// Compared to just being password protected lobby where password doesn't affect team composition.
    pub const fn is_allies_password_mode(self) -> bool {
        matches!(self.0 % 10, 7..=9)
    }
    pub const fn match_size(self) -> QuickMatchSize {
        match self.0 % 10 {
            1 | 4 | 7 => QuickMatchSize::OneVsOne,
            2 | 5 | 8 => QuickMatchSize::TwoVsTwo,
            3 | 6 | 9 => QuickMatchSize::ThreeVsThree,
            _ => QuickMatchSize::Duel,
        }
    }
}

/// Source of name: RTTI
#[repr(C)]
pub struct CSQuickMatchContext {
    vtable: usize,
    /// Encodes the battle type (1v1, 2v2, 3v3, etc)
    pub match_settings: QuickMatchSettings,
    /// Map for this map as an integer, 45000000 as an example.
    pub match_map: QuickMatchArena,
    /// Spawn data for the local player.
    pub spawn_data: QuickmatchSpawnData,
    /// Vector of arenas available for quickmatch to randomly select from.
    pub arena_list: FNVector<QuickMatchArena>,
    unk40: DLVector<usize>,
    unk60: DLVector<usize>,
    /// All quickmatch participants.
    pub participants: DLList<QuickmatchParticipant>,
    unk98: u8,
    /// Seems to be indicative of why some QM lobby failed
    pub error_state: u8,
    unk9a: u8,
    unk9b: u8,
    pub venue: QuickMatchVenue,
    unka0: u32,
    unka4: u32,
    unka8: u32,
    unkac: u32,
}

#[repr(C)]
pub struct QuickmatchSpawnData {
    pub block_id: BlockId,
    pub block_position: BlockPosition,
    pub role: u32,
}

#[repr(C)]
pub struct QuickmatchParticipant {}

#[repr(u32)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum QuickMatchVenue {
    Invalid = 0,
    RoyalColosseum = 1,
    LimgraveColosseum = 2,
    CaelidColosseum = 3,
}

#[repr(u32)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum QuickMatchArena {
    Invalid = 0,
    RoyalColosseum = 4500000,
    LimgraveColosseum = 4502000,
    CaelidColosseum = 4501000,
}