Enhance achievement / milestone editor

Closes #238

Extract milestone thresholds / milestone count
Abstract out achievement data to workable structure

add giveall/clearall for individual / all, cuz ppl are lazy
This commit is contained in:
Kurt 2020-05-21 22:54:05 -07:00
parent 50f2902b65
commit 406b64333a
11 changed files with 796 additions and 132 deletions

View File

@ -100,16 +100,10 @@ public IReadOnlyList<Item> ItemChest
set => BitConverter.GetBytes(value).CopyTo(Data, Offsets.ItemChest + (Offsets.ItemChestCount * Item.SIZE));
}
public uint[] GetCountAchievement()
public AchievementList Achievements
{
var result = new uint[Offsets.MaxAchievementID];
Buffer.BlockCopy(Data, Offsets.CountAchievement, result, 0, sizeof(uint) * result.Length);
return result;
}
public void SetCountAchievement(uint[] achievements)
{
Buffer.BlockCopy(achievements, 0, Data, Offsets.CountAchievement, sizeof(uint) * Offsets.MaxAchievementID);
get => Data.Slice(Offsets.CountAchievement, AchievementList.SIZE).ToStructure<AchievementList>();
set => value.ToBytes().CopyTo(Data, Offsets.CountAchievement);
}
public bool[] GetRecipeList() => ArrayUtil.GitBitFlagArray(Data, Offsets.Recipes, Offsets.MaxRecipeID + 1);

View File

@ -0,0 +1,118 @@
using System;
using System.Runtime.InteropServices;
#pragma warning disable CS8618, CA1815, CA1819, IDE1006
namespace NHSE.Core
{
[StructLayout(LayoutKind.Sequential, Pack = 4)]
public struct AchievementList
{
/*
u32 CountAchievement[512];
bool ReadAchievement[512][6];
GSaveDate DateAchievement[512][6];
_44c6787c NewAchievement[512];
*/
public const int MaxCount = 512;
public const int SIZE = MaxCount * (4 + (6 * 1) + (6 * 4) + 1);
[field: MarshalAs(UnmanagedType.ByValArray, SizeConst = MaxCount)]
public uint[] Counts { get; set; }
[field: MarshalAs(UnmanagedType.ByValArray, SizeConst = MaxCount)]
public AchievementReadSet[] Read { get; set; }
[field: MarshalAs(UnmanagedType.ByValArray, SizeConst = MaxCount)]
public AchievementDateSet[] Date { get; set; }
[field: MarshalAs(UnmanagedType.ByValArray, SizeConst = MaxCount)]
public byte[] Flags { get; set; }
public readonly void GiveAll(DateTime t)
{
for (int i = 0; i < Counts.Length; i++)
GiveAll(i, t);
}
public readonly void GiveAll(in int index, DateTime t)
{
if (!LifeSupportAchievement.List.TryGetValue(index, out var detail))
{
ClearAll(index);
return;
}
var max = detail.MaxThreshold;
if (max != 0)
Counts[index] = Math.Max(Counts[index], max);
Flags[index] = 1; // visible
for (int i = 0; i < detail.AchievementCount; i++)
{
if (Read[index][i])
continue;
if (!Date[index][i].IsEmpty)
continue;
Date[index][i] = t;
Read[index][i] = true;
t = t.AddDays(1);
}
}
public readonly void ClearAll()
{
for (int i = 0; i < Counts.Length; i++)
ClearAll(i);
}
public readonly void ClearAll(int index)
{
Read[index].Clear();
Date[index].Clear();
Flags[index] = 0;
Counts[index] = 0;
}
}
public struct AchievementReadSet
{
public const int MaxCount = 6;
[field: MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.I1, SizeConst = MaxCount)]
public bool[] Read { get; set; }
public bool this[int index]
{
get => Read[index];
set => Read[index] = value;
}
public void Clear()
{
for (int i = 0; i < Read.Length; i++)
Read[i] = false;
}
}
public struct AchievementDateSet
{
public const int MaxCount = 6;
[field: MarshalAs(UnmanagedType.ByValArray, SizeConst = MaxCount)]
public GSaveDate[] Dates { get; set; }
public GSaveDate this[int index]
{
get => Dates[index];
set => Dates[index] = value;
}
public void Clear()
{
for (int i = 0; i < Dates.Length; i++)
Dates[i] = new GSaveDate();
}
}
}

