diff --git a/NHSE.Core/Structures/Records/EventFlagVillagerMemoryPlayer.cs b/NHSE.Core/Structures/Records/EventFlagVillagerMemoryPlayer.cs new file mode 100644 index 0000000..dcea5d5 --- /dev/null +++ b/NHSE.Core/Structures/Records/EventFlagVillagerMemoryPlayer.cs @@ -0,0 +1,169 @@ +using System.Collections.Generic; + +namespace NHSE.Core +{ + public class EventFlagVillagerMemoryPlayer : INamedValue + { + public readonly byte Initial; + public readonly byte Maximum; + + public ushort Index { get; } + public string Name { get; } + + public EventFlagVillagerMemoryPlayer(byte init, byte max, ushort index, string name) + { + Name = name; + Index = index; + Initial = init; + Maximum = max; + } + + public static readonly IReadOnlyDictionary List = new Dictionary + { + {0x00, new EventFlagVillagerMemoryPlayer(0 , 1 , 000, "TalkFreeMultiDayEventNow" )}, // (ボツ)今、期間型イベント用のFreeFを聞いたか? + {0x01, new EventFlagVillagerMemoryPlayer(0 , 7 , 001, "ContinuousTalkDays" )}, // 何日連続で話しかけられたか + {0x02, new EventFlagVillagerMemoryPlayer(0 , 1 , 002, "SameLand" )}, // 同じ村に住んでいたことがあるか + {0x03, new EventFlagVillagerMemoryPlayer(0 , 1 , 003, "SetGreeting" )}, // 挨拶を設定したことがある + {0x04, new EventFlagVillagerMemoryPlayer(0 , 1 , 004, "EasterGetRecipeFlag" )}, // イースター|このNPCからリアクション会話でレシピを受け取った + {0x05, new EventFlagVillagerMemoryPlayer(0 , 1 , 005, "TalkProgressMuseumBuilt2" )}, // 博物館2への拡張に関するFreeH_Progressを聞いたか? + {0x06, new EventFlagVillagerMemoryPlayer(0 , 1 , 006, "NextMoveOutTalk" )}, // 引越ししたい会話したか + {0x07, new EventFlagVillagerMemoryPlayer(0 , 1 , 007, "TalkMoveOut" )}, // 引越していく状態で会話したか + {0x08, new EventFlagVillagerMemoryPlayer(0 , 7 , 008, "VisitCount" )}, // そのプレイヤーの家に行った回数 + {0x09, new EventFlagVillagerMemoryPlayer(0 , 7 , 009, "VisitedCount" )}, // そのプレイヤーが家に来た回数 + {0x0A, new EventFlagVillagerMemoryPlayer(25, 255, 010, "Friendship" )}, // 親密度 + {0x0B, new EventFlagVillagerMemoryPlayer(0 , 7 , 011, "TalkCountToday" )}, // 今日の会話回数(通算) + {0x0C, new EventFlagVillagerMemoryPlayer(0 , 7 , 012, "TalkCountInNpcHouseToday" )}, // 今日NPCの家での会話回数 + {0x0D, new EventFlagVillagerMemoryPlayer(0 , 1 , 013, "HasAcquaintanceship" )}, // 面識ありか + {0x0E, new EventFlagVillagerMemoryPlayer(0 , 1 , 014, "SitBenchFlag" )}, // NPCをベンチに座らせる + {0x12, new EventFlagVillagerMemoryPlayer(0 , 1 , 018, "TalkedAsSameVillageResident" )}, // 同じ村の住人として会話した + {0x13, new EventFlagVillagerMemoryPlayer(0 , 1 , 019, "InputGreeting" )}, // 挨拶を設定した + {0x14, new EventFlagVillagerMemoryPlayer(0 , 1 , 020, "InputPhraseMemory" )}, // 口癖を設定した + {0x15, new EventFlagVillagerMemoryPlayer(0 , 1 , 021, "InputNickname" )}, // ニックネームを設定した + {0x16, new EventFlagVillagerMemoryPlayer(0 , 1 , 022, "GEventTalkBeforeFlag" )}, // イベント共通|イベント前に会話した? + {0x17, new EventFlagVillagerMemoryPlayer(0 , 1 , 023, "GEventTalkProgressFlag" )}, // イベント共通|イベント中に会話した? + {0x18, new EventFlagVillagerMemoryPlayer(0 , 1 , 024, "GEventTalkAfterFlag" )}, // イベント共通|イベント後に会話した? + {0x19, new EventFlagVillagerMemoryPlayer(0 , 1 , 025, "InduceFailFlag" )}, // 勧誘失敗フラグ + {0x1A, new EventFlagVillagerMemoryPlayer(0 , 1 , 026, "InduceFailAlreadyInduced" )}, // 勧誘失敗理由|既に転入枠に勧誘されたNPCがいる + {0x1B, new EventFlagVillagerMemoryPlayer(0 , 1 , 027, "InduceFailSameNpcLiving" )}, // 勧誘失敗理由|同名NPCが村に住んでいる + {0x1C, new EventFlagVillagerMemoryPlayer(0 , 1 , 028, "InduceFailSameNpcMoveIn" )}, // 勧誘失敗理由|同名NPCが転入確定状態 + {0x1D, new EventFlagVillagerMemoryPlayer(0 , 1 , 029, "IsReceiptWrappingItem" )}, // プレゼントできるアイテムを受け取った + {0x1E, new EventFlagVillagerMemoryPlayer(0 , 1 , 030, "BirhdayNPresentFlag" )}, // NPC誕生日|プレゼント渡した? + {0x1F, new EventFlagVillagerMemoryPlayer(0 , 1 , 031, "NicknameOriginal" )}, // ニックネーム設定本人 + {0x20, new EventFlagVillagerMemoryPlayer(0 , 1 , 032, "BirhdayNTalkInFlag" )}, // NPC誕生日|家に入ったときの会話した? + {0x21, new EventFlagVillagerMemoryPlayer(0 , 1 , 033, "BirhdayNTalkFlag" )}, // NPC誕生日|誕生会で会話した? + {0x22, new EventFlagVillagerMemoryPlayer(0 , 1 , 034, "BirhdayNTalkOutFlag" )}, // NPC誕生日|家から出たときの会話した? + {0x23, new EventFlagVillagerMemoryPlayer(0 , 1 , 035, "BirhdayPTalkFlag" )}, // プレイヤ誕生日|おめでとう会話した? + {0x24, new EventFlagVillagerMemoryPlayer(0 , 1 , 036, "BirhdayPCupcakeFlag" )}, // プレイヤ誕生日|カップケーキ渡した? + {0x25, new EventFlagVillagerMemoryPlayer(0 , 1 , 037, "FriendOfHostPC" )}, // ホストPCのフレンドとして訪問した + {0x26, new EventFlagVillagerMemoryPlayer(0 , 1 , 038, "TalkClothesPToday" )}, // 今日ClothesPを見たか? + {0x27, new EventFlagVillagerMemoryPlayer(0 , 1 , 039, "TalkedDuringSick" )}, // 病気中に会話した + {0x28, new EventFlagVillagerMemoryPlayer(0 , 1 , 040, "InduceFailVillagerMax" )}, // 勧誘失敗理由|住人数が既に上限 + {0x29, new EventFlagVillagerMemoryPlayer(0 , 1 , 041, "FirstMeetGreetAtThisVillage" )}, // この村で初対面の挨拶をした + {0x2A, new EventFlagVillagerMemoryPlayer(0 , 1 , 042, "MoveCancelTalk" )}, // 引越し中止の報告したか + {0x2B, new EventFlagVillagerMemoryPlayer(0 , 1 , 043, "BirthdayPAttendance" )}, // プレイヤ誕生日|出席していた + {0x2C, new EventFlagVillagerMemoryPlayer(0 , 255, 044, "TalkCountInHouseToday" )}, // 室内で会話した回数 (訪問クエスト中) + {0x2D, new EventFlagVillagerMemoryPlayer(0 , 1 , 045, "TalkClothesNToday" )}, // 今日ClothesNを見たか? + {0x2E, new EventFlagVillagerMemoryPlayer(0 , 1 , 046, "TalkWantToday" )}, // 今日Wantを聞いたか? + {0x2F, new EventFlagVillagerMemoryPlayer(0 , 1 , 047, "InduceFailSellPlaceNothing" )}, // 勧誘失敗理由|売り土地が無い + {0x30, new EventFlagVillagerMemoryPlayer(0 , 1 , 048, "IsPlayedQuestVisitN" )}, // 訪問Nをクリアした事ある? + {0x31, new EventFlagVillagerMemoryPlayer(0 , 1 , 049, "AcquaintanceshipFlagOff" )}, // 面識フラグを立てない + {0x32, new EventFlagVillagerMemoryPlayer(0 , 1 , 050, "InducementAtMovingDay" )}, // 転出当日に勧誘された + {0x33, new EventFlagVillagerMemoryPlayer(0 , 1 , 051, "ChokeOffMoveOut" )}, // 引越を引き止められた + {0x34, new EventFlagVillagerMemoryPlayer(0 , 1 , 052, "InducementFlag" )}, // 勧誘されて来たか + {0x35, new EventFlagVillagerMemoryPlayer(0 , 1 , 053, "ApproachQuestOrdered" )}, // アプローチ経由でのクエスト発注か? + {0x37, new EventFlagVillagerMemoryPlayer(0 , 255, 055, "ContinuousTalkCount" )}, // 連続会話回数 + {0x38, new EventFlagVillagerMemoryPlayer(0 , 255, 056, "Again2Count" )}, // XX_Again2を聞いた回数 + {0x39, new EventFlagVillagerMemoryPlayer(0 , 1 , 057, "TalkDIY" )}, // DIY会話をした + {0x3B, new EventFlagVillagerMemoryPlayer(0 , 1 , 059, "NewYearGreetingFlag" )}, // 新年(1月1日)のあいさつをした + {0x3C, new EventFlagVillagerMemoryPlayer(0 , 1 , 060, "GotRecipeorMaterial" )}, // DIYレシピか素材をもらった + {0x3D, new EventFlagVillagerMemoryPlayer(0 , 1 , 061, "GotMaterialItemFull" )}, // 素材をもらうときもちものがいっぱいだった + {0x3E, new EventFlagVillagerMemoryPlayer(0 , 1 , 062, "MailedVisitPLayout" )}, // 訪問Pで手紙を投函した + {0x3F, new EventFlagVillagerMemoryPlayer(0 , 1 , 063, "FirstMeetGreetAtThisVillageToday" )}, // この村で初対面の挨拶をした当日 + {0x40, new EventFlagVillagerMemoryPlayer(0 , 1 , 064, "ReceivedFirstPresent" )}, // NPCから初めてPresentを貰った + {0x41, new EventFlagVillagerMemoryPlayer(0 , 1 , 065, "BanClothesN1Today" )}, // ClothesN1:今日は禁止 + {0x42, new EventFlagVillagerMemoryPlayer(0 , 1 , 066, "BanClothesN2Now" )}, // ClothesN2:今は禁止 + {0x43, new EventFlagVillagerMemoryPlayer(0 , 1 , 067, "BanClothesN1Now" )}, // ClothesN1:今は禁止 + {0x44, new EventFlagVillagerMemoryPlayer(0 , 1 , 068, "TalkWeekNow" )}, // 今、Weekを聞いたか? + {0x45, new EventFlagVillagerMemoryPlayer(0 , 1 , 069, "TalkQuestionsNow" )}, // 今、Questionsを聞いたか? + {0x46, new EventFlagVillagerMemoryPlayer(0 , 1 , 070, "TalkAlwaysNow" )}, // 今、Alwaysを聞いたか? + {0x47, new EventFlagVillagerMemoryPlayer(0 , 1 , 071, "TalkAlwaysABNow" )}, // 今、AlwaysABを聞いたか? + {0x48, new EventFlagVillagerMemoryPlayer(0 , 1 , 072, "TalkWeatherSPNow" )}, // 今、WeatherSPを聞いたか? + {0x49, new EventFlagVillagerMemoryPlayer(0 , 1 , 073, "TalkWeatherNow" )}, // 今、Weatherを聞いたか? + {0x4A, new EventFlagVillagerMemoryPlayer(0 , 1 , 074, "TalkSeasonsNow" )}, // 今、Seasonsを聞いたか? + {0x4B, new EventFlagVillagerMemoryPlayer(0 , 1 , 075, "TalkSpotSPNow" )}, // 今、SpotSPを聞いたか? + {0x4C, new EventFlagVillagerMemoryPlayer(0 , 1 , 076, "TalkSpotNow" )}, // 今、Spotを聞いたか? + {0x4E, new EventFlagVillagerMemoryPlayer(0 , 1 , 078, "TalkItemNNow" )}, // 今、ItemNを聞いたか? + {0x4F, new EventFlagVillagerMemoryPlayer(0 , 1 , 079, "TalkItemPNow" )}, // 今、ItemPを聞いたか? + {0x50, new EventFlagVillagerMemoryPlayer(0 , 1 , 080, "TalkRumorOActionToday" )}, // 今日RumorOP_Action(同じ島の住人P)を聞いたか? + {0x51, new EventFlagVillagerMemoryPlayer(0 , 1 , 081, "TalkRumorVActionToday" )}, // 今日RumorOP_Action(ビジター)を聞いたか? + {0x52, new EventFlagVillagerMemoryPlayer(0 , 1 , 082, "TalkRumorOFavoriteToday" )}, // 今日RumorO_Favoriteを聞いたか? + {0x53, new EventFlagVillagerMemoryPlayer(0 , 1 , 083, "TalkRumorNToday" )}, // 今日このNPCからRumorN1を聞いたか? + {0x54, new EventFlagVillagerMemoryPlayer(0 , 1 , 084, "TalkFreeMovingNow" )}, // 今、FreeD_Movingを聞いたか? + {0x55, new EventFlagVillagerMemoryPlayer(0 , 1 , 085, "TalkFreeEventNow" )}, // 今日FreeE_Eventを聞いたか? + {0x56, new EventFlagVillagerMemoryPlayer(0 , 1 , 086, "TalkSnpcNow" )}, // 今日Snpcを聞いたか? + {0x57, new EventFlagVillagerMemoryPlayer(0 , 1 , 087, "TalkFreeFNow" )}, // 今、FreeFを聞いたか? + {0x58, new EventFlagVillagerMemoryPlayer(0 , 1 , 088, "TalkFreeGNow" )}, // 今、FreeGを聞いたか? + {0x5A, new EventFlagVillagerMemoryPlayer(0 , 1 , 090, "TalkHHAToday" )}, // 今日FreeE_HHAを聞いたか? + {0x5B, new EventFlagVillagerMemoryPlayer(0 , 5 , 091, "TalkReSelectLast" )}, // 再抽選前に聞いたファイル識別番号 + {0x5C, new EventFlagVillagerMemoryPlayer(0 , 1 , 092, "HHABuildCelebraition" )}, // 増築祝いの手紙を出した + {0x5D, new EventFlagVillagerMemoryPlayer(0 , 1 , 093, "ChineseNewYearGreetingFlag" )}, // 旧正月のあいさつをした + {0x5E, new EventFlagVillagerMemoryPlayer(0 , 1 , 094, "TalkFreeZodiacNow" )}, // 今、FreeF_NewYear_Zodiacを聞いたか? + {0x5F, new EventFlagVillagerMemoryPlayer(0 , 1 , 095, "TalkProgressNpcHouseBuilt" )}, // 初期NPCの家に関するFreeH_Progressを聞いたか? + {0x60, new EventFlagVillagerMemoryPlayer(0 , 1 , 096, "TalkProgressMyHouseBuilt" )}, // 現在プレイヤーの家に関するFreeH_Progressを聞いたか? + {0x61, new EventFlagVillagerMemoryPlayer(0 , 1 , 097, "TalkProgressMyHouseBuilt2" )}, // 現在プレイヤーの家(地下室完成)に関するFreeH_Progressを聞いたか? + {0x62, new EventFlagVillagerMemoryPlayer(0 , 1 , 098, "TalkProgressMigrantsQuest" )}, // 移住クエストに関するFreeH_Progressを聞いたか? + {0x63, new EventFlagVillagerMemoryPlayer(0 , 1 , 099, "TalkProgressHelpBuildShop" )}, // 商店の木材集めに関するFreeH_Progressを聞いたか? + {0x64, new EventFlagVillagerMemoryPlayer(0 , 1 , 100, "TalkProgressOfficeBuilt" )}, // 案内所の完成に関するFreeH_Progressを聞いたか? + {0x65, new EventFlagVillagerMemoryPlayer(0 , 1 , 101, "TalkProgressShopBuilt" )}, // 商店の完成に関するFreeH_Progressを聞いたか? + {0x66, new EventFlagVillagerMemoryPlayer(0 , 1 , 102, "TalkProgressShopBuilt2" )}, // 商店の拡張に関するFreeH_Progressを聞いたか? + {0x67, new EventFlagVillagerMemoryPlayer(0 , 1 , 103, "TalkProgressMuseumTent" )}, // フータのテントに関するFreeH_Progressを聞いたか? + {0x68, new EventFlagVillagerMemoryPlayer(0 , 1 , 104, "TalkProgressMuseumBuilt" )}, // 博物館の完成に関するFreeH_Progressを聞いたか? + {0x69, new EventFlagVillagerMemoryPlayer(0 , 1 , 105, "TalkProgressTailorBuilt" )}, // 仕立て屋の完成に関するFreeH_Progressを聞いたか? + {0x6A, new EventFlagVillagerMemoryPlayer(0 , 1 , 106, "TalkProgressBridgeBuilt" )}, // 初めての橋の完成に関するFreeH_Progressを聞いたか? + {0x6B, new EventFlagVillagerMemoryPlayer(0 , 1 , 107, "TalkProgressSlopeBuilt" )}, // 初めての坂の完成に関するFreeH_Progressを聞いたか? + {0x6C, new EventFlagVillagerMemoryPlayer(0 , 1 , 108, "TalkProgressOtherPlayerMoveIn" )}, // 他プレイヤーの移住に関するFreeH_Progressを聞いたか? + {0x6D, new EventFlagVillagerMemoryPlayer(0 , 1 , 109, "TalkRumorPActionToday" )}, // 今日RumorP_Action(現在プレイヤー)を聞いたか? + {0x6E, new EventFlagVillagerMemoryPlayer(0 , 1 , 110, "FirstNpcEarlyAcquaintanceFlag" )}, // NPC宅がテントだった頃からの知り合い? + {0x6F, new EventFlagVillagerMemoryPlayer(0 , 1 , 111, "TalkSickCured" )}, // Curedを聞いたか? + {0x70, new EventFlagVillagerMemoryPlayer(0 , 1 , 112, "StandingUpFlag" )}, // NPCを立たせる + {0x71, new EventFlagVillagerMemoryPlayer(0 , 1 , 113, "StoodUpFlag" )}, // NPCが立った + {0x72, new EventFlagVillagerMemoryPlayer(0 , 1 , 114, "TalkBeefaceToday" )}, // 今日BeeFaceを聞いたか? + {0x73, new EventFlagVillagerMemoryPlayer(0 , 1 , 115, "TalkItchingToday" )}, // 今日Itchingを聞いたか? + {0x74, new EventFlagVillagerMemoryPlayer(0 , 1 , 116, "TalkEarlyLateToday" )}, // 今日EarlyLateを聞いたか? + {0x75, new EventFlagVillagerMemoryPlayer(0 , 1 , 117, "FinishVisitPToday" )}, // 今日訪問Pクエストをしたか? + {0x76, new EventFlagVillagerMemoryPlayer(0 , 1 , 118, "FinishVisitNToday" )}, // 今日訪問Nクエストをしたか? + {0x77, new EventFlagVillagerMemoryPlayer(0 , 1 , 119, "OrderQuest" )}, // クエストを発注or達成した + {0x78, new EventFlagVillagerMemoryPlayer(0 , 1 , 120, "FirstGreetingAtNpcHome" )}, // 引越し当日のダンボール自宅でNPCと初対面会話をした(自分の村) + {0x79, new EventFlagVillagerMemoryPlayer(0 , 1 , 121, "AppF_MoveinGiftOccured" )}, // 引越し挨拶アプローチ会話発生済か? + {0x7A, new EventFlagVillagerMemoryPlayer(0 , 1 , 122, "UncollectedFishishTreasureHunt" )}, // 宝未回収でクエスト終了した + {0x7B, new EventFlagVillagerMemoryPlayer(0 , 7 , 123, "TalkCountInFacilityToday" )}, // 今日の会話回数(施設用) + {0x7C, new EventFlagVillagerMemoryPlayer(0 , 1 , 124, "FriendshipBecomeAcqHToday" )}, // 今日親密度が知人(高)に到達した + {0x7D, new EventFlagVillagerMemoryPlayer(0 , 1 , 125, "FriendshipBecomeAcqH" )}, // 親密度が知人(高)に到達済み + {0x7E, new EventFlagVillagerMemoryPlayer(0 , 3 , 126, "DC_NPCBirthdayFlag" )}, // NPC誕生日|おでかけ先の村でもらうお返しの種類 + {0x7F, new EventFlagVillagerMemoryPlayer(0 , 1 , 127, "EasterCannotGetNnpcRecipeFlag" )}, // イースター|このNPCから受け取り損ねたレシピがある? + }; + + private const string Unknown = "???"; + + public static string GetName(ushort index, uint count, IReadOnlyDictionary str) + { + var dict = List; + if (dict.TryGetValue(index, out var val)) + { + string name = val.Name; + if (str.TryGetValue(name, out var translated)) + name = translated; + return $"{index:00} - {name} = {count}"; + } + return $"{index:00} - {Unknown} = {count}"; + } + + public static string GetName(ushort index, uint count) + { + var dict = List; + if (dict.TryGetValue(index, out var val)) + return $"{index:00} - {val.Name} = {count}"; + return $"{index:00} - {Unknown} = {count}"; + } + } +} diff --git a/NHSE.Core/Structures/Villager/GSaveMemory.cs b/NHSE.Core/Structures/Villager/GSaveMemory.cs new file mode 100644 index 0000000..df78b77 --- /dev/null +++ b/NHSE.Core/Structures/Villager/GSaveMemory.cs @@ -0,0 +1,47 @@ +using System; + +namespace NHSE.Core +{ + public class GSaveMemory : IVillagerOrigin + { + public const int SIZE = 0x5F0; + + public readonly byte[] Data; + + public GSaveMemory(byte[] data) => Data = data; + + public GSavePlayerId PlayerId => Data.Slice(0, GSavePlayerId.SIZE).ToStructure(); + + public uint TownID + { + get => BitConverter.ToUInt32(Data, 0x00); + set => BitConverter.GetBytes(value).CopyTo(Data, 0x00); + } + + public string TownName + { + get => StringUtil.GetString(Data, 0x04, 10); + set => StringUtil.GetBytes(value, 10).CopyTo(Data, 0x04); + } + public byte[] GetTownIdentity() => Data.Slice(0x00, 20); + + public uint PlayerID + { + get => BitConverter.ToUInt32(Data, 0x1C); + set => BitConverter.GetBytes(value).CopyTo(Data, 0x1C); + } + + public string PlayerName + { + get => StringUtil.GetString(Data, 0x20, 10); + set => StringUtil.GetBytes(value, 10).CopyTo(Data, 0x20); + } + + public byte[] GetPlayerIdentity() => Data.Slice(0x1C, 20); + + public byte[] GetEventFlags() => Data.Slice(0x38, 0x100); + public void SetEventFlags(byte[] value) => value.CopyTo(Data, 0x38); + + public byte Friendship { get => Data[0x38 + 10]; set => Data[0x38 + 10] = value; } + }; +} diff --git a/NHSE.Core/Structures/Villager/Villager.cs b/NHSE.Core/Structures/Villager/Villager.cs index acdbbd7..f5fce6c 100644 --- a/NHSE.Core/Structures/Villager/Villager.cs +++ b/NHSE.Core/Structures/Villager/Villager.cs @@ -28,37 +28,42 @@ public VillagerPersonality Personality set => Data[2] = (byte)value; } - public uint TownID + public string TownName => GetMemory(0).TownName; + public byte[] GetTownIdentity() => GetMemory(0).GetTownIdentity(); + public string PlayerName => GetMemory(0).PlayerName; + public byte[] GetPlayerIdentity() => GetMemory(0).GetPlayerIdentity(); + + public const int PlayerMemoryCount = 8; + + public GSaveMemory GetMemory(int index) { - get => BitConverter.ToUInt32(Data, 0x04); - set => BitConverter.GetBytes(value).CopyTo(Data, 0x04); + if ((uint) index >= PlayerMemoryCount) + throw new ArgumentOutOfRangeException(nameof(index)); + + var bytes = Data.Slice(0x4 + (index * GSaveMemory.SIZE), GSaveMemory.SIZE); + return new GSaveMemory(bytes); } - public string TownName + public GSaveMemory[] GetMemories() { - get => StringUtil.GetString(Data, 0x08, 10); - set => StringUtil.GetBytes(value, 10).CopyTo(Data, 0x08); - } - public byte[] GetTownIdentity() => Data.Slice(0x04, 4 + 20); - - public uint PlayerID - { - get => BitConverter.ToUInt32(Data, 0x20); - set => BitConverter.GetBytes(value).CopyTo(Data, 0x20); + var memories = new GSaveMemory[PlayerMemoryCount]; + for (int i = 0; i < memories.Length; i++) + memories[i] = GetMemory(i); + return memories; } - public string PlayerName + public void SetMemory(GSaveMemory memory, int index) { - get => StringUtil.GetString(Data, 0x24, 10); - set => StringUtil.GetBytes(value, 10).CopyTo(Data, 0x24); + if ((uint)index >= PlayerMemoryCount) + throw new ArgumentOutOfRangeException(nameof(index)); + + memory.Data.CopyTo(Data, 0x4 + (index * GSaveMemory.SIZE)); } - public byte[] GetPlayerIdentity() => Data.Slice(0x20, 4 + 20); - - public string TownName2 + public void SetMemories(IReadOnlyList memories) { - get => StringUtil.GetString(Data, 0x5CC, 10); - set => StringUtil.GetBytes(value, 10).CopyTo(Data, 0x5CC); + for (int i = 0; i < memories.Count; i++) + SetMemory(memories[i], i); } public string CatchPhrase @@ -109,5 +114,17 @@ public DesignPatternPRO Design get => new DesignPatternPRO(Data.Slice(0x12128, DesignPatternPRO.SIZE)); set => value.Data.CopyTo(Data, 0x12128); } + + public void SetFriendshipAll(byte value = byte.MaxValue) + { + for (int i = 0; i < PlayerMemoryCount; i++) + { + var m = GetMemory(i); + if (string.IsNullOrEmpty(m.PlayerName)) + continue; + m.Friendship = value; + SetMemory(m, i); + } + } } } diff --git a/NHSE.Parsing/GameBCSVDumper.cs b/NHSE.Parsing/GameBCSVDumper.cs index 2cca82d..a1dad0a 100644 --- a/NHSE.Parsing/GameBCSVDumper.cs +++ b/NHSE.Parsing/GameBCSVDumper.cs @@ -58,6 +58,7 @@ void DumpB(string fn, byte[] bytes, string dir = "bin") DumpS("eventFlagPlayer.txt", GetEventFlagNames(pathBCSV)); DumpS("eventFlagHouse.txt", GetEventFlagHouse(pathBCSV)); DumpS("eventFlagVillager.txt", GetVillagerEventFlagNames(pathBCSV)); + DumpS("eventFlagVillagerMemoryPlayer.txt", GetVillagerEventFlagNamesMemoryPlayer(pathBCSV)); DumpS("eventFlagLand.txt", GetLandEventFlagNames(pathBCSV)); DumpS("ItemKind.txt", GetPossibleEnum(pathBCSV, "ItemParam.bcsv", 0xFC275E86)); @@ -527,6 +528,41 @@ private static IEnumerable GetVillagerEventFlagNames(string pathBCSV, st return result; } + private static IEnumerable GetVillagerEventFlagNamesMemoryPlayer(string pathBCSV, string fn = "EventFlagsNpcMemoryParam.bcsv") + { + var bcsv = BCSVConverter.GetBCSV(pathBCSV, fn); + var dict = bcsv.GetFieldDictionary(); + var finit = dict[0xD55938BD]; + var fmax = dict[0xBD7682F5]; + var fname = dict[0x45F320F2]; + var findex = dict[0x54706054]; + var fcomment = dict[0x85CF1615]; + + var result = new List(); + for (int i = 0; i < bcsv.EntryCount; i++) + { + var isinit = bcsv.ReadValue(i, finit); + var iinit = short.Parse(isinit); + + var ismax = bcsv.ReadValue(i, fmax); + var imax = short.Parse(ismax); + + var iid = bcsv.ReadValue(i, findex); + var index = (ushort)short.Parse(iid); + + var name = bcsv.ReadValue(i, fname).TrimEnd('\0'); + var comment = bcsv.ReadValue(i, fcomment).TrimEnd('\0'); + + var paddedName = $"\"{name}\"".PadRight(45, ' '); + var v = $"new {nameof(EventFlagVillagerMemoryPlayer)}({iinit,-2}, {imax,-3}, {index:000}, {paddedName})"; + var kvp = $"{{0x{index:X2}, {v}}}, // {comment}"; + result.Add(kvp); + } + + result.Sort(); + return result; + } + private static IEnumerable GetLandEventFlagNames(string pathBCSV, string fn = "EventFlagsLandParam.bcsv") { var bcsv = BCSVConverter.GetBCSV(pathBCSV, fn); diff --git a/NHSE.WinForms/Controls/VillagerEditor.Designer.cs b/NHSE.WinForms/Controls/VillagerEditor.Designer.cs index e503f79..8a0e84b 100644 --- a/NHSE.WinForms/Controls/VillagerEditor.Designer.cs +++ b/NHSE.WinForms/Controls/VillagerEditor.Designer.cs @@ -52,6 +52,7 @@ private void InitializeComponent() this.B_EditFurniture = new System.Windows.Forms.ToolStripMenuItem(); this.B_EditVillagerRoom = new System.Windows.Forms.ToolStripMenuItem(); this.B_EditVillagerDesign = new System.Windows.Forms.ToolStripMenuItem(); + this.B_EditVillagerPlayerMemories = new System.Windows.Forms.ToolStripMenuItem(); ((System.ComponentModel.ISupportInitialize)(this.NUD_Variant)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.NUD_Species)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.PB_Villager)).BeginInit(); @@ -255,31 +256,39 @@ private void InitializeComponent() this.CM_EditVillager.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { this.B_EditFurniture, this.B_EditVillagerRoom, - this.B_EditVillagerDesign}); + this.B_EditVillagerDesign, + this.B_EditVillagerPlayerMemories}); this.CM_EditVillager.Name = "CM_EditPlayer"; - this.CM_EditVillager.Size = new System.Drawing.Size(175, 70); + this.CM_EditVillager.Size = new System.Drawing.Size(186, 114); // // B_EditFurniture // this.B_EditFurniture.Name = "B_EditFurniture"; - this.B_EditFurniture.Size = new System.Drawing.Size(180, 22); + this.B_EditFurniture.Size = new System.Drawing.Size(185, 22); this.B_EditFurniture.Text = "Edit Furniture"; this.B_EditFurniture.Click += new System.EventHandler(this.B_EditFurniture_Click); // // B_EditVillagerRoom // this.B_EditVillagerRoom.Name = "B_EditVillagerRoom"; - this.B_EditVillagerRoom.Size = new System.Drawing.Size(180, 22); + this.B_EditVillagerRoom.Size = new System.Drawing.Size(185, 22); this.B_EditVillagerRoom.Text = "Edit Room"; this.B_EditVillagerRoom.Click += new System.EventHandler(this.B_EditVillagerRoom_Click); // // B_EditVillagerDesign // this.B_EditVillagerDesign.Name = "B_EditVillagerDesign"; - this.B_EditVillagerDesign.Size = new System.Drawing.Size(180, 22); + this.B_EditVillagerDesign.Size = new System.Drawing.Size(185, 22); this.B_EditVillagerDesign.Text = "Edit Design Pattern"; this.B_EditVillagerDesign.Click += new System.EventHandler(this.B_EditVillagerDesign_Click); // + // B_EditVillagerPlayerMemories + // + this.B_EditVillagerPlayerMemories.Name = "B_EditVillagerPlayerMemories"; + this.B_EditVillagerPlayerMemories.Size = new System.Drawing.Size(185, 22); + this.B_EditVillagerPlayerMemories.Text = "Edit Player Memories"; + this.B_EditVillagerPlayerMemories.Click += new System.EventHandler(this.B_EditVillagerPlayerMemories_Click); + // // VillagerEditor // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); @@ -340,5 +349,6 @@ private void InitializeComponent() private System.Windows.Forms.ToolStripMenuItem B_EditFurniture; private System.Windows.Forms.ToolStripMenuItem B_EditVillagerRoom; private System.Windows.Forms.ToolStripMenuItem B_EditVillagerDesign; + private System.Windows.Forms.ToolStripMenuItem B_EditVillagerPlayerMemories; } } diff --git a/NHSE.WinForms/Controls/VillagerEditor.cs b/NHSE.WinForms/Controls/VillagerEditor.cs index 4d27ace..19fe42c 100644 --- a/NHSE.WinForms/Controls/VillagerEditor.cs +++ b/NHSE.WinForms/Controls/VillagerEditor.cs @@ -208,5 +208,24 @@ private void B_EditVillagerDesign_Click(object sender, EventArgs e) if (editor.ShowDialog() == DialogResult.OK) v.Design = tmp[0]; } + + private void B_EditVillagerPlayerMemories_Click(object sender, EventArgs e) + { + if (ModifierKeys == Keys.Shift) + { + var prompt = WinFormsUtil.Prompt(MessageBoxButtons.YesNo, MessageStrings.MsgVillagerFriendshipMax); + if (prompt != DialogResult.Yes) + return; + foreach (var villager in Villagers) + villager.SetFriendshipAll(); + System.Media.SystemSounds.Asterisk.Play(); + return; + } + + var v = Villagers[VillagerIndex]; + using var editor = new VillagerMemoryEditor(v); + if (editor.ShowDialog() == DialogResult.OK) + { } // editor saves our changes + } } } diff --git a/NHSE.WinForms/NHSE.WinForms.csproj b/NHSE.WinForms/NHSE.WinForms.csproj index 6de027d..a8134a2 100644 --- a/NHSE.WinForms/NHSE.WinForms.csproj +++ b/NHSE.WinForms/NHSE.WinForms.csproj @@ -98,6 +98,9 @@ Form + + Form + Form diff --git a/NHSE.WinForms/Subforms/Map/MiscDumpHelper.cs b/NHSE.WinForms/Subforms/Map/MiscDumpHelper.cs index d8f3885..b66d026 100644 --- a/NHSE.WinForms/Subforms/Map/MiscDumpHelper.cs +++ b/NHSE.WinForms/Subforms/Map/MiscDumpHelper.cs @@ -7,6 +7,57 @@ namespace NHSE.WinForms { public static class MiscDumpHelper { + public static void DumpVillagerMemoryPlayer(Villager v, GSaveMemory memory) + { + using var sfd = new SaveFileDialog + { + Filter = "New Horizons Villager Player Memory (*.nhvpm)|*.nhvpm|All files (*.*)|*.*", + FileName = $"{v.InternalName}.nhvpm", + }; + if (sfd.ShowDialog() != DialogResult.OK) + return; + + var data = memory.Data; + File.WriteAllBytes(sfd.FileName, data); + } + + public static bool LoadVillagerMemoryPlayer(Villager v, GSaveMemory[] memories, int index) + { + using var ofd = new OpenFileDialog + { + Filter = "New Horizons Villager Player Memory (*.nhvpm)|*.nhvpm|All files (*.*)|*.*", + FileName = $"{v.InternalName}.nhvpm", + }; + if (ofd.ShowDialog() != DialogResult.OK) + return false; + + var file = ofd.FileName; + var fi = new FileInfo(file); + const int expectLength = GSaveMemory.SIZE; + if (fi.Length != expectLength) + { + WinFormsUtil.Error(MessageStrings.MsgCanceling, string.Format(MessageStrings.MsgDataSizeMismatchImport, fi.Length, expectLength)); + return false; + } + + var data = File.ReadAllBytes(file); + var memory = new GSaveMemory(data); + var old = memories[index]; + + if (!memory.IsOriginatedFrom(old)) + { + string msg = string.Format(MessageStrings.MsgDataDidNotOriginateFromHost_0, old.PlayerName); + var result = WinFormsUtil.Prompt(MessageBoxButtons.YesNoCancel, msg, MessageStrings.MsgAskUpdateValues); + if (result == DialogResult.Cancel) + return false; + if (result == DialogResult.Yes) + memory.ChangeOrigins(old, memory.Data); + } + + memories[index] = memory; + return true; + } + public static void DumpMuseum(Museum museum) { using var sfd = new SaveFileDialog diff --git a/NHSE.WinForms/Subforms/Villager/VillagerMemoryEditor.Designer.cs b/NHSE.WinForms/Subforms/Villager/VillagerMemoryEditor.Designer.cs new file mode 100644 index 0000000..200e3c0 --- /dev/null +++ b/NHSE.WinForms/Subforms/Villager/VillagerMemoryEditor.Designer.cs @@ -0,0 +1,181 @@ +namespace NHSE.WinForms +{ + partial class VillagerMemoryEditor + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.B_Cancel = new System.Windows.Forms.Button(); + this.B_Save = new System.Windows.Forms.Button(); + this.LB_Counts = new System.Windows.Forms.ListBox(); + this.NUD_Count = new System.Windows.Forms.NumericUpDown(); + this.L_Count = new System.Windows.Forms.Label(); + this.LB_Players = new System.Windows.Forms.ListBox(); + this.B_Dump = new System.Windows.Forms.Button(); + this.B_Load = new System.Windows.Forms.Button(); + this.GB_Flags = new System.Windows.Forms.GroupBox(); + ((System.ComponentModel.ISupportInitialize)(this.NUD_Count)).BeginInit(); + this.GB_Flags.SuspendLayout(); + this.SuspendLayout(); + // + // B_Cancel + // + this.B_Cancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.B_Cancel.Location = new System.Drawing.Point(321, 268); + this.B_Cancel.Name = "B_Cancel"; + this.B_Cancel.Size = new System.Drawing.Size(72, 23); + this.B_Cancel.TabIndex = 7; + this.B_Cancel.Text = "Cancel"; + this.B_Cancel.UseVisualStyleBackColor = true; + this.B_Cancel.Click += new System.EventHandler(this.B_Cancel_Click); + // + // B_Save + // + this.B_Save.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.B_Save.Location = new System.Drawing.Point(399, 268); + this.B_Save.Name = "B_Save"; + this.B_Save.Size = new System.Drawing.Size(72, 23); + this.B_Save.TabIndex = 6; + this.B_Save.Text = "Save"; + this.B_Save.UseVisualStyleBackColor = true; + this.B_Save.Click += new System.EventHandler(this.B_Save_Click); + // + // LB_Counts + // + this.LB_Counts.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.LB_Counts.FormattingEnabled = true; + this.LB_Counts.Location = new System.Drawing.Point(6, 39); + this.LB_Counts.Name = "LB_Counts"; + this.LB_Counts.Size = new System.Drawing.Size(278, 199); + this.LB_Counts.TabIndex = 8; + this.LB_Counts.SelectedIndexChanged += new System.EventHandler(this.LB_Counts_SelectedIndexChanged); + // + // NUD_Count + // + this.NUD_Count.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.NUD_Count.Location = new System.Drawing.Point(212, 13); + this.NUD_Count.Maximum = new decimal(new int[] { + 255, + 0, + 0, + 0}); + this.NUD_Count.Name = "NUD_Count"; + this.NUD_Count.Size = new System.Drawing.Size(72, 20); + this.NUD_Count.TabIndex = 9; + this.NUD_Count.ValueChanged += new System.EventHandler(this.NUD_Count_ValueChanged); + // + // L_Count + // + this.L_Count.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.L_Count.Location = new System.Drawing.Point(126, 13); + this.L_Count.Name = "L_Count"; + this.L_Count.Size = new System.Drawing.Size(80, 20); + this.L_Count.TabIndex = 10; + this.L_Count.Text = "Value:"; + this.L_Count.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // LB_Players + // + this.LB_Players.FormattingEnabled = true; + this.LB_Players.Location = new System.Drawing.Point(12, 12); + this.LB_Players.Name = "LB_Players"; + this.LB_Players.Size = new System.Drawing.Size(163, 108); + this.LB_Players.TabIndex = 11; + this.LB_Players.SelectedIndexChanged += new System.EventHandler(this.LB_Players_SelectedIndexChanged); + // + // B_Dump + // + this.B_Dump.Location = new System.Drawing.Point(12, 126); + this.B_Dump.Name = "B_Dump"; + this.B_Dump.Size = new System.Drawing.Size(79, 23); + this.B_Dump.TabIndex = 13; + this.B_Dump.Text = "Dump"; + this.B_Dump.UseVisualStyleBackColor = true; + this.B_Dump.Click += new System.EventHandler(this.B_Dump_Click); + // + // B_Load + // + this.B_Load.Location = new System.Drawing.Point(96, 126); + this.B_Load.Name = "B_Load"; + this.B_Load.Size = new System.Drawing.Size(79, 23); + this.B_Load.TabIndex = 12; + this.B_Load.Text = "Load"; + this.B_Load.UseVisualStyleBackColor = true; + this.B_Load.Click += new System.EventHandler(this.B_Load_Click); + // + // GB_Flags + // + this.GB_Flags.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.GB_Flags.Controls.Add(this.LB_Counts); + this.GB_Flags.Controls.Add(this.L_Count); + this.GB_Flags.Controls.Add(this.NUD_Count); + this.GB_Flags.Location = new System.Drawing.Point(181, 12); + this.GB_Flags.Name = "GB_Flags"; + this.GB_Flags.Size = new System.Drawing.Size(290, 245); + this.GB_Flags.TabIndex = 14; + this.GB_Flags.TabStop = false; + this.GB_Flags.Text = "Flags"; + // + // VillagerMemoryEditor + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(483, 303); + this.Controls.Add(this.GB_Flags); + this.Controls.Add(this.B_Dump); + this.Controls.Add(this.B_Load); + this.Controls.Add(this.LB_Players); + this.Controls.Add(this.B_Cancel); + this.Controls.Add(this.B_Save); + this.Icon = global::NHSE.WinForms.Properties.Resources.icon; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "VillagerMemoryEditor"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; + this.Text = "Villager Player Memory Editor"; + ((System.ComponentModel.ISupportInitialize)(this.NUD_Count)).EndInit(); + this.GB_Flags.ResumeLayout(false); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.Button B_Cancel; + private System.Windows.Forms.Button B_Save; + private System.Windows.Forms.ListBox LB_Counts; + private System.Windows.Forms.NumericUpDown NUD_Count; + private System.Windows.Forms.Label L_Count; + private System.Windows.Forms.ListBox LB_Players; + private System.Windows.Forms.Button B_Dump; + private System.Windows.Forms.Button B_Load; + private System.Windows.Forms.GroupBox GB_Flags; + } +} \ No newline at end of file diff --git a/NHSE.WinForms/Subforms/Villager/VillagerMemoryEditor.cs b/NHSE.WinForms/Subforms/Villager/VillagerMemoryEditor.cs new file mode 100644 index 0000000..f93bbfb --- /dev/null +++ b/NHSE.WinForms/Subforms/Villager/VillagerMemoryEditor.cs @@ -0,0 +1,113 @@ +using System; +using System.Collections.Generic; +using System.Windows.Forms; +using NHSE.Core; + +namespace NHSE.WinForms +{ + public partial class VillagerMemoryEditor : Form + { + private readonly Villager Villager; + private readonly GSaveMemory[] Memories; + + private int PlayerIndex = -1; + private int FlagIndex = -1; + private byte[] Flags = Array.Empty(); + + public VillagerMemoryEditor(Villager villager) + { + InitializeComponent(); + this.TranslateInterface(GameInfo.CurrentLanguage); + + Villager = villager; + Memories = villager.GetMemories(); + + for (int i = 0; i < Memories.Length; i++) + LB_Players.Items.Add($"{i} - {Memories[i].PlayerName} ({Memories[i].TownName})"); + + LB_Players.SelectedIndex = 0; + + DialogResult = DialogResult.Cancel; + LB_Counts.SelectedIndex = 0; + } + + private void B_Cancel_Click(object sender, EventArgs e) => Close(); + + private void B_Save_Click(object sender, EventArgs e) + { + SavePlayer(PlayerIndex); + Villager.SetMemories(Memories); + DialogResult = DialogResult.OK; + Close(); + } + + private static string GetFlagDescription(int index, IReadOnlyList flags, IReadOnlyDictionary str) + { + return EventFlagVillagerMemoryPlayer.GetName((ushort)index, flags[index], str); + } + + private void NUD_Count_ValueChanged(object sender, EventArgs e) + { + var flagIndex = FlagIndex; + if (flagIndex < 0) + return; + + Flags[FlagIndex] = (byte) NUD_Count.Value; + LB_Counts.Items[FlagIndex] = GetFlagDescription(flagIndex, Flags, GameInfo.Strings.InternalNameTranslation); + } + + private void LB_Counts_SelectedIndexChanged(object sender, EventArgs e) + { + if (LB_Counts.SelectedIndex < 0) + return; + + NUD_Count.Value = Flags[FlagIndex = LB_Counts.SelectedIndex]; + } + + private void LB_Players_SelectedIndexChanged(object sender, EventArgs e) + { + SavePlayer(PlayerIndex); + PlayerIndex = LB_Players.SelectedIndex; + LoadPlayer(PlayerIndex); + } + + private void LoadPlayer(in int playerIndex) + { + FlagIndex = -1; + var index = LB_Counts.SelectedIndex; + var memory = Memories[playerIndex]; + var flags = memory.GetEventFlags(); + + var str = GameInfo.Strings.InternalNameTranslation; + LB_Counts.Items.Clear(); + for (int i = 0; i < flags.Length; i++) + LB_Counts.Items.Add(GetFlagDescription((byte)i, flags, str)); + + Flags = flags; + LB_Counts.SelectedIndex = FlagIndex = index; + } + + private void SavePlayer(in int playerIndex) + { + if (playerIndex < 0) + return; + + var memory = Memories[playerIndex]; + memory.SetEventFlags(Flags); + } + + private void B_Dump_Click(object sender, EventArgs e) + { + SavePlayer(PlayerIndex); + MiscDumpHelper.DumpVillagerMemoryPlayer(Villager, Memories[PlayerIndex]); + } + + private void B_Load_Click(object sender, EventArgs e) + { + if (!MiscDumpHelper.LoadVillagerMemoryPlayer(Villager, Memories, PlayerIndex)) + return; + LoadPlayer(PlayerIndex); + System.Media.SystemSounds.Asterisk.Play(); + } + } +} diff --git a/NHSE.WinForms/Subforms/Villager/VillagerMemoryEditor.resx b/NHSE.WinForms/Subforms/Villager/VillagerMemoryEditor.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/NHSE.WinForms/Subforms/Villager/VillagerMemoryEditor.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/NHSE.WinForms/Util/MessageStrings.cs b/NHSE.WinForms/Util/MessageStrings.cs index 60b5828..afa5678 100644 --- a/NHSE.WinForms/Util/MessageStrings.cs +++ b/NHSE.WinForms/Util/MessageStrings.cs @@ -40,5 +40,7 @@ public static class MessageStrings public static string MsgTerrainSetElevation0 { get; set; } = "Set the elevation of all tiles on the map to 0?"; public static string MsgTerrainSetAll { get; set; } = "Set the tile from the Tile Editor to all tiles on the map?"; public static string MsgTerrainSetAllSkipExterior { get; set; } = "Do you want to skip the tiles in exterior acres (beach/rocks)?"; + + public static string MsgVillagerFriendshipMax { get; set; } = "Do you want to set all Villager Friendship memories to 255?"; } }