Rework houses to expose more details

This commit is contained in:
Kurt
2020-05-11 22:20:21 -07:00
parent 60faecdc69
commit f50071c0be
9 changed files with 170 additions and 36 deletions

View File

@@ -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()

View File

@@ -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);
}

View 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);
}
}

View File

@@ -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;
}
}
}

View File

@@ -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

View File

@@ -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
}
}

View 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);
}
}

View File

@@ -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; }
}
}

View File

@@ -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);