View File

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
namespace NHSE.Core
{
@ -10,102 +11,130 @@ public class LifeSupportAchievement : INamedValue
public ushort Index { get; }
public string Name { get; }
public LifeSupportAchievement(short land, short player, ushort index, string name)
public readonly int AchievementCount;
public readonly uint Threshold1;
public readonly uint Threshold2;
public readonly uint Threshold3;
public readonly uint Threshold4;
public readonly uint Threshold5;
public LifeSupportAchievement(ushort index, byte max, uint t1, uint t2, uint t3, uint t4, uint t5, short land, short player, string name)
{
Name = name;
Index = index;
AchievementCount = max;
Threshold1 = t1;
Threshold2 = t2;
Threshold3 = t3;
Threshold4 = t4;
Threshold5 = t5;
FlagLand = land;
FlagPlayer = player;
Name = name;
}
public uint MaxThreshold => Math.Max(Threshold1, Math.Max(Threshold2, Math.Max(Threshold3, Math.Max(Threshold4, Threshold5))));
public uint GetThresholdValue(in int row)
{
return row switch
{
0 => Threshold1,
1 => Threshold2,
2 => Threshold3,
3 => Threshold4,
4 => Threshold5,
_ => 0,
};
}
private const string Unknown = "???";
public static readonly IReadOnlyDictionary<int, LifeSupportAchievement> List = new Dictionary<int, LifeSupportAchievement>
{
{0x000, new LifeSupportAchievement( 3, -1, 0000, "CatchFish" )}, // サカナを釣った
{0x001, new LifeSupportAchievement( 3, -1, 0001, "CatchInsect" )}, // ムシを捕まえた
{0x002, new LifeSupportAchievement(-1, -1, 0002, "CatchFishContinuously" )}, // 連続で釣りを成功させた回数の最高記録
{0x003, new LifeSupportAchievement(-1, -1, 0003, "FillFishList" )}, // サカナ図鑑を埋めた
{0x004, new LifeSupportAchievement(-1, -1, 0004, "FillInsectList" )}, // ムシ図鑑を埋めた
{0x005, new LifeSupportAchievement(-1, -1, 0005, "ShootDownBalloon" )}, // ふうせんを撃ち落とした
{0x006, new LifeSupportAchievement( 3, -1, 0006, "PlantFlowerSeed" )}, // 花の種を植えた
{0x007, new LifeSupportAchievement(-1, -1, 0007, "PlantFruit" )}, // 6種類のフルーツを植えた
{0x008, new LifeSupportAchievement(-1, -1, 0008, "PlantTreeSeedling" )}, // 木の苗を植えた
{0x009, new LifeSupportAchievement(-1, -1, 0009, "BuyKabu" )}, // カブを買った
{0x00A, new LifeSupportAchievement(-1, -1, 0010, "HowmuchSellKabu" )}, // カブで得た累計利益
{0x00B, new LifeSupportAchievement(-1, -1, 0011, "HowmuchBuyItem" )}, // これまでの買い物総額
{0x00C, new LifeSupportAchievement(-1, -1, 0012, "BuyItemRcm" )}, // まめきちの店で買い物した
{0x00D, new LifeSupportAchievement(-1, -1, 0013, "SellItemRcm" )}, // まめきちの店で売った
{0x00E, new LifeSupportAchievement(-1, -1, 0014, "RemakeFurniture" )}, // リメイクをした
{0x00F, new LifeSupportAchievement(-1, -1, 0015, "FillCatalog" )}, // カタログの項目数
{0x010, new LifeSupportAchievement(-1, -1, 0016, "BuyItemCatalog" )}, // 通販を利用した
{0x011, new LifeSupportAchievement(-1, -1, 0017, "GotoTotakekeShow" )}, // とたけけのライブを観た
{0x012, new LifeSupportAchievement(-1, -1, 0018, "VisitAnotherIsland" )}, // よその島へのおでかけした
{0x013, new LifeSupportAchievement(-1, -1, 0019, "InviteFriend" )}, // 島にフレンドを招いた
{0x015, new LifeSupportAchievement(-1, -1, 0021, "DayPlayed" )}, // 活動日数
{0x017, new LifeSupportAchievement(-1, -1, 0023, "WaterPlant" )}, // 水やりをした
{0x018, new LifeSupportAchievement(-1, -1, 0024, "BeStungbyWasp" )}, // ハチに刺された
{0x019, new LifeSupportAchievement(-1, -1, 0025, "CatchSeminonukegara" )}, // セミのぬけがらを取った
{0x01A, new LifeSupportAchievement(-1, -1, 0026, "CatchNomi" )}, // ノミを取ってあげた
{0x01B, new LifeSupportAchievement(-1, -1, 0027, "BeStungbyPoisonousInsect" )}, // こわいムシで気絶した
{0x01F, new LifeSupportAchievement(-1, -1, 0031, "FillReactionList" )}, // リアクションを覚えた
{0x020, new LifeSupportAchievement(-1, -1, 0032, "CatchTrash" )}, // ゴミを釣った
{0x021, new LifeSupportAchievement(-1, -1, 0033, "PayImmigrationCost" )}, // 移住費用を支払った
{0x022, new LifeSupportAchievement(-1, -1, 0034, "RepayLoan" )}, // ローンを完済した
{0x023, new LifeSupportAchievement( 3, -1, 0035, "UseMydesign" )}, // マイデザインを使った
{0x024, new LifeSupportAchievement(-1, -1, 0036, "UseMydesignPRO" )}, // マイデザインPROを使った
{0x025, new LifeSupportAchievement(-1, -1, 0037, "SellWeed" )}, // 雑草を引き取ってもらった
{0x027, new LifeSupportAchievement(-1, -1, 0039, "CollectGoldTool" )}, // 6種類の金の道具を集めた
{0x028, new LifeSupportAchievement(-1, -1, 0040, "GetSRankonHHA" )}, // HHAでSを取った
{0x029, new LifeSupportAchievement(-1, -1, 0041, "AttendFishingConvention" )}, // 釣り大会に春夏秋冬参加した
{0x02A, new LifeSupportAchievement(-1, -1, 0042, "AttendInsectConvention" )}, // ムシとり大会に69月参加した
{0x02B, new LifeSupportAchievement(-1, -1, 0043, "HelpGul" )}, // ジョニーを助けた
{0x02C, new LifeSupportAchievement(-1, -1, 0044, "HelpGst" )}, // ゆうたろうを助けた
{0x02D, new LifeSupportAchievement(-1, -1, 0045, "MakePerfectSnowball" )}, // 最高のゆきだるまを作った
{0x02E, new LifeSupportAchievement(-1, -1, 0046, "ReachMyBirthday" )}, // 誕生日を迎えた
{0x02F, new LifeSupportAchievement(-1, -1, 0047, "CelebrateVillagersBithday" )}, // 村民の誕生日を祝ってあげた
{0x030, new LifeSupportAchievement(-1, -1, 0048, "AttendCountdownParty" )}, // カウントダウンに参加した
{0x031, new LifeSupportAchievement(-1, -1, 0049, "BreakTool" )}, // 道具を壊した
{0x032, new LifeSupportAchievement(-1, -1, 0050, "SendLetter" )}, // 手紙を送った
{0x033, new LifeSupportAchievement(-1, -1, 0051, "MakePitfall" )}, // 落とし穴を作った
{0x034, new LifeSupportAchievement(-1, -1, 0052, "FallintoPitfall" )}, // 落とし穴に落ちた
{0x035, new LifeSupportAchievement(-1, -1, 0053, "ImmigratetoIsland" )}, // 島に移住した
{0x036, new LifeSupportAchievement(-1, -1, 0054, "BeFriendwithVillager" )}, // どうぶつとなかよしになった
{0x037, new LifeSupportAchievement(-1, -1, 0055, "FillRecipeList" )}, // 集めたレシピの数
{0x038, new LifeSupportAchievement(-1, -1, 0056, "BootPhone" )}, // スマホを起動した(上級)
{0x039, new LifeSupportAchievement(-1, -1, 0057, "StrikeRock8Times" )}, // コイン岩を8連打した
{0x03A, new LifeSupportAchievement(-1, 320, 0058, "AchieveAppQuest" )}, // 村活クエストを達成した
{0x03B, new LifeSupportAchievement(-1, -1, 0059, "AchieveVillagersQuest" )}, // どうぶつのお願いを聞いた
{0x03C, new LifeSupportAchievement(-1, -1, 0060, "DigBell" )}, // ベルを掘り出した
{0x03D, new LifeSupportAchievement(-1, -1, 0061, "DigFossil" )}, // 化石を掘り出した
{0x03E, new LifeSupportAchievement(-1, -1, 0062, "JudgeFossil" )}, // 化石を鑑定した
{0x03F, new LifeSupportAchievement(66, -1, 0063, "DigShell" )}, // 潮干狩りをした
{0x040, new LifeSupportAchievement( 3, -1, 0064, "PutFurnitureOutside" )}, // 外に家具を置いた
{0x043, new LifeSupportAchievement(-1, -1, 0067, "StrikeWood" )}, // 木からもくざいを出した
{0x044, new LifeSupportAchievement( 3, -1, 0068, "GreetAllVillager" )}, // 村民全員とあいさつをした
{0x045, new LifeSupportAchievement( 3, -1, 0069, "SellShell" )}, // 貝殻を売った
{0x046, new LifeSupportAchievement( 3, -1, 0070, "SellFruit" )}, // フルーツを売った
{0x047, new LifeSupportAchievement(-1, -1, 0071, "CatchBeeContinuously" )}, // ハチを5連続捕まえた
{0x048, new LifeSupportAchievement(-1, -1, 0072, "DIYTool" )}, // 道具をDIYした
{0x049, new LifeSupportAchievement(-1, -1, 0073, "DIYFurniture" )}, // 家具をDIYした
{0x04B, new LifeSupportAchievement( 3, -1, 0075, "TakePicture" )}, // カメラで写真を撮った
{0x04C, new LifeSupportAchievement(-1, -1, 0076, "BootPhoneBeginner" )}, // スマホを起動した(初級)
{0x04D, new LifeSupportAchievement(-1, -1, 0077, "HouseStorageItem" )}, // 家の倉庫に収納したアイテムの数
{0x04E, new LifeSupportAchievement(-1, -1, 0078, "PlaceFurnitureMyHouse" )}, // 家に飾っている家具の数
{0x04F, new LifeSupportAchievement(-1, -1, 0079, "FallFurnitureLeaf" )}, // 木を揺すって家具(葉っぱ)を落とした回数
{0x050, new LifeSupportAchievement(-1, -1, 0080, "DropPresentinWater" )}, // 風船のプレゼントを撃ち水の中に落とした
{0x051, new LifeSupportAchievement(-1, -1, 0081, "ReformMyHome" )}, // マイホームをリフォームした
{0x052, new LifeSupportAchievement(-1, -1, 0082, "ExtendMyHome" )}, // マイホームを増築した
{0x053, new LifeSupportAchievement( 3, -1, 0083, "PostMessageBoard" )}, // 自分の島の掲示板に書き込む
{0x054, new LifeSupportAchievement(-1, -1, 0084, "UseCloset" )}, // クローゼットで着替える
{0x055, new LifeSupportAchievement(-1, -1, 0085, "PrayShootingStar" )}, // 流れ星に祈る
{0x056, new LifeSupportAchievement(59, -1, 0086, "ChangeSymbol" )}, // 島の旗、島メロを変える
{0x057, new LifeSupportAchievement( 3, -1, 0087, "UpdatePassport" )}, // パスポートを更新した
{0x058, new LifeSupportAchievement(-1, 513, 0088, "ModifyIsland" )}, // 各地形造成をやってみた
{0x059, new LifeSupportAchievement(-1, 478, 0089, "BuildFence" )}, // 柵を置く
{0x05A, new LifeSupportAchievement(-1, -1, 0090, "DonateFake" )}, // 寄贈しようとした美術品が贋作だった
{0x05B, new LifeSupportAchievement(-1, -1, 0091, "BuyatTsunekichiShop" )}, // いなりマーケットで芸術品を買った
{0x05C, new LifeSupportAchievement(-1, -1, 0092, "PlantBushSeedling" )}, // 各種低木の苗を植えた
{0x00, new LifeSupportAchievement(000, 5, 0010, 0100, 0500, 2000, 5000, 3, -1, "CatchFish" )}, // サカナを釣った
{0x01, new LifeSupportAchievement(001, 5, 0010, 0100, 0500, 2000, 5000, 3, -1, "CatchInsect" )}, // ムシを捕まえた
{0x02, new LifeSupportAchievement(002, 3, 0010, 0050, 0100, 0000, 0000, -1, -1, "CatchFishContinuously" )}, // 連続で釣りを成功させた回数の最高記録
{0x03, new LifeSupportAchievement(003, 5, 0010, 0020, 0040, 0060, 0080, -1, -1, "FillFishList" )}, // サカナ図鑑を埋めた
{0x04, new LifeSupportAchievement(004, 5, 0010, 0020, 0040, 0060, 0080, -1, -1, "FillInsectList" )}, // ムシ図鑑を埋めた
{0x05, new LifeSupportAchievement(005, 5, 0005, 0020, 0050, 0100, 0300, -1, -1, "ShootDownBalloon" )}, // ふうせんを撃ち落とした
{0x06, new LifeSupportAchievement(006, 5, 0010, 0050, 0100, 0200, 0300, 3, -1, "PlantFlowerSeed" )}, // 花の種を植えた
{0x07, new LifeSupportAchievement(007, 6, 0000, 0000, 0000, 0000, 0000, -1, -1, "PlantFruit" )}, // 6種類のフルーツを植えた
{0x08, new LifeSupportAchievement(008, 3, 0005, 0010, 0030, 0000, 0000, -1, -1, "PlantTreeSeedling" )}, // 木の苗を植えた
{0x09, new LifeSupportAchievement(009, 1, 0001, 0000, 0000, 0000, 0000, -1, -1, "BuyKabu" )}, // カブを買った
{0x0A, new LifeSupportAchievement(010, 5, 1000, 10000, 100000, 1000000, 10000000, -1, -1, "HowmuchSellKabu" )}, // カブで得た累計利益
{0x0B, new LifeSupportAchievement(011, 5, 5000, 50000, 500000, 2000000, 5000000, -1, -1, "HowmuchBuyItem" )}, // これまでの買い物総額
{0x0C, new LifeSupportAchievement(012, 1, 0001, 0000, 0000, 0000, 0000, -1, -1, "BuyItemRcm" )}, // まめきちの店で買い物した
{0x0D, new LifeSupportAchievement(013, 1, 0001, 0000, 0000, 0000, 0000, -1, -1, "SellItemRcm" )}, // まめきちの店で売った
{0x0E, new LifeSupportAchievement(014, 5, 0005, 0020, 0050, 0100, 0200, -1, -1, "RemakeFurniture" )}, // リメイクをした
{0x0F, new LifeSupportAchievement(015, 5, 0100, 0200, 0300, 0400, 0500, -1, -1, "FillCatalog" )}, // カタログの項目数
{0x10, new LifeSupportAchievement(016, 5, 0001, 0020, 0050, 0100, 0200, -1, -1, "BuyItemCatalog" )}, // 通販を利用した
{0x11, new LifeSupportAchievement(017, 5, 0001, 0010, 0030, 0060, 0100, -1, -1, "GotoTotakekeShow" )}, // とたけけのライブを観た
{0x12, new LifeSupportAchievement(018, 3, 0001, 0005, 0010, 0000, 0000, -1, -1, "VisitAnotherIsland" )}, // よその島へのおでかけした
{0x13, new LifeSupportAchievement(019, 3, 0001, 0005, 0010, 0000, 0000, -1, -1, "InviteFriend" )}, // 島にフレンドを招いた
{0x15, new LifeSupportAchievement(021, 5, 0003, 0020, 0050, 0100, 0300, -1, -1, "DayPlayed" )}, // 活動日数
{0x17, new LifeSupportAchievement(023, 5, 0010, 0050, 0100, 0500, 1000, -1, -1, "WaterPlant" )}, // 水やりをした
{0x18, new LifeSupportAchievement(024, 1, 0001, 0000, 0000, 0000, 0000, -1, -1, "BeStungbyWasp" )}, // ハチに刺された
{0x19, new LifeSupportAchievement(025, 1, 0001, 0000, 0000, 0000, 0000, -1, -1, "CatchSeminonukegara" )}, // セミのぬけがらを取った
{0x1A, new LifeSupportAchievement(026, 3, 0001, 0005, 0010, 0000, 0000, -1, -1, "CatchNomi" )}, // ノミを取ってあげた
{0x1B, new LifeSupportAchievement(027, 1, 0001, 0000, 0000, 0000, 0000, -1, -1, "BeStungbyPoisonousInsect" )}, // こわいムシで気絶した
{0x1F, new LifeSupportAchievement(031, 5, 0001, 0010, 0020, 0030, 0042, -1, -1, "FillReactionList" )}, // リアクションを覚えた
{0x20, new LifeSupportAchievement(032, 3, 0003, 0010, 0020, 0000, 0000, -1, -1, "CatchTrash" )}, // ゴミを釣った
{0x21, new LifeSupportAchievement(033, 1, 0001, 0000, 0000, 0000, 0000, -1, -1, "PayImmigrationCost" )}, // 移住費用を支払った
{0x22, new LifeSupportAchievement(034, 1, 0001, 0000, 0000, 0000, 0000, -1, -1, "RepayLoan" )}, // ローンを完済した
{0x23, new LifeSupportAchievement(035, 1, 0001, 0000, 0000, 0000, 0000, 3, -1, "UseMydesign" )}, // マイデザインを使った
{0x24, new LifeSupportAchievement(036, 1, 0001, 0000, 0000, 0000, 0000, -1, -1, "UseMydesignPRO" )}, // マイデザインPROを使った
{0x25, new LifeSupportAchievement(037, 5, 0050, 0200, 1000, 2000, 3000, -1, -1, "SellWeed" )}, // 雑草を引き取ってもらった
{0x27, new LifeSupportAchievement(039, 6, 0000, 0000, 0000, 0000, 0000, -1, -1, "CollectGoldTool" )}, // 6種類の金の道具を集めた
{0x28, new LifeSupportAchievement(040, 1, 0001, 0000, 0000, 0000, 0000, -1, -1, "GetSRankonHHA" )}, // HHAでSを取った
{0x29, new LifeSupportAchievement(041, 4, 0000, 0000, 0000, 0000, 0000, -1, -1, "AttendFishingConvention" )}, // 釣り大会に春夏秋冬参加した
{0x2A, new LifeSupportAchievement(042, 4, 0000, 0000, 0000, 0000, 0000, -1, -1, "AttendInsectConvention" )}, // ムシとり大会に69月参加した
{0x2B, new LifeSupportAchievement(043, 3, 0001, 0010, 0020, 0000, 0000, -1, -1, "HelpGul" )}, // ジョニーを助けた
{0x2C, new LifeSupportAchievement(044, 3, 0001, 0010, 0020, 0000, 0000, -1, -1, "HelpGst" )}, // ゆうたろうを助けた
{0x2D, new LifeSupportAchievement(045, 3, 0001, 0010, 0020, 0000, 0000, -1, -1, "MakePerfectSnowball" )}, // 最高のゆきだるまを作った
{0x2E, new LifeSupportAchievement(046, 1, 0001, 0000, 0000, 0000, 0000, -1, -1, "ReachMyBirthday" )}, // 誕生日を迎えた
{0x2F, new LifeSupportAchievement(047, 3, 0001, 0010, 0020, 0000, 0000, -1, -1, "CelebrateVillagersBithday" )}, // 村民の誕生日を祝ってあげた
{0x30, new LifeSupportAchievement(048, 1, 0001, 0000, 0000, 0000, 0000, -1, -1, "AttendCountdownParty" )}, // カウントダウンに参加した
{0x31, new LifeSupportAchievement(049, 5, 0001, 0020, 0050, 0100, 0200, -1, -1, "BreakTool" )}, // 道具を壊した
{0x32, new LifeSupportAchievement(050, 5, 0005, 0020, 0050, 0100, 0200, -1, -1, "SendLetter" )}, // 手紙を送った
{0x33, new LifeSupportAchievement(051, 1, 0001, 0000, 0000, 0000, 0000, -1, -1, "MakePitfall" )}, // 落とし穴を作った
{0x34, new LifeSupportAchievement(052, 1, 0001, 0000, 0000, 0000, 0000, -1, -1, "FallintoPitfall" )}, // 落とし穴に落ちた
{0x35, new LifeSupportAchievement(053, 1, 0001, 0000, 0000, 0000, 0000, -1, -1, "ImmigratetoIsland" )}, // 島に移住した
{0x36, new LifeSupportAchievement(054, 3, 0001, 0002, 0003, 0000, 0000, -1, -1, "BeFriendwithVillager" )}, // どうぶつとなかよしになった
{0x37, new LifeSupportAchievement(055, 5, 0010, 0050, 0100, 0150, 0200, -1, -1, "FillRecipeList" )}, // 集めたレシピの数
{0x38, new LifeSupportAchievement(056, 1, 1000, 0000, 0000, 0000, 0000, -1, -1, "BootPhone" )}, // スマホを起動した(上級)
{0x39, new LifeSupportAchievement(057, 1, 0001, 0000, 0000, 0000, 0000, -1, -1, "StrikeRock8Times" )}, // コイン岩を8連打した
{0x3A, new LifeSupportAchievement(058, 5, 0005, 0050, 0200, 1000, 3000, -1, 320, "AchieveAppQuest" )}, // 村活クエストを達成した
{0x3B, new LifeSupportAchievement(059, 5, 0001, 0010, 0050, 0100, 0300, -1, -1, "AchieveVillagersQuest" )}, // どうぶつのお願いを聞いた
{0x3C, new LifeSupportAchievement(060, 1, 0001, 0000, 0000, 0000, 0000, -1, -1, "DigBell" )}, // ベルを掘り出した
{0x3D, new LifeSupportAchievement(061, 1, 0001, 0000, 0000, 0000, 0000, -1, -1, "DigFossil" )}, // 化石を掘り出した
{0x3E, new LifeSupportAchievement(062, 5, 0005, 0030, 0100, 0300, 0500, -1, -1, "JudgeFossil" )}, // 化石を鑑定した
{0x3F, new LifeSupportAchievement(063, 5, 0005, 0020, 0050, 0100, 0200, 66, -1, "DigShell" )}, // 潮干狩りをした
{0x40, new LifeSupportAchievement(064, 1, 0010, 0000, 0000, 0000, 0000, 3, -1, "PutFurnitureOutside" )}, // 外に家具を置いた
{0x43, new LifeSupportAchievement(067, 5, 0020, 0100, 0500, 2000, 5000, -1, -1, "StrikeWood" )}, // 木からもくざいを出した
{0x44, new LifeSupportAchievement(068, 5, 0001, 0010, 0020, 0030, 0050, 3, -1, "GreetAllVillager" )}, // 村民全員とあいさつをした
{0x45, new LifeSupportAchievement(069, 5, 0010, 0050, 0200, 0500, 1000, 3, -1, "SellShell" )}, // 貝殻を売った
{0x46, new LifeSupportAchievement(070, 5, 0020, 0100, 0500, 1000, 3000, 3, -1, "SellFruit" )}, // フルーツを売った
{0x47, new LifeSupportAchievement(071, 1, 0001, 0000, 0000, 0000, 0000, -1, -1, "CatchBeeContinuously" )}, // ハチを5連続捕まえた
{0x48, new LifeSupportAchievement(072, 5, 0005, 0050, 0200, 1000, 3000, -1, -1, "DIYTool" )}, // 道具をDIYした
{0x49, new LifeSupportAchievement(073, 5, 0005, 0050, 0200, 1000, 3000, -1, -1, "DIYFurniture" )}, // 家具をDIYした
{0x4B, new LifeSupportAchievement(075, 1, 0001, 0000, 0000, 0000, 0000, 3, -1, "TakePicture" )}, // カメラで写真を撮った
{0x4C, new LifeSupportAchievement(076, 1, 0010, 0000, 0000, 0000, 0000, -1, -1, "BootPhoneBeginner" )}, // スマホを起動した(初級)
{0x4D, new LifeSupportAchievement(077, 5, 0020, 0050, 0100, 0200, 0300, -1, -1, "HouseStorageItem" )}, // 家の倉庫に収納したアイテムの数
{0x4E, new LifeSupportAchievement(078, 5, 0005, 0015, 0030, 0100, 0150, -1, -1, "PlaceFurnitureMyHouse" )}, // 家に飾っている家具の数
{0x4F, new LifeSupportAchievement(079, 5, 0001, 0010, 0020, 0050, 0100, -1, -1, "FallFurnitureLeaf" )}, // 木を揺すって家具(葉っぱ)を落とした回数
{0x50, new LifeSupportAchievement(080, 1, 0001, 0000, 0000, 0000, 0000, -1, -1, "DropPresentinWater" )}, // 風船のプレゼントを撃ち水の中に落とした
{0x51, new LifeSupportAchievement(081, 3, 0001, 0003, 0005, 0000, 0000, -1, -1, "ReformMyHome" )}, // マイホームをリフォームした
{0x52, new LifeSupportAchievement(082, 5, 0001, 0002, 0005, 0006, 0007, -1, -1, "ExtendMyHome" )}, // マイホームを増築した
{0x53, new LifeSupportAchievement(083, 1, 0001, 0000, 0000, 0000, 0000, 3, -1, "PostMessageBoard" )}, // 自分の島の掲示板に書き込む
{0x54, new LifeSupportAchievement(084, 1, 0001, 0000, 0000, 0000, 0000, -1, -1, "UseCloset" )}, // クローゼットで着替える
{0x55, new LifeSupportAchievement(085, 3, 0001, 0030, 0200, 0000, 0000, -1, -1, "PrayShootingStar" )}, // 流れ星に祈る
{0x56, new LifeSupportAchievement(086, 2, 0000, 0000, 0000, 0000, 0000, 59, -1, "ChangeSymbol" )}, // 島の旗、島メロを変える
{0x57, new LifeSupportAchievement(087, 1, 0001, 0000, 0000, 0000, 0000, 3, -1, "UpdatePassport" )}, // パスポートを更新した
{0x58, new LifeSupportAchievement(088, 3, 0000, 0000, 0000, 0000, 0000, -1, 513, "ModifyIsland" )}, // 各地形造成をやってみた
{0x59, new LifeSupportAchievement(089, 1, 0020, 0000, 0000, 0000, 0000, -1, 478, "BuildFence" )}, // 柵を置く
{0x5A, new LifeSupportAchievement(090, 1, 0001, 0000, 0000, 0000, 0000, -1, -1, "DonateFake" )}, // 寄贈しようとした美術品が贋作だった
{0x5B, new LifeSupportAchievement(091, 3, 0001, 0010, 0020, 0000, 0000, -1, -1, "BuyatTsunekichiShop" )}, // いなりマーケットで芸術品を買った
{0x5C, new LifeSupportAchievement(092, 3, 0001, 0005, 0020, 0000, 0000, -1, -1, "PlantBushSeedling" )}, // 各種低木の苗を植えた
};
public static string GetName(int index, uint count, IReadOnlyDictionary<string, string> str)

View File

@ -279,27 +279,44 @@ public static List<string> GetLifeSupportAchievementList(string pathBCSV, string
var findex = dict[0x54706054];
var fname = dict[0x45F320F2];
var fcomment = dict[0x85CF1615];
var fv1 = dict[0x3FE43170];
var fv2 = dict[0x4171A41D];
var fLand = dict[0x3FE43170];
var fPlayer = dict[0x4171A41D];
var fmaxLevel = dict[0x1BE772F0];
var fThreshold1 = dict[0xCE0933FC];
var fThreshold2 = dict[0x89A9492C];
var fThreshold3 = dict[0xB4C9609C];
var fThreshold4 = dict[0x06E9BC8C];
var fThreshold5 = dict[0x3B89953C];
var result = new List<string>();
for (int i = 0; i < bcsv.EntryCount; i++)
{
int readHex(BCSVFieldParam p) => int.Parse(bcsv.ReadValue(i, p).Substring(2), NumberStyles.HexNumber);
var iid = bcsv.ReadValue(i, findex);
var ival = ushort.Parse(iid);
var index = ushort.Parse(iid);
var iv1 = bcsv.ReadValue(i, fv1).Substring(2);
var iv1a = int.Parse(iv1, NumberStyles.HexNumber);
var iv2 = bcsv.ReadValue(i, fv2).Substring(2);
var iv2a = int.Parse(iv2, NumberStyles.HexNumber);
var land = readHex(fLand);
var player = readHex(fPlayer);
var name = bcsv.ReadValue(i, fname).TrimEnd('\0');
var paddedName = $"\"{name}\"".PadRight(30, ' ');
var comment = bcsv.ReadValue(i, fcomment).TrimEnd('\0');
var paddedName = $"\"{name}\"".PadRight(30, ' ');
var v = $"new {nameof(LifeSupportAchievement)}({iv1a,2}, {iv2a,4}, {ival:0000}, {paddedName})";
var kvp = $"{{0x{ival:X3}, {v}}}, // {comment}";
var tmp = bcsv.ReadValue(i, fmaxLevel);
var max = ushort.Parse(tmp);
var t1 = readHex(fThreshold1);
var t2 = readHex(fThreshold2);
var t3 = readHex(fThreshold3);
var t4 = readHex(fThreshold4);
var t5 = readHex(fThreshold5);
var values = $"{max}, {t1:0000}, {t2:0000}, {t3:0000}, {t4:0000}, {t5:0000}";
var v = $"new {nameof(LifeSupportAchievement)}({index:000}, {values}, {land,3}, {player,3}, {paddedName})";
var kvp = $"{{0x{index:X2}, {v}}}, // {comment}";
result.Add(kvp);
}

View File

@ -21,9 +21,12 @@ public class FancyMarshalTests
[Fact] public void MarshalGSaveManpu() => MarshalBytesTestS<GSavePlayerManpu>(GSavePlayerManpu.SIZE);
[Fact] public void MarshalGSavePlayerHandleName() => MarshalBytesTestS<GSavePlayerHandleName>(GSavePlayerHandleName.SIZE);
[Fact] public void MarshalGSaveFg() => MarshalBytesTest<GSaveFg>(GSaveFg.SIZE);
[Fact] public void MarshalGSaveVisitorNpc() => MarshalBytesTest<GSaveVisitorNpc>(GSaveVisitorNpc.SIZE);
[Fact] public void MarshalAchievementList() => MarshalBytesTestS<AchievementList>(AchievementList.SIZE);
private static void MarshalBytesTestS<T>(int size) where T : struct
{
var bytes = new byte[size];

View File

@ -0,0 +1,84 @@
namespace NHSE.WinForms
{
partial class AchievementRow
{
/// <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 Component 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.L_Threshold = new System.Windows.Forms.Label();
this.CHK_Read = new System.Windows.Forms.CheckBox();
this.CAL_Date = new System.Windows.Forms.DateTimePicker();
this.SuspendLayout();
//
// L_Threshold
//
this.L_Threshold.Location = new System.Drawing.Point(3, 0);
this.L_Threshold.Name = "L_Threshold";
this.L_Threshold.Size = new System.Drawing.Size(80, 20);
this.L_Threshold.TabIndex = 32;
this.L_Threshold.Text = "1";
this.L_Threshold.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
//
// CHK_Read
//
this.CHK_Read.AutoSize = true;
this.CHK_Read.Location = new System.Drawing.Point(295, 3);
this.CHK_Read.Name = "CHK_Read";
this.CHK_Read.Size = new System.Drawing.Size(52, 17);
this.CHK_Read.TabIndex = 31;
this.CHK_Read.Text = "Read";
this.CHK_Read.UseVisualStyleBackColor = true;
//
// CAL_Date
//
this.CAL_Date.Location = new System.Drawing.Point(89, 0);
this.CAL_Date.Name = "CAL_Date";
this.CAL_Date.Size = new System.Drawing.Size(200, 20);
this.CAL_Date.TabIndex = 30;
this.CAL_Date.MouseDown += new System.Windows.Forms.MouseEventHandler(this.CAL_Date_MouseDown);
//
// AchievementRow
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.AutoSize = true;
this.Controls.Add(this.L_Threshold);
this.Controls.Add(this.CHK_Read);
this.Controls.Add(this.CAL_Date);
this.Name = "AchievementRow";
this.Size = new System.Drawing.Size(366, 23);
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.Label L_Threshold;
private System.Windows.Forms.CheckBox CHK_Read;
private System.Windows.Forms.DateTimePicker CAL_Date;
}
}

View File

@ -0,0 +1,64 @@
using System;
using System.Drawing;
using System.Windows.Forms;
using NHSE.Core;
namespace NHSE.WinForms
{
public partial class AchievementRow : UserControl
{
public AchievementRow()
{
InitializeComponent();
CAL_Date.MinDate = DateTime.MinValue;
}
public void LoadRow(AchievementList list, in int index, in int row)
{
if (!LifeSupportAchievement.List.TryGetValue(index, out var detail))
{
L_Threshold.Text = "N/A";
}
else
{
if (row >= detail.AchievementCount)
{
L_Threshold.Text = string.Empty;
}
else
{
var threshold = detail.GetThresholdValue(row);
L_Threshold.Text = threshold <= 0 ? "N/A" : threshold.ToString();
}
}
var date = list.Date[index][row];
if (date < CAL_Date.MinDate)
date = CAL_Date.MinDate;
CAL_Date.Value = date;
CHK_Read.Checked = list.Read[index][row];
}
public void SaveRow(AchievementList list, in int index, in int row)
{
list.Date[index][row] = CAL_Date.Value;
list.Read[index][row] = CHK_Read.Checked;
}
public void ChangeCount(in int index, in int row, in uint count)
{
if (!LifeSupportAchievement.List.TryGetValue(index, out var detail))
return;
var threshold = detail.GetThresholdValue(row);
L_Threshold.ForeColor = row < detail.AchievementCount && count >= threshold ? Color.Red : CHK_Read.ForeColor;
}
private void CAL_Date_MouseDown(object sender, MouseEventArgs e)
{
if ((ModifierKeys & Keys.Alt) != 0 && e.Button == MouseButtons.Left)
CAL_Date.Value = CAL_Date.MinDate;
}
}
}

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

@ -354,10 +354,8 @@ private void SavePlayer(int index)
private void B_EditAchievements_Click(object sender, EventArgs e)
{
var pers = SAV.Players[PlayerIndex].Personal;
var records = pers.GetCountAchievement();
using var editor = new AchievementEditor(records);
if (editor.ShowDialog() == DialogResult.OK)
pers.SetCountAchievement(records);
using var editor = new AchievementEditor(pers);
editor.ShowDialog();
}
private void B_EditPlayerFlags_Click(object sender, EventArgs e)

View File

@ -33,13 +33,25 @@ private void InitializeComponent()
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.NUD_Unk = new System.Windows.Forms.NumericUpDown();
this.B_GiveAll = new System.Windows.Forms.Button();
this.B_Clear = new System.Windows.Forms.Button();
this.B_ClearAll = new System.Windows.Forms.Button();
this.AR_6 = new NHSE.WinForms.AchievementRow();
this.AR_5 = new NHSE.WinForms.AchievementRow();
this.AR_4 = new NHSE.WinForms.AchievementRow();
this.AR_3 = new NHSE.WinForms.AchievementRow();
this.AR_2 = new NHSE.WinForms.AchievementRow();
this.AR_1 = new NHSE.WinForms.AchievementRow();
this.B_Max = new System.Windows.Forms.Button();
((System.ComponentModel.ISupportInitialize)(this.NUD_Count)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.NUD_Unk)).BeginInit();
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(200, 226);
this.B_Cancel.Location = new System.Drawing.Point(505, 226);
this.B_Cancel.Name = "B_Cancel";
this.B_Cancel.Size = new System.Drawing.Size(72, 23);
this.B_Cancel.TabIndex = 7;
@ -50,7 +62,7 @@ private void InitializeComponent()
// 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(200, 197);
this.B_Save.Location = new System.Drawing.Point(505, 197);
this.B_Save.Name = "B_Save";
this.B_Save.Size = new System.Drawing.Size(72, 23);
this.B_Save.TabIndex = 6;
@ -66,14 +78,14 @@ private void InitializeComponent()
this.LB_Counts.FormattingEnabled = true;
this.LB_Counts.Location = new System.Drawing.Point(12, 12);
this.LB_Counts.Name = "LB_Counts";
this.LB_Counts.Size = new System.Drawing.Size(177, 238);
this.LB_Counts.Size = new System.Drawing.Size(206, 238);
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(200, 28);
this.NUD_Count.Location = new System.Drawing.Point(227, 33);
this.NUD_Count.Maximum = new decimal(new int[] {
2147483647,
0,
@ -88,17 +100,145 @@ private void InitializeComponent()
//
this.L_Count.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.L_Count.AutoSize = true;
this.L_Count.Location = new System.Drawing.Point(197, 12);
this.L_Count.Location = new System.Drawing.Point(224, 17);
this.L_Count.Name = "L_Count";
this.L_Count.Size = new System.Drawing.Size(38, 13);
this.L_Count.TabIndex = 10;
this.L_Count.Text = "Count:";
//
// ActivityEditor
// NUD_Unk
//
this.NUD_Unk.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.NUD_Unk.Location = new System.Drawing.Point(227, 200);
this.NUD_Unk.Maximum = new decimal(new int[] {
255,
0,
0,
0});
this.NUD_Unk.Name = "NUD_Unk";
this.NUD_Unk.Size = new System.Drawing.Size(51, 20);
this.NUD_Unk.TabIndex = 17;
//
// B_GiveAll
//
this.B_GiveAll.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.B_GiveAll.Location = new System.Drawing.Point(227, 226);
this.B_GiveAll.Name = "B_GiveAll";
this.B_GiveAll.Size = new System.Drawing.Size(100, 23);
this.B_GiveAll.TabIndex = 18;
this.B_GiveAll.Text = "Give All";
this.B_GiveAll.UseVisualStyleBackColor = true;
this.B_GiveAll.Click += new System.EventHandler(this.B_GiveAll_Click);
//
// B_Clear
//
this.B_Clear.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.B_Clear.Location = new System.Drawing.Point(383, 30);
this.B_Clear.Name = "B_Clear";
this.B_Clear.Size = new System.Drawing.Size(72, 23);
this.B_Clear.TabIndex = 19;
this.B_Clear.Text = "Clear";
this.B_Clear.UseVisualStyleBackColor = true;
this.B_Clear.Click += new System.EventHandler(this.B_Clear_Click);
//
// B_ClearAll
//
this.B_ClearAll.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.B_ClearAll.Location = new System.Drawing.Point(333, 226);
this.B_ClearAll.Name = "B_ClearAll";
this.B_ClearAll.Size = new System.Drawing.Size(100, 23);
this.B_ClearAll.TabIndex = 20;
this.B_ClearAll.Text = "Clear All";
this.B_ClearAll.UseVisualStyleBackColor = true;
this.B_ClearAll.Click += new System.EventHandler(this.B_ClearAll_Click);
//
// AR_6
//
this.AR_6.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.AR_6.AutoSize = true;
this.AR_6.Location = new System.Drawing.Point(227, 171);
this.AR_6.Margin = new System.Windows.Forms.Padding(0);
this.AR_6.Name = "AR_6";
this.AR_6.Size = new System.Drawing.Size(350, 23);
this.AR_6.TabIndex = 16;
//
// AR_5
//
this.AR_5.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.AR_5.AutoSize = true;
this.AR_5.Location = new System.Drawing.Point(227, 148);
this.AR_5.Margin = new System.Windows.Forms.Padding(0);
this.AR_5.Name = "AR_5";
this.AR_5.Size = new System.Drawing.Size(350, 23);
this.AR_5.TabIndex = 15;
//
// AR_4
//
this.AR_4.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.AR_4.AutoSize = true;
this.AR_4.Location = new System.Drawing.Point(227, 125);
this.AR_4.Margin = new System.Windows.Forms.Padding(0);
this.AR_4.Name = "AR_4";
this.AR_4.Size = new System.Drawing.Size(350, 23);
this.AR_4.TabIndex = 14;
//
// AR_3
//
this.AR_3.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.AR_3.AutoSize = true;
this.AR_3.Location = new System.Drawing.Point(227, 102);
this.AR_3.Margin = new System.Windows.Forms.Padding(0);
this.AR_3.Name = "AR_3";
this.AR_3.Size = new System.Drawing.Size(350, 23);
this.AR_3.TabIndex = 13;
//
// AR_2
//
this.AR_2.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.AR_2.AutoSize = true;
this.AR_2.Location = new System.Drawing.Point(227, 79);
this.AR_2.Margin = new System.Windows.Forms.Padding(0);
this.AR_2.Name = "AR_2";
this.AR_2.Size = new System.Drawing.Size(350, 23);
this.AR_2.TabIndex = 12;
//
// AR_1
//
this.AR_1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.AR_1.AutoSize = true;
this.AR_1.Location = new System.Drawing.Point(227, 56);
this.AR_1.Margin = new System.Windows.Forms.Padding(0);
this.AR_1.Name = "AR_1";
this.AR_1.Size = new System.Drawing.Size(350, 23);
this.AR_1.TabIndex = 11;
//
// B_Max
//
this.B_Max.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.B_Max.Location = new System.Drawing.Point(305, 30);
this.B_Max.Name = "B_Max";
this.B_Max.Size = new System.Drawing.Size(72, 23);
this.B_Max.TabIndex = 21;
this.B_Max.Text = "Max";
this.B_Max.UseVisualStyleBackColor = true;
this.B_Max.Click += new System.EventHandler(this.B_Max_Click);
//
// AchievementEditor
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(284, 261);
this.ClientSize = new System.Drawing.Size(589, 261);
this.Controls.Add(this.B_Max);
this.Controls.Add(this.B_ClearAll);
this.Controls.Add(this.B_Clear);
this.Controls.Add(this.B_GiveAll);
this.Controls.Add(this.NUD_Unk);
this.Controls.Add(this.AR_6);
this.Controls.Add(this.AR_5);
this.Controls.Add(this.AR_4);
this.Controls.Add(this.AR_3);
this.Controls.Add(this.AR_2);
this.Controls.Add(this.AR_1);
this.Controls.Add(this.L_Count);
this.Controls.Add(this.NUD_Count);
this.Controls.Add(this.LB_Counts);
@ -107,10 +247,11 @@ private void InitializeComponent()
this.Icon = global::NHSE.WinForms.Properties.Resources.icon;
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "ActivityEditor";
this.Name = "AchievementEditor";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "Record Editor";
((System.ComponentModel.ISupportInitialize)(this.NUD_Count)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.NUD_Unk)).EndInit();
this.ResumeLayout(false);
this.PerformLayout();
@ -123,5 +264,16 @@ private void InitializeComponent()
private System.Windows.Forms.ListBox LB_Counts;
private System.Windows.Forms.NumericUpDown NUD_Count;
private System.Windows.Forms.Label L_Count;
private AchievementRow AR_1;
private AchievementRow AR_2;
private AchievementRow AR_3;
private AchievementRow AR_4;
private AchievementRow AR_5;
private AchievementRow AR_6;
private System.Windows.Forms.NumericUpDown NUD_Unk;
private System.Windows.Forms.Button B_GiveAll;
private System.Windows.Forms.Button B_Clear;
private System.Windows.Forms.Button B_ClearAll;
private System.Windows.Forms.Button B_Max;
}
}

View File

@ -6,17 +6,25 @@ namespace NHSE.WinForms
{
public partial class AchievementEditor : Form
{
private readonly uint[] Counts;
private readonly Personal Personal;
private readonly AchievementList List;
private readonly AchievementRow[] Rows;
public AchievementEditor(uint[] counts)
private int Index = -1;
public AchievementEditor(Personal personal)
{
Counts = counts;
InitializeComponent();
this.TranslateInterface(GameInfo.CurrentLanguage);
Personal = personal;
var list = List = personal.Achievements;
var str = GameInfo.Strings.InternalNameTranslation;
for (int i = 0; i < counts.Length; i++)
LB_Counts.Items.Add(LifeSupportAchievement.GetName(i, counts[i], str));
DialogResult = DialogResult.Cancel;
for (int i = 0; i < list.Counts.Length; i++)
LB_Counts.Items.Add(LifeSupportAchievement.GetName(i, list.Counts[i], str));
Rows = new[] {AR_1, AR_2, AR_3, AR_4, AR_5, AR_6};
LB_Counts.SelectedIndex = 0;
}
@ -24,19 +32,27 @@ public AchievementEditor(uint[] counts)
private void B_Save_Click(object sender, EventArgs e)
{
DialogResult = DialogResult.OK;
SaveIndex(Index);
Personal.Achievements = List;
Close();
}
private int Index;
private void NUD_Count_ValueChanged(object sender, EventArgs e)
{
if (Index < 0)
return;
Counts[Index] = (uint) NUD_Count.Value;
LB_Counts.Items[Index] = LifeSupportAchievement.GetName(Index, Counts[Index], GameInfo.Strings.InternalNameTranslation);
List.Counts[Index] = (uint) NUD_Count.Value;
for (int i = 0; i < Rows.Length; i++)
Rows[i].ChangeCount(Index, i, List.Counts[Index]);
SetEntryDescription(Index);
}
private void SetEntryDescription(int index)
{
LB_Counts.Items[index] = LifeSupportAchievement.GetName(index, List.Counts[index], GameInfo.Strings.InternalNameTranslation);
}
private void LB_Counts_SelectedIndexChanged(object sender, EventArgs e)
@ -44,8 +60,77 @@ private void LB_Counts_SelectedIndexChanged(object sender, EventArgs e)
if (LB_Counts.SelectedIndex < 0)
return;
var val = Counts[Index = LB_Counts.SelectedIndex];
NUD_Count.Value = (int) val;
SaveIndex(Index);
LoadIndex(Index = LB_Counts.SelectedIndex);
}
private void LoadIndex(int index)
{
if (index < 0)
return;
for (int i = 0; i < Rows.Length; i++)
Rows[i].LoadRow(List, Index, i);
NUD_Unk.Value = List.Flags[index];
var val = List.Counts[index];
NUD_Count.Value = (int)val;
}
private void SaveIndex(in int index)
{
if (index < 0)
return;
for (int i = 0; i < Rows.Length; i++)
Rows[i].SaveRow(List, index, i);
List.Flags[index] = (byte)NUD_Unk.Value;
// count updated on value changed
}
private void B_Clear_Click(object sender, EventArgs e)
{
List.ClearAll(Index);
LoadIndex(Index);
System.Media.SystemSounds.Asterisk.Play();
}
private void B_Max_Click(object sender, EventArgs e)
{
List.GiveAll(Index, DateTime.Now);
LoadIndex(Index);
System.Media.SystemSounds.Asterisk.Play();
}
private void B_GiveAll_Click(object sender, EventArgs e)
{
SaveIndex(Index);
var index = Index;
Index = -1;
List.GiveAll(DateTime.Now);
for (int i = 0; i < List.Counts.Length; i++)
SetEntryDescription(i);
LoadIndex(Index = index);
System.Media.SystemSounds.Asterisk.Play();
}
private void B_ClearAll_Click(object sender, EventArgs e)
{
SaveIndex(Index);
var index = Index;
Index = -1;
List.ClearAll();
for (int i = 0; i < List.Counts.Length; i++)
SetEntryDescription(i);
LoadIndex(Index = index);
System.Media.SystemSounds.Asterisk.Play();
}
}
}