mirror of
https://github.com/kwsch/NHSE.git
synced 2026-08-27 21:34:15 -05:00
Rework houses to expose more details
This commit is contained in:
@@ -52,28 +52,28 @@ public PlayerHouse GetPlayerHouse(int index)
|
||||
{
|
||||
if ((uint)index >= MainSaveOffsets.PlayerCount)
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
return Data.Slice(Offsets.PlayerHouseList + (index * PlayerHouse.SIZE), PlayerHouse.SIZE).ToClass<PlayerHouse>();
|
||||
return new PlayerHouse(Data.Slice(Offsets.PlayerHouseList + (index * PlayerHouse.SIZE), PlayerHouse.SIZE));
|
||||
}
|
||||
|
||||
public void SetPlayerHouse(PlayerHouse h, int index)
|
||||
{
|
||||
if ((uint)index >= MainSaveOffsets.PlayerCount)
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
h.ToBytesClass().CopyTo(Data, Offsets.PlayerHouseList + (index * PlayerHouse.SIZE));
|
||||
h.Data.CopyTo(Data, Offsets.PlayerHouseList + (index * PlayerHouse.SIZE));
|
||||
}
|
||||
|
||||
public VillagerHouse GetVillagerHouse(int index)
|
||||
{
|
||||
if ((uint)index >= MainSaveOffsets.VillagerCount)
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
return Data.Slice(Offsets.NpcHouseList + (index * VillagerHouse.SIZE), VillagerHouse.SIZE).ToClass<VillagerHouse>();
|
||||
return new VillagerHouse(Data.Slice(Offsets.NpcHouseList + (index * VillagerHouse.SIZE), VillagerHouse.SIZE));
|
||||
}
|
||||
|
||||
public void SetVillagerHouse(VillagerHouse h, int index)
|
||||
{
|
||||
if ((uint)index >= MainSaveOffsets.VillagerCount)
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
h.ToBytesClass().CopyTo(Data, Offsets.NpcHouseList + (index * VillagerHouse.SIZE));
|
||||
h.Data.CopyTo(Data, Offsets.NpcHouseList + (index * VillagerHouse.SIZE));
|
||||
}
|
||||
|
||||
public VillagerHouse[] GetVillagerHouses()
|
||||
|
||||
@@ -89,7 +89,7 @@ public static void DumpPlayerHouses(this HorizonSave sav, string path)
|
||||
private static void Dump(this PlayerHouse h, string player, string path)
|
||||
{
|
||||
var dest = Path.Combine(path, $"{player}.nhph");
|
||||
var data = h.ToBytesClass();
|
||||
var data = h.Data;
|
||||
File.WriteAllBytes(dest, data);
|
||||
}
|
||||
|
||||
@@ -112,7 +112,7 @@ private static void Dump(this VillagerHouse h, string path, Villager v)
|
||||
{
|
||||
var name = GameInfo.Strings.GetVillager(v.InternalName);
|
||||
var dest = Path.Combine(path, $"{name}.nhvh");
|
||||
var data = h.ToBytesClass();
|
||||
var data = h.Data;
|
||||
File.WriteAllBytes(dest, data);
|
||||
}
|
||||
|
||||
|
||||
31
NHSE.Core/Structures/Map/Managers/RoomItemManager.cs
Normal file
31
NHSE.Core/Structures/Map/Managers/RoomItemManager.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace NHSE.Core
|
||||
{
|
||||
public class RoomItemManager
|
||||
{
|
||||
// Layer 1: Base Layer Placed Items
|
||||
// Layer 2: Supported Layer Placed Items
|
||||
// Layer 3: North Wall
|
||||
// Layer 4: ???
|
||||
// Layer 5: ???
|
||||
// Layer 6: ???
|
||||
// Layer 7: Rug
|
||||
// Layer 8: ???
|
||||
|
||||
public readonly RoomItemLayer[] Layers;
|
||||
|
||||
public readonly PlayerRoom Room;
|
||||
|
||||
public RoomItemManager(PlayerRoom room)
|
||||
{
|
||||
Layers = room.GetItemLayers();
|
||||
Room = room;
|
||||
Debug.Assert(Layers.Length == 8);
|
||||
}
|
||||
|
||||
public void Save() => Room.SetItemLayers(Layers);
|
||||
|
||||
// public bool IsOccupied(int x, int y) => Layers.Any(z => !z.GetTile(x, y).IsNone);
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,16 @@ public struct GSaveItemName
|
||||
[field: FieldOffset(0x00)] public ushort UniqueID { get; set; }
|
||||
[field: FieldOffset(0x02)] public byte SystemParam { get; set; }
|
||||
[field: FieldOffset(0x03)] public byte AdditionalParam { get; set; }
|
||||
[field: FieldOffset(0x04)] public uint FreeParam { get; set; }
|
||||
[field: FieldOffset(0x04)] public int FreeParam { get; set; }
|
||||
|
||||
public Item ToItem() => this.ToBytes().ToClass<Item>();
|
||||
|
||||
public void CopyFrom(Item item)
|
||||
{
|
||||
UniqueID = item.ItemId;
|
||||
SystemParam = item.SystemParam;
|
||||
AdditionalParam = item.AdditionalParam;
|
||||
FreeParam = item.FreeParam;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ public interface IHouseInfo
|
||||
WallType OrderWallUniqueID { get; set; }
|
||||
RoofType OrderRoofUniqueID { get; set; }
|
||||
DoorKind OrderDoorUniqueID { get; set; }
|
||||
GSaveItemName DoorDecoItemName { get; set; }
|
||||
Item DoorDecoItemName { get; set; }
|
||||
}
|
||||
|
||||
public static class HouseInfoExtensions
|
||||
|
||||
@@ -1,23 +1,88 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using System;
|
||||
|
||||
namespace NHSE.Core
|
||||
{
|
||||
[StructLayout(LayoutKind.Explicit, Size = SIZE, Pack = 4)]
|
||||
public class PlayerHouse : IHouseInfo
|
||||
{
|
||||
public const int SIZE = 0x26400;
|
||||
|
||||
[field: FieldOffset(0x000)] public uint HouseLevel { get; set; }
|
||||
[field: FieldOffset(0x004)] public WallType WallUniqueID { get; set; }
|
||||
[field: FieldOffset(0x006)] public RoofType RoofUniqueID { get; set; }
|
||||
[field: FieldOffset(0x008)] public DoorKind DoorUniqueID { get; set; }
|
||||
[field: FieldOffset(0x00A)] public WallType OrderWallUniqueID { get; set; }
|
||||
[field: FieldOffset(0x00C)] public RoofType OrderRoofUniqueID { get; set; }
|
||||
[field: FieldOffset(0x00E)] public DoorKind OrderDoorUniqueID { get; set; }
|
||||
public readonly byte[] Data;
|
||||
public PlayerHouse(byte[] data) => Data = data;
|
||||
|
||||
[field: FieldOffset(0x263D4)] public GSaveItemName DoorDecoItemName { get; set; }
|
||||
public uint HouseLevel { get => BitConverter.ToUInt32(Data, 0x00); set => BitConverter.GetBytes(value).CopyTo(Data, 0x00); }
|
||||
public WallType WallUniqueID { get => (WallType)BitConverter.ToUInt16(Data, 0x04); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x04); }
|
||||
public RoofType RoofUniqueID { get => (RoofType)BitConverter.ToUInt16(Data, 0x06); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x06); }
|
||||
public DoorKind DoorUniqueID { get => (DoorKind)BitConverter.ToUInt16(Data, 0x08); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x08); }
|
||||
public WallType OrderWallUniqueID { get => (WallType)BitConverter.ToUInt16(Data, 0x0A); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x0A); }
|
||||
public RoofType OrderRoofUniqueID { get => (RoofType)BitConverter.ToUInt16(Data, 0x0C); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x0C); }
|
||||
public DoorKind OrderDoorUniqueID { get => (DoorKind)BitConverter.ToUInt16(Data, 0x0E); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x0E); }
|
||||
|
||||
[field: FieldOffset(0x263E0)] public GSaveItemName PostItemName { get; set; }
|
||||
[field: FieldOffset(0x263E8)] public GSaveItemName OrderPostItemName { get; set; }
|
||||
public EncryptedInt32 Loan
|
||||
{
|
||||
get => EncryptedInt32.Read(Data, 0x10);
|
||||
set => value.Write(Data, 0x10);
|
||||
}
|
||||
|
||||
public EncryptedInt32 Unknown
|
||||
{
|
||||
get => EncryptedInt32.Read(Data, 0x18);
|
||||
set => value.Write(Data, 0x18);
|
||||
}
|
||||
|
||||
public short[] GetEventFlags()
|
||||
{
|
||||
var result = new short[0x100 / 2];
|
||||
Buffer.BlockCopy(Data, 0x20, result, 0, result.Length * sizeof(short));
|
||||
return result;
|
||||
}
|
||||
|
||||
public void SetEventFlags(short[] value) => Buffer.BlockCopy(value, 0, Data, 0x20, value.Length * sizeof(short));
|
||||
|
||||
public const int MaxRoom = 6;
|
||||
private const int RoomStart = 0x120;
|
||||
|
||||
public PlayerRoom GetRoom(int roomIndex)
|
||||
{
|
||||
if ((uint)roomIndex >= MaxRoom)
|
||||
throw new ArgumentOutOfRangeException(nameof(roomIndex));
|
||||
|
||||
var data = Data.Slice(RoomStart + (roomIndex * PlayerRoom.SIZE), PlayerRoom.SIZE);
|
||||
return new PlayerRoom(data);
|
||||
}
|
||||
|
||||
public void SetRoom(int roomIndex, PlayerRoom room)
|
||||
{
|
||||
if ((uint)roomIndex >= MaxRoom)
|
||||
throw new ArgumentOutOfRangeException(nameof(roomIndex));
|
||||
|
||||
room.Data.CopyTo(Data, RoomStart + (roomIndex * PlayerRoom.SIZE));
|
||||
}
|
||||
|
||||
public sbyte NPC1 { get => (sbyte)Data[0x263D0]; set => Data[0x263D0] = (byte)value; }
|
||||
public sbyte NPC2 { get => (sbyte)Data[0x263D1]; set => Data[0x263D1] = (byte)value; }
|
||||
|
||||
// 2bytes padding
|
||||
|
||||
public Item DoorDecoItemName
|
||||
{
|
||||
get => Data.Slice(0x263D4, 8).ToClass<Item>();
|
||||
set => value.ToBytesClass().CopyTo(Data, 0x263D4);
|
||||
}
|
||||
|
||||
public bool PlayerHouseFlag { get => Data[0x263DC] != 0; set => Data[0x263DC] = (byte)(value ? 1 : 0); }
|
||||
|
||||
public Item PostItemName
|
||||
{
|
||||
get => Data.Slice(0x263E0, 8).ToClass<Item>();
|
||||
set => value.ToBytesClass().CopyTo(Data, 0x263E0);
|
||||
}
|
||||
|
||||
public Item OrderPostItemName
|
||||
{
|
||||
get => Data.Slice(0x263E8, 8).ToClass<Item>();
|
||||
set => value.ToBytesClass().CopyTo(Data, 0x263E8);
|
||||
}
|
||||
|
||||
// cockroach @ 0x263f0 -- meh
|
||||
}
|
||||
}
|
||||
|
||||
23
NHSE.Core/Structures/Villager/PlayerRoom.cs
Normal file
23
NHSE.Core/Structures/Villager/PlayerRoom.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace NHSE.Core
|
||||
{
|
||||
public class PlayerRoom
|
||||
{
|
||||
public const int SIZE = 0x65C8;
|
||||
|
||||
public readonly byte[] Data;
|
||||
public PlayerRoom(byte[] data) => Data = data;
|
||||
|
||||
/*
|
||||
s_d8bc748b ItemLayerList[8]; // @0x0 size 0xc80, align 4
|
||||
s_c20a4615 ItemSwitchList[8]; // @0x6400 size 0x34, align 4
|
||||
GSaveAudioInfo AudioInfo; // @0x65a0 size 0x4, align 2
|
||||
GSaveRoomFloorWall RoomFloorWall; // @0x65a4 size 0x24, align 4
|
||||
*/
|
||||
|
||||
public const int LayerCount = 8;
|
||||
public RoomItemLayer[] GetItemLayers() => RoomItemLayer.GetArray(Data.Slice(0, LayerCount * RoomItemLayer.SIZE));
|
||||
public void SetItemLayers(IReadOnlyList<RoomItemLayer> value) => RoomItemLayer.SetArray(value).CopyTo(Data, 0);
|
||||
}
|
||||
}
|
||||
@@ -1,26 +1,33 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using System;
|
||||
|
||||
namespace NHSE.Core
|
||||
{
|
||||
[StructLayout(LayoutKind.Explicit, Size = SIZE, Pack = 4)]
|
||||
public class VillagerHouse : IHouseInfo
|
||||
{
|
||||
public const int SIZE = 0x1D4;
|
||||
public const int ItemCount = 36;
|
||||
|
||||
[field: FieldOffset(0x000)] public uint HouseLevel { get; set; }
|
||||
[field: FieldOffset(0x004)] public uint HouseStatus { get; set; }
|
||||
[field: FieldOffset(0x008)] public WallType WallUniqueID { get; set; }
|
||||
[field: FieldOffset(0x00A)] public RoofType RoofUniqueID { get; set; }
|
||||
[field: FieldOffset(0x00C)] public DoorKind DoorUniqueID { get; set; }
|
||||
[field: FieldOffset(0x00E)] public WallType OrderWallUniqueID { get; set; }
|
||||
[field: FieldOffset(0x010)] public RoofType OrderRoofUniqueID { get; set; }
|
||||
[field: FieldOffset(0x012)] public DoorKind OrderDoorUniqueID { get; set; }
|
||||
public readonly byte[] Data;
|
||||
public VillagerHouse(byte[] data) => Data = data;
|
||||
|
||||
[field: FieldOffset(0x1C4)] public sbyte NPC1 { get; set; }
|
||||
[field: FieldOffset(0x1C5)] public sbyte NPC2 { get; set; }
|
||||
public uint HouseLevel { get => BitConverter.ToUInt32(Data, 0x00); set => BitConverter.GetBytes(value).CopyTo(Data, 0x00); }
|
||||
public uint HouseStatus { get => BitConverter.ToUInt32(Data, 0x04); set => BitConverter.GetBytes(value).CopyTo(Data, 0x04); }
|
||||
public WallType WallUniqueID { get => (WallType)BitConverter.ToUInt16(Data, 0x08); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x08); }
|
||||
public RoofType RoofUniqueID { get => (RoofType)BitConverter.ToUInt16(Data, 0x0A); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x0A); }
|
||||
public DoorKind DoorUniqueID { get => (DoorKind)BitConverter.ToUInt16(Data, 0x0C); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x0C); }
|
||||
public WallType OrderWallUniqueID { get => (WallType)BitConverter.ToUInt16(Data, 0x0E); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x0E); }
|
||||
public RoofType OrderRoofUniqueID { get => (RoofType)BitConverter.ToUInt16(Data, 0x10); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x10); }
|
||||
public DoorKind OrderDoorUniqueID { get => (DoorKind)BitConverter.ToUInt16(Data, 0x12); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x12); }
|
||||
|
||||
[field: FieldOffset(0x1C8)] public GSaveItemName DoorDecoItemName { get; set; }
|
||||
[field: FieldOffset(0x1D0)] public sbyte BuildPlayer { get; set; }
|
||||
public Item DoorDecoItemName
|
||||
{
|
||||
get => Data.Slice(0x1C8, 8).ToClass<Item>();
|
||||
set => value.ToBytesClass().CopyTo(Data, 0x1C8);
|
||||
}
|
||||
|
||||
public sbyte NPC1 { get => (sbyte)Data[0x1C4]; set => Data[0x1C4] = (byte)value; }
|
||||
public sbyte NPC2 { get => (sbyte)Data[0x1C5]; set => Data[0x1C5] = (byte)value; }
|
||||
|
||||
public sbyte BuildPlayer { get => (sbyte)Data[0x1D0]; set => Data[0x1D0] = (byte)value; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,8 +11,6 @@ public class MarshalTests
|
||||
[Fact] public void MarshalTerrainTile() => MarshalTest<TerrainTile>(TerrainTile.SIZE);
|
||||
[Fact] public void MarshalTurnip() => MarshalTest<TurnipStonk>(TurnipStonk.SIZE);
|
||||
[Fact] public void MarshalBuilding() => MarshalTest<Building>(Building.SIZE);
|
||||
[Fact] public void MarshalVillagerHouse() => MarshalTest<VillagerHouse>(VillagerHouse.SIZE);
|
||||
[Fact] public void MarshalPlayerHouse() => MarshalTest<PlayerHouse>(PlayerHouse.SIZE);
|
||||
|
||||
[Fact] public void MarshalVillagerHouseItem() => MarshalTestS<VillagerHouseItem>(VillagerHouseItem.SIZE);
|
||||
[Fact] public void MarshalGSaveItemName() => MarshalTestS<GSaveItemName>(GSaveItemName.SIZE);
|
||||
|
||||
Reference in New Issue
Block a user