Add villager memory (per player) editor

Change friendship here.

Can also cheat all friendships (of all villagers, for all saved players) to 255 by shift clicking the drop-down button instead.
This commit is contained in:
Kurt
2020-05-14 16:10:33 -07:00
parent ce47a76e1b
commit a8f111a8f1
12 changed files with 794 additions and 26 deletions

View File

@@ -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<ushort, EventFlagVillagerMemoryPlayer> List = new Dictionary<ushort, EventFlagVillagerMemoryPlayer>
{
{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<string, string> 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}";
}
}
}

View File

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

View File

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

View File

@@ -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<string> GetVillagerEventFlagNames(string pathBCSV, st
return result;
}
private static IEnumerable<string> 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<string>();
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<string> GetLandEventFlagNames(string pathBCSV, string fn = "EventFlagsLandParam.bcsv")
{
var bcsv = BCSVConverter.GetBCSV(pathBCSV, fn);

View File

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

View File

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

View File

@@ -98,6 +98,9 @@
<Compile Update="Subforms\Villager\SaveRoomFloorWallEditor.cs">
<SubType>Form</SubType>
</Compile>
<Compile Update="Subforms\Villager\VillagerMemoryEditor.cs">
<SubType>Form</SubType>
</Compile>
<Compile Update="Subforms\Villager\VillagerFlagEditor.cs">
<SubType>Form</SubType>
</Compile>

View File

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

View File

@@ -0,0 +1,181 @@
namespace NHSE.WinForms
{
partial class VillagerMemoryEditor
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
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;
}
}

View File

@@ -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<byte>();
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<byte> flags, IReadOnlyDictionary<string, string> 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();
}
}
}

View File

@@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

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