1
Fork 0
mirror of https://github.com/redstrate/Kawari.git synced 2025-07-24 21:47:45 +00:00

Make unlocked content persistent

This commit is contained in:
Joshua Goins 2025-07-18 20:13:17 -04:00
parent 234e804953
commit 80d79c9e2f
5 changed files with 99 additions and 5 deletions

View file

@ -65,6 +65,62 @@ pub enum ItemInfoQuery {
ByName(String),
}
// From FFXIVClientStructs
// This is actually indexes of InstanceContentType, but we want nice names.
#[derive(Debug)]
pub enum InstanceContentType {
Raid = 1,
Dungeon = 2,
Guildhests = 3,
Trial = 4,
CrystallineConflict = 5,
Frontlines = 6,
QuestBattle = 7,
BeginnerTraining = 8,
DeepDungeon = 9,
TreasureHuntDungeon = 10,
SeasonalDungeon = 11,
RivalWing = 12,
MaskedCarnivale = 13,
Mahjong = 14,
GoldSaucer = 15,
OceanFishing = 16,
UnrealTrial = 17,
TripleTriad = 18,
VariantDungeon = 19,
CriterionDungeon = 20,
}
impl TryFrom<u8> for InstanceContentType {
type Error = ();
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
1 => Ok(Self::Raid),
2 => Ok(Self::Dungeon),
3 => Ok(Self::Guildhests),
4 => Ok(Self::Trial),
5 => Ok(Self::CrystallineConflict),
6 => Ok(Self::Frontlines),
7 => Ok(Self::QuestBattle),
8 => Ok(Self::BeginnerTraining),
9 => Ok(Self::DeepDungeon),
10 => Ok(Self::TreasureHuntDungeon),
11 => Ok(Self::SeasonalDungeon),
12 => Ok(Self::RivalWing),
13 => Ok(Self::MaskedCarnivale),
14 => Ok(Self::Mahjong),
15 => Ok(Self::GoldSaucer),
16 => Ok(Self::OceanFishing),
17 => Ok(Self::UnrealTrial),
18 => Ok(Self::TripleTriad),
19 => Ok(Self::VariantDungeon),
20 => Ok(Self::CriterionDungeon),
_ => Err(()),
}
}
}
impl GameData {
pub fn new() -> Self {
let config = get_config();
@ -453,6 +509,20 @@ impl GameData {
content_finder_row.TerritoryType().into_u16().copied()
}
/// Gets the content type for the given InstanceContent.
pub fn find_type_for_content(&mut self, content_id: u16) -> Option<InstanceContentType> {
let instance_content_sheet =
InstanceContentSheet::read_from(&mut self.resource, Language::None).unwrap();
let instance_content_row = instance_content_sheet.get_row(content_id as u32)?;
instance_content_row
.InstanceContentType()
.into_u8()
.copied()?
.try_into()
.ok()
}
}
// Simple enum for GameData::get_territory_name

View file

@ -12,7 +12,7 @@ pub use position::Position;
mod gamedata;
pub use gamedata::GameData;
pub use gamedata::{ItemInfo, ItemInfoQuery, TerritoryNameKind};
pub use gamedata::{InstanceContentType, ItemInfo, ItemInfoQuery, TerritoryNameKind};
pub mod workdefinitions;

View file

@ -27,6 +27,7 @@ impl TryFrom<i32> for RemakeMode {
}
}
}
#[cfg(not(target_family = "wasm"))]
impl rusqlite::types::FromSql for RemakeMode {
fn column_result(value: rusqlite::types::ValueRef<'_>) -> rusqlite::types::FromSqlResult<Self> {

View file

@ -86,10 +86,10 @@ pub const COMPLETED_LEVEQUEST_BITMASK_SIZE: usize = 226;
pub const CLASSJOB_ARRAY_SIZE: usize = 32;
/// The size of various raid bitmasks.
pub const RAID_ARRAY_SIZE: usize = 24;
pub const RAID_ARRAY_SIZE: usize = 22;
/// The size of various dungeon bitmasks.
pub const DUNGEON_ARRAY_SIZE: usize = 15;
pub const DUNGEON_ARRAY_SIZE: usize = 17;
/// The size of various guildhest bitmasks.
pub const GUILDHEST_ARRAY_SIZE: usize = 10;

View file

@ -15,8 +15,8 @@ use crate::{
GUILDHEST_ARRAY_SIZE, LogMessageType, PVP_ARRAY_SIZE, RAID_ARRAY_SIZE, TRIAL_ARRAY_SIZE,
UNLOCK_BITMASK_SIZE,
common::{
GameData, INVALID_OBJECT_ID, ItemInfoQuery, ObjectId, ObjectTypeId, Position,
timestamp_secs, value_to_flag_byte_index_value,
GameData, INVALID_OBJECT_ID, InstanceContentType, ItemInfoQuery, ObjectId, ObjectTypeId,
Position, timestamp_secs, value_to_flag_byte_index_value,
},
config::{WorldConfig, get_config},
inventory::{BuyBackList, ContainerType, Inventory, Item, Storage},
@ -998,6 +998,29 @@ impl ZoneConnection {
self.send_quest_information().await;
}
Task::UnlockContent { id } => {
{
let mut game_data = self.gamedata.lock().unwrap();
if let Some(instance_content_type) = game_data.find_type_for_content(*id) {
match instance_content_type {
InstanceContentType::Dungeon => {
let (value, index) = value_to_flag_byte_index_value(*id as u32);
self.player_data.unlocks.unlocked_dungeons[index as usize] |=
value;
}
InstanceContentType::Raid => {}
InstanceContentType::Guildhests => {}
InstanceContentType::Trial => {}
_ => {
tracing::warn!(
"Not sure what to do about {instance_content_type:?} {id}!"
);
}
};
} else {
tracing::warn!("Unknown content {id}!");
}
}
self.actor_control_self(ActorControlSelf {
category: ActorControlCategory::UnlockInstanceContent {
id: *id as u32,