punish v6.8.18.0
This commit is contained in:
commit
060278c1b7
317 changed files with 554155 additions and 0 deletions
24
GatheringPaths/GatheringPaths.csproj
Normal file
24
GatheringPaths/GatheringPaths.csproj
Normal file
|
@ -0,0 +1,24 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<AssemblyName>GatheringPaths</AssemblyName>
|
||||
<GenerateAssemblyInfo>False</GenerateAssemblyInfo>
|
||||
<TargetFramework>netcoreapp9.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<LangVersion>12.0</LangVersion>
|
||||
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<RootNamespace />
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<None Remove="Questionable.GatheringPaths.GatheringLocationSchema" />
|
||||
<EmbeddedResource Include="Questionable.GatheringPaths.GatheringLocationSchema" LogicalName="Questionable.GatheringPaths.GatheringLocationSchema" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Questionable.Model">
|
||||
<HintPath>..\..\Questionable.Model.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -0,0 +1,115 @@
|
|||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://qstxiv.github.io/schema/gatheringlocation-v1.json",
|
||||
"title": "Gathering Location V1",
|
||||
"description": "A series of gathering locationsk",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"$schema": {
|
||||
"type": "string",
|
||||
"const": "https://qstxiv.github.io/schema/gatheringlocation-v1.json"
|
||||
},
|
||||
"Author": {
|
||||
"description": "Author of the gathering location data",
|
||||
"type": [
|
||||
"string",
|
||||
"array"
|
||||
],
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"Steps": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "https://qstxiv.github.io/schema/quest-v1.json#/$defs/Step"
|
||||
},
|
||||
"minItems": 1
|
||||
},
|
||||
"FlyBetweenNodes": {
|
||||
"description": "If nodes are close enough together, flying makes no sense due to the pathfinding overhead",
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"ExtraQuestItems": {
|
||||
"description": "Some quests (such as Ixal) add quest items to gathering nodes, but there's no clear connection between the item and the node in the sheets",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "integer",
|
||||
"minimum": 2000000
|
||||
}
|
||||
},
|
||||
"Groups": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"Nodes": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"DataId": {
|
||||
"type": "number",
|
||||
"minimum": 30000,
|
||||
"maximum": 50000
|
||||
},
|
||||
"Fly": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"Locations": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"Position": {
|
||||
"$ref": "https://qstxiv.github.io/schema/common-vector3.json"
|
||||
},
|
||||
"MinimumAngle": {
|
||||
"type": "number",
|
||||
"minimum": -360,
|
||||
"maximum": 360
|
||||
},
|
||||
"MaximumAngle": {
|
||||
"type": "number",
|
||||
"minimum": -360,
|
||||
"maximum": 360
|
||||
},
|
||||
"MinimumDistance": {
|
||||
"type": "number",
|
||||
"minimum": 0
|
||||
},
|
||||
"MaximumDistance": {
|
||||
"type": "number",
|
||||
"exclusiveMinimum": 0
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"Position"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"DataId"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"Nodes"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"$schema",
|
||||
"Author",
|
||||
"Steps",
|
||||
"Groups"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
File diff suppressed because it is too large
Load diff
48
LLib/LLib.GameData/EClassJob.cs
Normal file
48
LLib/LLib.GameData/EClassJob.cs
Normal file
|
@ -0,0 +1,48 @@
|
|||
namespace LLib.GameData;
|
||||
|
||||
public enum EClassJob : uint
|
||||
{
|
||||
Adventurer,
|
||||
Gladiator,
|
||||
Pugilist,
|
||||
Marauder,
|
||||
Lancer,
|
||||
Archer,
|
||||
Conjurer,
|
||||
Thaumaturge,
|
||||
Carpenter,
|
||||
Blacksmith,
|
||||
Armorer,
|
||||
Goldsmith,
|
||||
Leatherworker,
|
||||
Weaver,
|
||||
Alchemist,
|
||||
Culinarian,
|
||||
Miner,
|
||||
Botanist,
|
||||
Fisher,
|
||||
Paladin,
|
||||
Monk,
|
||||
Warrior,
|
||||
Dragoon,
|
||||
Bard,
|
||||
WhiteMage,
|
||||
BlackMage,
|
||||
Arcanist,
|
||||
Summoner,
|
||||
Scholar,
|
||||
Rogue,
|
||||
Ninja,
|
||||
Machinist,
|
||||
DarkKnight,
|
||||
Astrologian,
|
||||
Samurai,
|
||||
RedMage,
|
||||
BlueMage,
|
||||
Gunbreaker,
|
||||
Dancer,
|
||||
Reaper,
|
||||
Sage,
|
||||
Viper,
|
||||
Pictomancer
|
||||
}
|
188
LLib/LLib.GameData/EClassJobExtensions.cs
Normal file
188
LLib/LLib.GameData/EClassJobExtensions.cs
Normal file
|
@ -0,0 +1,188 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace LLib.GameData;
|
||||
|
||||
public static class EClassJobExtensions
|
||||
{
|
||||
public static bool IsClass(this EClassJob classJob)
|
||||
{
|
||||
bool flag;
|
||||
switch (classJob)
|
||||
{
|
||||
case EClassJob.Gladiator:
|
||||
case EClassJob.Pugilist:
|
||||
case EClassJob.Marauder:
|
||||
case EClassJob.Lancer:
|
||||
case EClassJob.Archer:
|
||||
case EClassJob.Conjurer:
|
||||
case EClassJob.Thaumaturge:
|
||||
case EClassJob.Arcanist:
|
||||
case EClassJob.Rogue:
|
||||
flag = true;
|
||||
break;
|
||||
default:
|
||||
flag = false;
|
||||
break;
|
||||
}
|
||||
if (!flag && !classJob.IsCrafter())
|
||||
{
|
||||
return classJob.IsGatherer();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool HasBaseClass(this EClassJob classJob)
|
||||
{
|
||||
return (from x in Enum.GetValues<EClassJob>()
|
||||
where x.IsClass()
|
||||
select x).Any((EClassJob x) => x.AsJob() == classJob);
|
||||
}
|
||||
|
||||
public static EClassJob AsJob(this EClassJob classJob)
|
||||
{
|
||||
return classJob switch
|
||||
{
|
||||
EClassJob.Gladiator => EClassJob.Paladin,
|
||||
EClassJob.Marauder => EClassJob.Warrior,
|
||||
EClassJob.Pugilist => EClassJob.Monk,
|
||||
EClassJob.Lancer => EClassJob.Dragoon,
|
||||
EClassJob.Rogue => EClassJob.Ninja,
|
||||
EClassJob.Archer => EClassJob.Bard,
|
||||
EClassJob.Conjurer => EClassJob.WhiteMage,
|
||||
EClassJob.Thaumaturge => EClassJob.BlackMage,
|
||||
EClassJob.Arcanist => EClassJob.Summoner,
|
||||
_ => classJob,
|
||||
};
|
||||
}
|
||||
|
||||
public static bool IsTank(this EClassJob classJob)
|
||||
{
|
||||
switch (classJob)
|
||||
{
|
||||
case EClassJob.Gladiator:
|
||||
case EClassJob.Marauder:
|
||||
case EClassJob.Paladin:
|
||||
case EClassJob.Warrior:
|
||||
case EClassJob.DarkKnight:
|
||||
case EClassJob.Gunbreaker:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsHealer(this EClassJob classJob)
|
||||
{
|
||||
switch (classJob)
|
||||
{
|
||||
case EClassJob.Conjurer:
|
||||
case EClassJob.WhiteMage:
|
||||
case EClassJob.Scholar:
|
||||
case EClassJob.Astrologian:
|
||||
case EClassJob.Sage:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsMelee(this EClassJob classJob)
|
||||
{
|
||||
switch (classJob)
|
||||
{
|
||||
case EClassJob.Pugilist:
|
||||
case EClassJob.Lancer:
|
||||
case EClassJob.Monk:
|
||||
case EClassJob.Dragoon:
|
||||
case EClassJob.Rogue:
|
||||
case EClassJob.Ninja:
|
||||
case EClassJob.Samurai:
|
||||
case EClassJob.Reaper:
|
||||
case EClassJob.Viper:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsPhysicalRanged(this EClassJob classJob)
|
||||
{
|
||||
switch (classJob)
|
||||
{
|
||||
case EClassJob.Archer:
|
||||
case EClassJob.Bard:
|
||||
case EClassJob.Machinist:
|
||||
case EClassJob.Dancer:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsCaster(this EClassJob classJob)
|
||||
{
|
||||
switch (classJob)
|
||||
{
|
||||
case EClassJob.Thaumaturge:
|
||||
case EClassJob.BlackMage:
|
||||
case EClassJob.Arcanist:
|
||||
case EClassJob.Summoner:
|
||||
case EClassJob.RedMage:
|
||||
case EClassJob.BlueMage:
|
||||
case EClassJob.Pictomancer:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool DealsPhysicalDamage(this EClassJob classJob)
|
||||
{
|
||||
if (!classJob.IsTank() && !classJob.IsMelee())
|
||||
{
|
||||
return classJob.IsPhysicalRanged();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool DealsMagicDamage(this EClassJob classJob)
|
||||
{
|
||||
if (!classJob.IsHealer())
|
||||
{
|
||||
return classJob.IsCaster();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool IsCrafter(this EClassJob classJob)
|
||||
{
|
||||
if (classJob >= EClassJob.Carpenter)
|
||||
{
|
||||
return classJob <= EClassJob.Culinarian;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool IsGatherer(this EClassJob classJob)
|
||||
{
|
||||
if (classJob >= EClassJob.Miner)
|
||||
{
|
||||
return classJob <= EClassJob.Fisher;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static string ToFriendlyString(this EClassJob classJob)
|
||||
{
|
||||
return classJob switch
|
||||
{
|
||||
EClassJob.WhiteMage => "White Mage",
|
||||
EClassJob.BlackMage => "Black Mage",
|
||||
EClassJob.DarkKnight => "Dark Knight",
|
||||
EClassJob.RedMage => "Red Mage",
|
||||
EClassJob.BlueMage => "Blue Mage",
|
||||
_ => classJob.ToString(),
|
||||
};
|
||||
}
|
||||
}
|
60
LLib/LLib.GameData/ETerritoryIntendedUse.cs
Normal file
60
LLib/LLib.GameData/ETerritoryIntendedUse.cs
Normal file
|
@ -0,0 +1,60 @@
|
|||
namespace LLib.GameData;
|
||||
|
||||
public enum ETerritoryIntendedUse : byte
|
||||
{
|
||||
CityArea = 0,
|
||||
OpenWorld = 1,
|
||||
Inn = 2,
|
||||
Dungeon = 3,
|
||||
VariantDungeon = 4,
|
||||
Gaol = 5,
|
||||
StartingArea = 6,
|
||||
QuestArea = 7,
|
||||
AllianceRaid = 8,
|
||||
QuestBattle = 9,
|
||||
Trial = 10,
|
||||
QuestArea2 = 12,
|
||||
ResidentialArea = 13,
|
||||
HousingInstances = 14,
|
||||
QuestArea3 = 15,
|
||||
Raid = 16,
|
||||
Raid2 = 17,
|
||||
Frontline = 18,
|
||||
ChocoboSquare = 20,
|
||||
RestorationEvent = 21,
|
||||
Sanctum = 22,
|
||||
GoldSaucer = 23,
|
||||
LordOfVerminion = 25,
|
||||
Diadem = 26,
|
||||
HallOfTheNovice = 27,
|
||||
CrystallineConflict = 28,
|
||||
QuestBattle2 = 29,
|
||||
Barracks = 30,
|
||||
DeepDungeon = 31,
|
||||
SeasonalEvent = 32,
|
||||
TreasureMapDuty = 33,
|
||||
SeasonalEventDuty = 34,
|
||||
Battlehall = 35,
|
||||
CrystallineConflict2 = 37,
|
||||
Diadem2 = 38,
|
||||
RivalWings = 39,
|
||||
Unknown1 = 40,
|
||||
Eureka = 41,
|
||||
SeasonalEvent2 = 43,
|
||||
LeapOfFaith = 44,
|
||||
MaskedCarnivale = 45,
|
||||
OceanFishing = 46,
|
||||
Diadem3 = 47,
|
||||
Bozja = 48,
|
||||
IslandSanctuary = 49,
|
||||
Battlehall2 = 50,
|
||||
Battlehall3 = 51,
|
||||
LargeScaleRaid = 52,
|
||||
LargeScaleSavageRaid = 53,
|
||||
QuestArea4 = 54,
|
||||
TribalInstance = 56,
|
||||
CriterionDuty = 57,
|
||||
CriterionSavageDuty = 58,
|
||||
Blunderville = 59,
|
||||
CosmicExploration = 60
|
||||
}
|
53
LLib/LLib.GameUI/LAddon.cs
Normal file
53
LLib/LLib.GameUI/LAddon.cs
Normal file
|
@ -0,0 +1,53 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using Dalamud.Game.NativeWrapper;
|
||||
using Dalamud.Plugin.Services;
|
||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||
|
||||
namespace LLib.GameUI;
|
||||
|
||||
public static class LAddon
|
||||
{
|
||||
private const int UnitListCount = 18;
|
||||
|
||||
public unsafe static AtkUnitBase* GetAddonById(uint id)
|
||||
{
|
||||
AtkUnitList* ptr = &AtkStage.Instance()->RaptureAtkUnitManager->AtkUnitManager.DepthLayerOneList;
|
||||
for (int i = 0; i < 18; i++)
|
||||
{
|
||||
AtkUnitList* ptr2 = ptr + i;
|
||||
foreach (int item in Enumerable.Range(0, Math.Min(ptr2->Count, ptr2->Entries.Length)))
|
||||
{
|
||||
AtkUnitBase* value = ptr2->Entries[item].Value;
|
||||
if (value != null && value->Id == id)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public unsafe static bool TryGetAddonByName<T>(this IGameGui gameGui, string addonName, out T* addonPtr) where T : unmanaged
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(gameGui, "gameGui");
|
||||
ArgumentException.ThrowIfNullOrEmpty(addonName, "addonName");
|
||||
AtkUnitBasePtr addonByName = gameGui.GetAddonByName(addonName);
|
||||
if (!addonByName.IsNull)
|
||||
{
|
||||
addonPtr = (T*)addonByName.Address;
|
||||
return true;
|
||||
}
|
||||
addonPtr = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
public unsafe static bool IsAddonReady(AtkUnitBase* addon)
|
||||
{
|
||||
if (addon->IsVisible)
|
||||
{
|
||||
return addon->UldManager.LoadedState == AtkLoadState.Loaded;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
21
LLib/LLib.GameUI/LAtkValue.cs
Normal file
21
LLib/LLib.GameUI/LAtkValue.cs
Normal file
|
@ -0,0 +1,21 @@
|
|||
using System;
|
||||
using Dalamud.Memory;
|
||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||
|
||||
namespace LLib.GameUI;
|
||||
|
||||
public static class LAtkValue
|
||||
{
|
||||
public unsafe static string? ReadAtkString(this AtkValue atkValue)
|
||||
{
|
||||
if (atkValue.Type == FFXIVClientStructs.FFXIV.Component.GUI.ValueType.Undefined)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (atkValue.String.HasValue)
|
||||
{
|
||||
return MemoryHelper.ReadSeStringNullTerminated(new IntPtr((byte*)atkValue.String)).WithCertainMacroCodeReplacements();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
16
LLib/LLib.GameUI/SeStringExtensions.cs
Normal file
16
LLib/LLib.GameUI/SeStringExtensions.cs
Normal file
|
@ -0,0 +1,16 @@
|
|||
using Dalamud.Game.Text.SeStringHandling;
|
||||
using Lumina.Text.ReadOnly;
|
||||
|
||||
namespace LLib.GameUI;
|
||||
|
||||
public static class SeStringExtensions
|
||||
{
|
||||
public static string WithCertainMacroCodeReplacements(this SeString? str)
|
||||
{
|
||||
if (str == null)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
return new ReadOnlySeString(str.Encode()).WithCertainMacroCodeReplacements();
|
||||
}
|
||||
}
|
28
LLib/LLib.Gear/EBaseParam.cs
Normal file
28
LLib/LLib.Gear/EBaseParam.cs
Normal file
|
@ -0,0 +1,28 @@
|
|||
namespace LLib.Gear;
|
||||
|
||||
public enum EBaseParam : byte
|
||||
{
|
||||
None = 0,
|
||||
Strength = 1,
|
||||
Dexterity = 2,
|
||||
Vitality = 3,
|
||||
Intelligence = 4,
|
||||
Mind = 5,
|
||||
Piety = 6,
|
||||
GP = 10,
|
||||
CP = 11,
|
||||
DamagePhys = 12,
|
||||
DamageMag = 13,
|
||||
DefensePhys = 21,
|
||||
DefenseMag = 24,
|
||||
Tenacity = 19,
|
||||
Crit = 27,
|
||||
DirectHit = 22,
|
||||
Determination = 44,
|
||||
SpellSpeed = 46,
|
||||
SkillSpeed = 45,
|
||||
Craftsmanship = 70,
|
||||
Control = 71,
|
||||
Gathering = 72,
|
||||
Perception = 73
|
||||
}
|
72
LLib/LLib.Gear/EquipmentStats.cs
Normal file
72
LLib/LLib.Gear/EquipmentStats.cs
Normal file
|
@ -0,0 +1,72 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace LLib.Gear;
|
||||
|
||||
public sealed record EquipmentStats(Dictionary<EBaseParam, StatInfo> Stats, byte MateriaCount)
|
||||
{
|
||||
private sealed class KeyValuePairComparer : IEqualityComparer<KeyValuePair<EBaseParam, StatInfo>>
|
||||
{
|
||||
public bool Equals(KeyValuePair<EBaseParam, StatInfo> x, KeyValuePair<EBaseParam, StatInfo> y)
|
||||
{
|
||||
if (x.Key == y.Key)
|
||||
{
|
||||
return object.Equals(x.Value, y.Value);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public int GetHashCode(KeyValuePair<EBaseParam, StatInfo> obj)
|
||||
{
|
||||
return HashCode.Combine((int)obj.Key, obj.Value);
|
||||
}
|
||||
}
|
||||
|
||||
public short Get(EBaseParam param)
|
||||
{
|
||||
return (short)(GetEquipment(param) + GetMateria(param));
|
||||
}
|
||||
|
||||
public short GetEquipment(EBaseParam param)
|
||||
{
|
||||
Stats.TryGetValue(param, out StatInfo value);
|
||||
return value?.EquipmentValue ?? 0;
|
||||
}
|
||||
|
||||
public short GetMateria(EBaseParam param)
|
||||
{
|
||||
Stats.TryGetValue(param, out StatInfo value);
|
||||
return value?.MateriaValue ?? 0;
|
||||
}
|
||||
|
||||
public bool IsOvercapped(EBaseParam param)
|
||||
{
|
||||
Stats.TryGetValue(param, out StatInfo value);
|
||||
return value?.Overcapped ?? false;
|
||||
}
|
||||
|
||||
public bool Has(EBaseParam substat)
|
||||
{
|
||||
return Stats.ContainsKey(substat);
|
||||
}
|
||||
|
||||
public bool HasMateria()
|
||||
{
|
||||
return Stats.Values.Any((StatInfo x) => x.MateriaValue > 0);
|
||||
}
|
||||
|
||||
public bool Equals(EquipmentStats? other)
|
||||
{
|
||||
if (other != null && MateriaCount == other.MateriaCount)
|
||||
{
|
||||
return Stats.SequenceEqual(other.Stats, new KeyValuePairComparer());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine(MateriaCount, Stats);
|
||||
}
|
||||
}
|
37
LLib/LLib.Gear/ExtendedBaseParam.cs
Normal file
37
LLib/LLib.Gear/ExtendedBaseParam.cs
Normal file
|
@ -0,0 +1,37 @@
|
|||
using Lumina.Excel;
|
||||
using Lumina.Excel.Sheets;
|
||||
|
||||
namespace LLib.Gear;
|
||||
|
||||
[Sheet("BaseParam")]
|
||||
public readonly struct ExtendedBaseParam : IExcelRow<ExtendedBaseParam>
|
||||
{
|
||||
private const int ParamCount = 23;
|
||||
|
||||
public uint RowId => _003Crow_003EP;
|
||||
|
||||
public BaseParam BaseParam => new BaseParam(_003Cpage_003EP, _003Coffset_003EP, _003Crow_003EP);
|
||||
|
||||
public unsafe Collection<ushort> EquipSlotCategoryPct => new Collection<ushort>(_003Cpage_003EP, _003Coffset_003EP, _003Coffset_003EP, (delegate*<ExcelPage, uint, uint, uint, ushort>)(&EquipSlotCategoryPctCtor), 23);
|
||||
|
||||
public ExtendedBaseParam(ExcelPage page, uint offset, uint row)
|
||||
{
|
||||
_003Cpage_003EP = page;
|
||||
_003Coffset_003EP = offset;
|
||||
_003Crow_003EP = row;
|
||||
}
|
||||
|
||||
private static ushort EquipSlotCategoryPctCtor(ExcelPage page, uint parentOffset, uint offset, uint i)
|
||||
{
|
||||
if (i != 0)
|
||||
{
|
||||
return page.ReadUInt16(offset + 8 + (i - 1) * 2);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static ExtendedBaseParam Create(ExcelPage page, uint offset, uint row)
|
||||
{
|
||||
return new ExtendedBaseParam(page, offset, row);
|
||||
}
|
||||
}
|
219
LLib/LLib.Gear/GearStatsCalculator.cs
Normal file
219
LLib/LLib.Gear/GearStatsCalculator.cs
Normal file
|
@ -0,0 +1,219 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Dalamud.Plugin.Services;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game;
|
||||
using Lumina.Excel;
|
||||
using Lumina.Excel.Sheets;
|
||||
|
||||
namespace LLib.Gear;
|
||||
|
||||
public sealed class GearStatsCalculator
|
||||
{
|
||||
private sealed record MateriaInfo(EBaseParam BaseParam, Collection<short> Values, bool HasItem);
|
||||
|
||||
private const uint EternityRingItemId = 8575u;
|
||||
|
||||
private static readonly uint[] CanHaveOffhand = new uint[14]
|
||||
{
|
||||
2u, 6u, 8u, 12u, 14u, 16u, 18u, 20u, 22u, 24u,
|
||||
26u, 28u, 30u, 32u
|
||||
};
|
||||
|
||||
private readonly ExcelSheet<Item> _itemSheet;
|
||||
|
||||
private readonly Dictionary<(uint ItemLevel, EBaseParam BaseParam), ushort> _itemLevelStatCaps = new Dictionary<(uint, EBaseParam), ushort>();
|
||||
|
||||
private readonly Dictionary<(EBaseParam BaseParam, int EquipSlotCategory), ushort> _equipSlotCategoryPct;
|
||||
|
||||
private readonly Dictionary<uint, MateriaInfo> _materiaStats;
|
||||
|
||||
public GearStatsCalculator(IDataManager? dataManager)
|
||||
: this(dataManager?.GetExcelSheet<ItemLevel>() ?? throw new ArgumentNullException("dataManager"), dataManager.GetExcelSheet<ExtendedBaseParam>(), dataManager.GetExcelSheet<Materia>(), dataManager.GetExcelSheet<Item>())
|
||||
{
|
||||
}
|
||||
|
||||
public GearStatsCalculator(ExcelSheet<ItemLevel> itemLevelSheet, ExcelSheet<ExtendedBaseParam> baseParamSheet, ExcelSheet<Materia> materiaSheet, ExcelSheet<Item> itemSheet)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(itemLevelSheet, "itemLevelSheet");
|
||||
ArgumentNullException.ThrowIfNull(baseParamSheet, "baseParamSheet");
|
||||
ArgumentNullException.ThrowIfNull(materiaSheet, "materiaSheet");
|
||||
ArgumentNullException.ThrowIfNull(itemSheet, "itemSheet");
|
||||
_itemSheet = itemSheet;
|
||||
foreach (ItemLevel item in itemLevelSheet)
|
||||
{
|
||||
_itemLevelStatCaps[(item.RowId, EBaseParam.Strength)] = item.Strength;
|
||||
_itemLevelStatCaps[(item.RowId, EBaseParam.Dexterity)] = item.Dexterity;
|
||||
_itemLevelStatCaps[(item.RowId, EBaseParam.Vitality)] = item.Vitality;
|
||||
_itemLevelStatCaps[(item.RowId, EBaseParam.Intelligence)] = item.Intelligence;
|
||||
_itemLevelStatCaps[(item.RowId, EBaseParam.Mind)] = item.Mind;
|
||||
_itemLevelStatCaps[(item.RowId, EBaseParam.Piety)] = item.Piety;
|
||||
_itemLevelStatCaps[(item.RowId, EBaseParam.GP)] = item.GP;
|
||||
_itemLevelStatCaps[(item.RowId, EBaseParam.CP)] = item.CP;
|
||||
_itemLevelStatCaps[(item.RowId, EBaseParam.DamagePhys)] = item.PhysicalDamage;
|
||||
_itemLevelStatCaps[(item.RowId, EBaseParam.DamageMag)] = item.MagicalDamage;
|
||||
_itemLevelStatCaps[(item.RowId, EBaseParam.DefensePhys)] = item.Defense;
|
||||
_itemLevelStatCaps[(item.RowId, EBaseParam.DefenseMag)] = item.MagicDefense;
|
||||
_itemLevelStatCaps[(item.RowId, EBaseParam.Tenacity)] = item.Tenacity;
|
||||
_itemLevelStatCaps[(item.RowId, EBaseParam.Crit)] = item.CriticalHit;
|
||||
_itemLevelStatCaps[(item.RowId, EBaseParam.DirectHit)] = item.DirectHitRate;
|
||||
_itemLevelStatCaps[(item.RowId, EBaseParam.Determination)] = item.Determination;
|
||||
_itemLevelStatCaps[(item.RowId, EBaseParam.SpellSpeed)] = item.SpellSpeed;
|
||||
_itemLevelStatCaps[(item.RowId, EBaseParam.SkillSpeed)] = item.SkillSpeed;
|
||||
_itemLevelStatCaps[(item.RowId, EBaseParam.Gathering)] = item.Gathering;
|
||||
_itemLevelStatCaps[(item.RowId, EBaseParam.Perception)] = item.Perception;
|
||||
_itemLevelStatCaps[(item.RowId, EBaseParam.Craftsmanship)] = item.Craftsmanship;
|
||||
_itemLevelStatCaps[(item.RowId, EBaseParam.Control)] = item.Control;
|
||||
}
|
||||
_equipSlotCategoryPct = baseParamSheet.SelectMany((ExtendedBaseParam x) => from y in Enumerable.Range(0, x.EquipSlotCategoryPct.Count)
|
||||
select ((EBaseParam)x.RowId, y: y, x.EquipSlotCategoryPct[y])).ToDictionary(((EBaseParam, int y, ushort) x) => (x.Item1, x.y), ((EBaseParam, int y, ushort) x) => x.Item3);
|
||||
_materiaStats = materiaSheet.Where((Materia x) => x.RowId != 0 && x.BaseParam.RowId != 0).ToDictionary((Materia x) => x.RowId, (Materia x) => new MateriaInfo((EBaseParam)x.BaseParam.RowId, x.Value, x.Item[0].RowId != 0));
|
||||
}
|
||||
|
||||
public unsafe EquipmentStats CalculateGearStats(InventoryItem* item)
|
||||
{
|
||||
List<(uint, byte)> list = new List<(uint, byte)>();
|
||||
byte b = 0;
|
||||
if (item->ItemId != 8575)
|
||||
{
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
ushort num = item->Materia[i];
|
||||
if (num != 0)
|
||||
{
|
||||
b++;
|
||||
list.Add((num, item->MateriaGrades[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
return CalculateGearStats(_itemSheet.GetRow(item->ItemId), item->Flags.HasFlag(InventoryItem.ItemFlags.HighQuality), list)with
|
||||
{
|
||||
MateriaCount = b
|
||||
};
|
||||
}
|
||||
|
||||
public EquipmentStats CalculateGearStats(Item item, bool highQuality, IReadOnlyList<(uint MateriaId, byte Grade)> materias)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(materias, "materias");
|
||||
Dictionary<EBaseParam, StatInfo> dictionary = new Dictionary<EBaseParam, StatInfo>();
|
||||
for (int i = 0; i < item.BaseParam.Count; i++)
|
||||
{
|
||||
AddEquipmentStat(dictionary, item.BaseParam[i], item.BaseParamValue[i]);
|
||||
}
|
||||
if (highQuality)
|
||||
{
|
||||
for (int j = 0; j < item.BaseParamSpecial.Count; j++)
|
||||
{
|
||||
AddEquipmentStat(dictionary, item.BaseParamSpecial[j], item.BaseParamValueSpecial[j]);
|
||||
}
|
||||
}
|
||||
foreach (var materia in materias)
|
||||
{
|
||||
if (_materiaStats.TryGetValue(materia.MateriaId, out MateriaInfo value))
|
||||
{
|
||||
AddMateriaStat(item, dictionary, value, materia.Grade);
|
||||
}
|
||||
}
|
||||
return new EquipmentStats(dictionary, 0);
|
||||
}
|
||||
|
||||
private static void AddEquipmentStat(Dictionary<EBaseParam, StatInfo> result, RowRef<BaseParam> baseParam, short value)
|
||||
{
|
||||
if (baseParam.RowId != 0)
|
||||
{
|
||||
if (result.TryGetValue((EBaseParam)baseParam.RowId, out StatInfo value2))
|
||||
{
|
||||
result[(EBaseParam)baseParam.RowId] = value2 with
|
||||
{
|
||||
EquipmentValue = (short)(value2.EquipmentValue + value)
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
result[(EBaseParam)baseParam.RowId] = new StatInfo(value, 0, Overcapped: false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void AddMateriaStat(Item item, Dictionary<EBaseParam, StatInfo> result, MateriaInfo materiaInfo, short grade)
|
||||
{
|
||||
if (!result.TryGetValue(materiaInfo.BaseParam, out StatInfo value))
|
||||
{
|
||||
value = (result[materiaInfo.BaseParam] = new StatInfo(0, 0, Overcapped: false));
|
||||
}
|
||||
if (materiaInfo.HasItem)
|
||||
{
|
||||
short num = (short)(GetMaximumStatValue(item, materiaInfo.BaseParam) - value.EquipmentValue);
|
||||
if (value.MateriaValue + materiaInfo.Values[grade] > num)
|
||||
{
|
||||
result[materiaInfo.BaseParam] = value with
|
||||
{
|
||||
MateriaValue = num,
|
||||
Overcapped = true
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
result[materiaInfo.BaseParam] = value with
|
||||
{
|
||||
MateriaValue = (short)(value.MateriaValue + materiaInfo.Values[grade])
|
||||
};
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result[materiaInfo.BaseParam] = value with
|
||||
{
|
||||
MateriaValue = (short)(value.MateriaValue + materiaInfo.Values[grade])
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public short GetMaximumStatValue(Item item, EBaseParam baseParamValue)
|
||||
{
|
||||
if (_itemLevelStatCaps.TryGetValue((item.LevelItem.RowId, baseParamValue), out var value))
|
||||
{
|
||||
return (short)Math.Round((float)(value * _equipSlotCategoryPct[(baseParamValue, (int)item.EquipSlotCategory.RowId)]) / 1000f, MidpointRounding.AwayFromZero);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public unsafe short CalculateAverageItemLevel(InventoryContainer* container)
|
||||
{
|
||||
uint num = 0u;
|
||||
int num2 = 12;
|
||||
for (int i = 0; i < 13; i++)
|
||||
{
|
||||
if (i == 5)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
InventoryItem* inventorySlot = container->GetInventorySlot(i);
|
||||
if (inventorySlot == null || inventorySlot->ItemId == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
Item? rowOrDefault = _itemSheet.GetRowOrDefault(inventorySlot->ItemId);
|
||||
if (!rowOrDefault.HasValue)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (rowOrDefault.Value.ItemUICategory.RowId == 105)
|
||||
{
|
||||
if (i == 0)
|
||||
{
|
||||
num2--;
|
||||
}
|
||||
num2--;
|
||||
continue;
|
||||
}
|
||||
if (i == 0 && !CanHaveOffhand.Contains(rowOrDefault.Value.ItemUICategory.RowId))
|
||||
{
|
||||
num += rowOrDefault.Value.LevelItem.RowId;
|
||||
i++;
|
||||
}
|
||||
num += rowOrDefault.Value.LevelItem.RowId;
|
||||
}
|
||||
return (short)(num / num2);
|
||||
}
|
||||
}
|
6
LLib/LLib.Gear/StatInfo.cs
Normal file
6
LLib/LLib.Gear/StatInfo.cs
Normal file
|
@ -0,0 +1,6 @@
|
|||
namespace LLib.Gear;
|
||||
|
||||
public sealed record StatInfo(short EquipmentValue, short MateriaValue, bool Overcapped)
|
||||
{
|
||||
public short TotalValue => (short)(EquipmentValue + MateriaValue);
|
||||
}
|
14
LLib/LLib.ImGui/IPersistableWindowConfig.cs
Normal file
14
LLib/LLib.ImGui/IPersistableWindowConfig.cs
Normal file
|
@ -0,0 +1,14 @@
|
|||
namespace LLib.ImGui;
|
||||
|
||||
public interface IPersistableWindowConfig
|
||||
{
|
||||
WindowConfig? WindowConfig { get; }
|
||||
|
||||
void SaveWindowConfig();
|
||||
}
|
||||
public interface IPersistableWindowConfig<out T> : IPersistableWindowConfig where T : WindowConfig
|
||||
{
|
||||
new T? WindowConfig { get; }
|
||||
|
||||
WindowConfig? IPersistableWindowConfig.WindowConfig => WindowConfig;
|
||||
}
|
188
LLib/LLib.ImGui/LWindow.cs
Normal file
188
LLib/LLib.ImGui/LWindow.cs
Normal file
|
@ -0,0 +1,188 @@
|
|||
using System.Runtime.CompilerServices;
|
||||
using Dalamud.Bindings.ImGui;
|
||||
using Dalamud.Interface.Windowing;
|
||||
|
||||
namespace LLib.ImGui;
|
||||
|
||||
public abstract class LWindow : Window
|
||||
{
|
||||
private bool _initializedConfig;
|
||||
|
||||
private bool _wasCollapsedLastFrame;
|
||||
|
||||
protected bool ClickedHeaderLastFrame { get; private set; }
|
||||
|
||||
protected bool ClickedHeaderCurrentFrame { get; private set; }
|
||||
|
||||
protected bool UncollapseNextFrame { get; set; }
|
||||
|
||||
public bool IsOpenAndUncollapsed
|
||||
{
|
||||
get
|
||||
{
|
||||
if (base.IsOpen)
|
||||
{
|
||||
return !_wasCollapsedLastFrame;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
set
|
||||
{
|
||||
base.IsOpen = value;
|
||||
UncollapseNextFrame = value;
|
||||
}
|
||||
}
|
||||
|
||||
protected bool IsPinned
|
||||
{
|
||||
get
|
||||
{
|
||||
return InternalIsPinned(this);
|
||||
}
|
||||
set
|
||||
{
|
||||
InternalIsPinned(this) = value;
|
||||
}
|
||||
}
|
||||
|
||||
protected bool IsClickthrough
|
||||
{
|
||||
get
|
||||
{
|
||||
return InternalIsClickthrough(this);
|
||||
}
|
||||
set
|
||||
{
|
||||
InternalIsClickthrough(this) = value;
|
||||
}
|
||||
}
|
||||
|
||||
protected int? Alpha
|
||||
{
|
||||
get
|
||||
{
|
||||
return (int?)(100000f * InternalAlpha(this));
|
||||
}
|
||||
set
|
||||
{
|
||||
InternalAlpha(this) = (float?)value / 100000f;
|
||||
}
|
||||
}
|
||||
|
||||
protected LWindow(string windowName, ImGuiWindowFlags flags = ImGuiWindowFlags.None, bool forceMainWindow = false)
|
||||
: base(windowName, flags, forceMainWindow)
|
||||
{
|
||||
}
|
||||
|
||||
private void LoadWindowConfig()
|
||||
{
|
||||
if (!(this is IPersistableWindowConfig persistableWindowConfig))
|
||||
{
|
||||
return;
|
||||
}
|
||||
WindowConfig windowConfig = persistableWindowConfig.WindowConfig;
|
||||
if (windowConfig != null)
|
||||
{
|
||||
if (base.AllowPinning)
|
||||
{
|
||||
IsPinned = windowConfig.IsPinned;
|
||||
}
|
||||
if (base.AllowClickthrough)
|
||||
{
|
||||
IsClickthrough = windowConfig.IsClickthrough;
|
||||
}
|
||||
Alpha = windowConfig.Alpha;
|
||||
}
|
||||
_initializedConfig = true;
|
||||
}
|
||||
|
||||
private void UpdateWindowConfig()
|
||||
{
|
||||
if (!(this is IPersistableWindowConfig persistableWindowConfig) || Dalamud.Bindings.ImGui.ImGui.IsAnyMouseDown())
|
||||
{
|
||||
return;
|
||||
}
|
||||
WindowConfig windowConfig = persistableWindowConfig.WindowConfig;
|
||||
if (windowConfig != null)
|
||||
{
|
||||
bool flag = false;
|
||||
if (base.AllowPinning && windowConfig.IsPinned != IsPinned)
|
||||
{
|
||||
windowConfig.IsPinned = IsPinned;
|
||||
flag = true;
|
||||
}
|
||||
if (base.AllowClickthrough && windowConfig.IsClickthrough != IsClickthrough)
|
||||
{
|
||||
windowConfig.IsClickthrough = IsClickthrough;
|
||||
flag = true;
|
||||
}
|
||||
if (windowConfig.Alpha != Alpha)
|
||||
{
|
||||
windowConfig.Alpha = Alpha;
|
||||
flag = true;
|
||||
}
|
||||
if (flag)
|
||||
{
|
||||
persistableWindowConfig.SaveWindowConfig();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ToggleOrUncollapse()
|
||||
{
|
||||
IsOpenAndUncollapsed = !IsOpenAndUncollapsed;
|
||||
}
|
||||
|
||||
public override void OnOpen()
|
||||
{
|
||||
UncollapseNextFrame = true;
|
||||
base.OnOpen();
|
||||
}
|
||||
|
||||
public override void Update()
|
||||
{
|
||||
_wasCollapsedLastFrame = true;
|
||||
}
|
||||
|
||||
public override void PreDraw()
|
||||
{
|
||||
if (!_initializedConfig)
|
||||
{
|
||||
LoadWindowConfig();
|
||||
}
|
||||
if (UncollapseNextFrame)
|
||||
{
|
||||
Dalamud.Bindings.ImGui.ImGui.SetNextWindowCollapsed(collapsed: false);
|
||||
UncollapseNextFrame = false;
|
||||
}
|
||||
base.PreDraw();
|
||||
ClickedHeaderLastFrame = ClickedHeaderCurrentFrame;
|
||||
ClickedHeaderCurrentFrame = false;
|
||||
}
|
||||
|
||||
public sealed override void Draw()
|
||||
{
|
||||
_wasCollapsedLastFrame = false;
|
||||
DrawContent();
|
||||
}
|
||||
|
||||
public abstract void DrawContent();
|
||||
|
||||
public override void PostDraw()
|
||||
{
|
||||
base.PostDraw();
|
||||
if (_initializedConfig)
|
||||
{
|
||||
UpdateWindowConfig();
|
||||
}
|
||||
}
|
||||
|
||||
[UnsafeAccessor(UnsafeAccessorKind.Field, Name = "internalIsPinned")]
|
||||
private static extern ref bool InternalIsPinned(Window @this);
|
||||
|
||||
[UnsafeAccessor(UnsafeAccessorKind.Field, Name = "internalIsClickthrough")]
|
||||
private static extern ref bool InternalIsClickthrough(Window @this);
|
||||
|
||||
[UnsafeAccessor(UnsafeAccessorKind.Field, Name = "internalAlpha")]
|
||||
private static extern ref float? InternalAlpha(Window @this);
|
||||
}
|
10
LLib/LLib.ImGui/WindowConfig.cs
Normal file
10
LLib/LLib.ImGui/WindowConfig.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
namespace LLib.ImGui;
|
||||
|
||||
public class WindowConfig
|
||||
{
|
||||
public bool IsPinned { get; set; }
|
||||
|
||||
public bool IsClickthrough { get; set; }
|
||||
|
||||
public int? Alpha { get; set; }
|
||||
}
|
14
LLib/LLib.Shop.Model/ItemForSale.cs
Normal file
14
LLib/LLib.Shop.Model/ItemForSale.cs
Normal file
|
@ -0,0 +1,14 @@
|
|||
namespace LLib.Shop.Model;
|
||||
|
||||
public sealed class ItemForSale
|
||||
{
|
||||
public required int Position { get; init; }
|
||||
|
||||
public required uint ItemId { get; init; }
|
||||
|
||||
public required string? ItemName { get; init; }
|
||||
|
||||
public required uint Price { get; init; }
|
||||
|
||||
public required uint OwnedItems { get; init; }
|
||||
}
|
24
LLib/LLib.Shop.Model/PurchaseState.cs
Normal file
24
LLib/LLib.Shop.Model/PurchaseState.cs
Normal file
|
@ -0,0 +1,24 @@
|
|||
using System;
|
||||
|
||||
namespace LLib.Shop.Model;
|
||||
|
||||
public sealed class PurchaseState
|
||||
{
|
||||
public int DesiredItems { get; }
|
||||
|
||||
public int OwnedItems { get; set; }
|
||||
|
||||
public int ItemsLeftToBuy => Math.Max(0, DesiredItems - OwnedItems);
|
||||
|
||||
public bool IsComplete => ItemsLeftToBuy == 0;
|
||||
|
||||
public bool IsAwaitingYesNo { get; set; }
|
||||
|
||||
public DateTime NextStep { get; set; } = DateTime.MinValue;
|
||||
|
||||
public PurchaseState(int desiredItems, int ownedItems)
|
||||
{
|
||||
DesiredItems = desiredItems;
|
||||
OwnedItems = ownedItems;
|
||||
}
|
||||
}
|
23
LLib/LLib.Shop/IShopWindow.cs
Normal file
23
LLib/LLib.Shop/IShopWindow.cs
Normal file
|
@ -0,0 +1,23 @@
|
|||
using System.Numerics;
|
||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||
|
||||
namespace LLib.Shop;
|
||||
|
||||
public interface IShopWindow
|
||||
{
|
||||
bool IsEnabled { get; }
|
||||
|
||||
bool IsOpen { get; set; }
|
||||
|
||||
Vector2? Position { get; set; }
|
||||
|
||||
int GetCurrencyCount();
|
||||
|
||||
unsafe void UpdateShopStock(AtkUnitBase* addon);
|
||||
|
||||
unsafe void TriggerPurchase(AtkUnitBase* addonShop, int buyNow);
|
||||
|
||||
void SaveExternalPluginState();
|
||||
|
||||
void RestoreExternalPluginState();
|
||||
}
|
275
LLib/LLib.Shop/RegularShopBase.cs
Normal file
275
LLib/LLib.Shop/RegularShopBase.cs
Normal file
|
@ -0,0 +1,275 @@
|
|||
using System;
|
||||
using System.Numerics;
|
||||
using Dalamud.Game.Addon.Lifecycle;
|
||||
using Dalamud.Game.Addon.Lifecycle.AddonArgTypes;
|
||||
using Dalamud.Plugin.Services;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game;
|
||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||
using LLib.GameUI;
|
||||
using LLib.Shop.Model;
|
||||
|
||||
namespace LLib.Shop;
|
||||
|
||||
public class RegularShopBase
|
||||
{
|
||||
private readonly IShopWindow _parentWindow;
|
||||
|
||||
private readonly string _addonName;
|
||||
|
||||
private readonly IPluginLog _pluginLog;
|
||||
|
||||
private readonly IGameGui _gameGui;
|
||||
|
||||
private readonly IAddonLifecycle _addonLifecycle;
|
||||
|
||||
public ItemForSale? ItemForSale { get; set; }
|
||||
|
||||
public PurchaseState? PurchaseState { get; private set; }
|
||||
|
||||
public bool AutoBuyEnabled => PurchaseState != null;
|
||||
|
||||
public bool IsAwaitingYesNo
|
||||
{
|
||||
get
|
||||
{
|
||||
return PurchaseState?.IsAwaitingYesNo ?? false;
|
||||
}
|
||||
set
|
||||
{
|
||||
PurchaseState.IsAwaitingYesNo = value;
|
||||
}
|
||||
}
|
||||
|
||||
public RegularShopBase(IShopWindow parentWindow, string addonName, IPluginLog pluginLog, IGameGui gameGui, IAddonLifecycle addonLifecycle)
|
||||
{
|
||||
_parentWindow = parentWindow;
|
||||
_addonName = addonName;
|
||||
_pluginLog = pluginLog;
|
||||
_gameGui = gameGui;
|
||||
_addonLifecycle = addonLifecycle;
|
||||
_addonLifecycle.RegisterListener(AddonEvent.PostSetup, _addonName, ShopPostSetup);
|
||||
_addonLifecycle.RegisterListener(AddonEvent.PreFinalize, _addonName, ShopPreFinalize);
|
||||
_addonLifecycle.RegisterListener(AddonEvent.PostUpdate, _addonName, ShopPostUpdate);
|
||||
}
|
||||
|
||||
private unsafe void ShopPostSetup(AddonEvent type, AddonArgs args)
|
||||
{
|
||||
if (!_parentWindow.IsEnabled)
|
||||
{
|
||||
ItemForSale = null;
|
||||
_parentWindow.IsOpen = false;
|
||||
return;
|
||||
}
|
||||
_parentWindow.UpdateShopStock((AtkUnitBase*)args.Addon.Address);
|
||||
PostUpdateShopStock();
|
||||
if (ItemForSale != null)
|
||||
{
|
||||
_parentWindow.IsOpen = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void ShopPreFinalize(AddonEvent type, AddonArgs args)
|
||||
{
|
||||
PurchaseState = null;
|
||||
_parentWindow.RestoreExternalPluginState();
|
||||
_parentWindow.IsOpen = false;
|
||||
}
|
||||
|
||||
private unsafe void ShopPostUpdate(AddonEvent type, AddonArgs args)
|
||||
{
|
||||
if (!_parentWindow.IsEnabled)
|
||||
{
|
||||
ItemForSale = null;
|
||||
_parentWindow.IsOpen = false;
|
||||
return;
|
||||
}
|
||||
_parentWindow.UpdateShopStock((AtkUnitBase*)args.Addon.Address);
|
||||
PostUpdateShopStock();
|
||||
if (ItemForSale != null)
|
||||
{
|
||||
AtkUnitBase* address = (AtkUnitBase*)args.Addon.Address;
|
||||
short num = 0;
|
||||
short num2 = 0;
|
||||
address->GetPosition(&num, &num2);
|
||||
short num3 = 0;
|
||||
short num4 = 0;
|
||||
address->GetSize(&num3, &num4, scaled: true);
|
||||
num += num3;
|
||||
Vector2? position = _parentWindow.Position;
|
||||
if (position.HasValue)
|
||||
{
|
||||
Vector2 valueOrDefault = position.GetValueOrDefault();
|
||||
if ((short)valueOrDefault.X != num || (short)valueOrDefault.Y != num2)
|
||||
{
|
||||
_parentWindow.Position = new Vector2(num, num2);
|
||||
}
|
||||
}
|
||||
_parentWindow.IsOpen = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
_parentWindow.IsOpen = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void PostUpdateShopStock()
|
||||
{
|
||||
if (ItemForSale != null && PurchaseState != null)
|
||||
{
|
||||
int ownedItems = (int)ItemForSale.OwnedItems;
|
||||
if (PurchaseState.OwnedItems != ownedItems)
|
||||
{
|
||||
PurchaseState.OwnedItems = ownedItems;
|
||||
PurchaseState.NextStep = DateTime.Now.AddSeconds(0.25);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public unsafe int GetItemCount(uint itemId)
|
||||
{
|
||||
return InventoryManager.Instance()->GetInventoryItemCount(itemId, isHq: false, checkEquipped: false, checkArmory: false, 0);
|
||||
}
|
||||
|
||||
public int GetMaxItemsToPurchase()
|
||||
{
|
||||
if (ItemForSale == null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return (int)(_parentWindow.GetCurrencyCount() / ItemForSale.Price);
|
||||
}
|
||||
|
||||
public void CancelAutoPurchase()
|
||||
{
|
||||
PurchaseState = null;
|
||||
_parentWindow.RestoreExternalPluginState();
|
||||
}
|
||||
|
||||
public void StartAutoPurchase(int toPurchase)
|
||||
{
|
||||
PurchaseState = new PurchaseState((int)ItemForSale.OwnedItems + toPurchase, (int)ItemForSale.OwnedItems);
|
||||
_parentWindow.SaveExternalPluginState();
|
||||
}
|
||||
|
||||
public unsafe void HandleNextPurchaseStep()
|
||||
{
|
||||
if (ItemForSale == null || PurchaseState == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
int num = DetermineMaxStackSize(ItemForSale.ItemId);
|
||||
if (num == 0 && !HasFreeInventorySlot())
|
||||
{
|
||||
_pluginLog.Warning("No free inventory slots, can't buy more " + ItemForSale.ItemName);
|
||||
PurchaseState = null;
|
||||
_parentWindow.RestoreExternalPluginState();
|
||||
}
|
||||
else if (!PurchaseState.IsComplete)
|
||||
{
|
||||
if (PurchaseState.NextStep <= DateTime.Now && _gameGui.TryGetAddonByName<AtkUnitBase>(_addonName, out var addonPtr))
|
||||
{
|
||||
int num2 = Math.Min(PurchaseState.ItemsLeftToBuy, num);
|
||||
_pluginLog.Information($"Buying {num2}x {ItemForSale.ItemName}");
|
||||
_parentWindow.TriggerPurchase(addonPtr, num2);
|
||||
PurchaseState.NextStep = DateTime.MaxValue;
|
||||
PurchaseState.IsAwaitingYesNo = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_pluginLog.Information($"Stopping item purchase (desired = {PurchaseState.DesiredItems}, owned = {PurchaseState.OwnedItems})");
|
||||
PurchaseState = null;
|
||||
_parentWindow.RestoreExternalPluginState();
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_addonLifecycle.UnregisterListener(AddonEvent.PostSetup, _addonName, ShopPostSetup);
|
||||
_addonLifecycle.UnregisterListener(AddonEvent.PreFinalize, _addonName, ShopPreFinalize);
|
||||
_addonLifecycle.UnregisterListener(AddonEvent.PostUpdate, _addonName, ShopPostUpdate);
|
||||
}
|
||||
|
||||
public bool HasFreeInventorySlot()
|
||||
{
|
||||
return CountFreeInventorySlots() > 0;
|
||||
}
|
||||
|
||||
public unsafe int CountFreeInventorySlots()
|
||||
{
|
||||
InventoryManager* ptr = InventoryManager.Instance();
|
||||
if (ptr == null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
int num = 0;
|
||||
for (InventoryType inventoryType = InventoryType.Inventory1; inventoryType <= InventoryType.Inventory4; inventoryType++)
|
||||
{
|
||||
InventoryContainer* inventoryContainer = ptr->GetInventoryContainer(inventoryType);
|
||||
for (int i = 0; i < inventoryContainer->Size; i++)
|
||||
{
|
||||
InventoryItem* inventorySlot = inventoryContainer->GetInventorySlot(i);
|
||||
if (inventorySlot == null || inventorySlot->ItemId == 0)
|
||||
{
|
||||
num++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
private unsafe int DetermineMaxStackSize(uint itemId)
|
||||
{
|
||||
InventoryManager* ptr = InventoryManager.Instance();
|
||||
if (ptr == null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
int num = 0;
|
||||
for (InventoryType inventoryType = InventoryType.Inventory1; inventoryType <= InventoryType.Inventory4; inventoryType++)
|
||||
{
|
||||
InventoryContainer* inventoryContainer = ptr->GetInventoryContainer(inventoryType);
|
||||
for (int i = 0; i < inventoryContainer->Size; i++)
|
||||
{
|
||||
InventoryItem* inventorySlot = inventoryContainer->GetInventorySlot(i);
|
||||
if (inventorySlot == null || inventorySlot->ItemId == 0)
|
||||
{
|
||||
return 99;
|
||||
}
|
||||
if (inventorySlot->ItemId == itemId)
|
||||
{
|
||||
num += 999 - inventorySlot->Quantity;
|
||||
if (num >= 99)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return Math.Min(99, num);
|
||||
}
|
||||
|
||||
public unsafe int CountInventorySlotsWithCondition(uint itemId, Predicate<int> predicate)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(predicate, "predicate");
|
||||
InventoryManager* ptr = InventoryManager.Instance();
|
||||
if (ptr == null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
int num = 0;
|
||||
for (InventoryType inventoryType = InventoryType.Inventory1; inventoryType <= InventoryType.Inventory4; inventoryType++)
|
||||
{
|
||||
InventoryContainer* inventoryContainer = ptr->GetInventoryContainer(inventoryType);
|
||||
for (int i = 0; i < inventoryContainer->Size; i++)
|
||||
{
|
||||
InventoryItem* inventorySlot = inventoryContainer->GetInventorySlot(i);
|
||||
if (inventorySlot != null && inventorySlot->ItemId != 0 && inventorySlot->ItemId == itemId && predicate(inventorySlot->Quantity))
|
||||
{
|
||||
num++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return num;
|
||||
}
|
||||
}
|
35
LLib/LLib.csproj
Normal file
35
LLib/LLib.csproj
Normal file
|
@ -0,0 +1,35 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<AssemblyName>LLib</AssemblyName>
|
||||
<GenerateAssemblyInfo>False</GenerateAssemblyInfo>
|
||||
<TargetFramework>netcoreapp9.0</TargetFramework>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<LangVersion>12.0</LangVersion>
|
||||
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup />
|
||||
<ItemGroup />
|
||||
<ItemGroup>
|
||||
<Reference Include="Dalamud">
|
||||
<HintPath>C:\Users\Aly\AppData\Roaming\XIVLauncher\addon\Hooks\dev\Dalamud.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Lumina">
|
||||
<HintPath>C:\Users\Aly\AppData\Roaming\XIVLauncher\addon\Hooks\dev\Lumina.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="FFXIVClientStructs">
|
||||
<HintPath>C:\Users\Aly\AppData\Roaming\XIVLauncher\addon\Hooks\dev\FFXIVClientStructs.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Dalamud.Bindings.ImGui">
|
||||
<HintPath>C:\Users\Aly\AppData\Roaming\XIVLauncher\addon\Hooks\dev\Dalamud.Bindings.ImGui.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Lumina.Excel">
|
||||
<HintPath>C:\Users\Aly\AppData\Roaming\XIVLauncher\addon\Hooks\dev\Lumina.Excel.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="InteropGenerator.Runtime">
|
||||
<HintPath>C:\Users\Aly\AppData\Roaming\XIVLauncher\addon\Hooks\dev\InteropGenerator.Runtime.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
</Project>
|
100
LLib/LLib/DalamudReflector.cs
Normal file
100
LLib/LLib/DalamudReflector.cs
Normal file
|
@ -0,0 +1,100 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Reflection;
|
||||
using Dalamud.Plugin;
|
||||
using Dalamud.Plugin.Services;
|
||||
|
||||
namespace LLib;
|
||||
|
||||
public sealed class DalamudReflector : IDisposable
|
||||
{
|
||||
private readonly IDalamudPluginInterface _pluginInterface;
|
||||
|
||||
private readonly IFramework _framework;
|
||||
|
||||
private readonly IPluginLog _pluginLog;
|
||||
|
||||
private readonly Dictionary<string, IDalamudPlugin> _pluginCache = new Dictionary<string, IDalamudPlugin>();
|
||||
|
||||
private bool _pluginsChanged;
|
||||
|
||||
public DalamudReflector(IDalamudPluginInterface pluginInterface, IFramework framework, IPluginLog pluginLog)
|
||||
{
|
||||
_pluginInterface = pluginInterface;
|
||||
_framework = framework;
|
||||
_pluginLog = pluginLog;
|
||||
object pluginManager = GetPluginManager();
|
||||
pluginManager.GetType().GetEvent("OnInstalledPluginsChanged").AddEventHandler(pluginManager, new Action(OnInstalledPluginsChanged));
|
||||
_framework.Update += FrameworkUpdate;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_framework.Update -= FrameworkUpdate;
|
||||
object pluginManager = GetPluginManager();
|
||||
pluginManager.GetType().GetEvent("OnInstalledPluginsChanged").RemoveEventHandler(pluginManager, new Action(OnInstalledPluginsChanged));
|
||||
}
|
||||
|
||||
private void FrameworkUpdate(IFramework framework)
|
||||
{
|
||||
if (_pluginsChanged)
|
||||
{
|
||||
_pluginsChanged = false;
|
||||
_pluginCache.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
private object GetPluginManager()
|
||||
{
|
||||
return _pluginInterface.GetType().Assembly.GetType("Dalamud.Service`1", throwOnError: true).MakeGenericType(_pluginInterface.GetType().Assembly.GetType("Dalamud.Plugin.Internal.PluginManager", throwOnError: true)).GetMethod("Get")
|
||||
.Invoke(null, BindingFlags.Default, null, Array.Empty<object>(), null);
|
||||
}
|
||||
|
||||
public bool TryGetDalamudPlugin(string internalName, [MaybeNullWhen(false)] out IDalamudPlugin instance, bool suppressErrors = false, bool ignoreCache = false)
|
||||
{
|
||||
if (!ignoreCache && _pluginCache.TryGetValue(internalName, out instance))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
try
|
||||
{
|
||||
object pluginManager = GetPluginManager();
|
||||
foreach (object item in (IList)pluginManager.GetType().GetProperty("InstalledPlugins").GetValue(pluginManager))
|
||||
{
|
||||
if ((string)item.GetType().GetProperty("Name").GetValue(item) == internalName)
|
||||
{
|
||||
IDalamudPlugin dalamudPlugin = (IDalamudPlugin)((item.GetType().Name == "LocalDevPlugin") ? item.GetType().BaseType : item.GetType()).GetField("instance", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(item);
|
||||
if (dalamudPlugin != null)
|
||||
{
|
||||
instance = dalamudPlugin;
|
||||
_pluginCache[internalName] = dalamudPlugin;
|
||||
return true;
|
||||
}
|
||||
if (!suppressErrors)
|
||||
{
|
||||
_pluginLog.Warning("[DalamudReflector] Found requested plugin " + internalName + " but it was null");
|
||||
}
|
||||
}
|
||||
}
|
||||
instance = null;
|
||||
return false;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (!suppressErrors)
|
||||
{
|
||||
_pluginLog.Error(ex, "Can't find " + internalName + " plugin: " + ex.Message);
|
||||
}
|
||||
instance = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnInstalledPluginsChanged()
|
||||
{
|
||||
_pluginLog.Verbose("Installed plugins changed event fired");
|
||||
_pluginsChanged = true;
|
||||
}
|
||||
}
|
104
LLib/LLib/DataManagerExtensions.cs
Normal file
104
LLib/LLib/DataManagerExtensions.cs
Normal file
|
@ -0,0 +1,104 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using Dalamud.Plugin.Services;
|
||||
using Lumina.Excel;
|
||||
using Lumina.Text.Payloads;
|
||||
using Lumina.Text.ReadOnly;
|
||||
|
||||
namespace LLib;
|
||||
|
||||
public static class DataManagerExtensions
|
||||
{
|
||||
public static ReadOnlySeString? GetSeString<T>(this IDataManager dataManager, string key) where T : struct, IQuestDialogueText, IExcelRow<T>
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(dataManager, "dataManager");
|
||||
return dataManager.GetExcelSheet<T>().Cast<T?>().SingleOrDefault((T? x) => x.Value.Key == key)?.Value;
|
||||
}
|
||||
|
||||
public static string? GetString<T>(this IDataManager dataManager, string key, IPluginLog? pluginLog) where T : struct, IQuestDialogueText, IExcelRow<T>
|
||||
{
|
||||
string text = dataManager.GetSeString<T>(key)?.WithCertainMacroCodeReplacements();
|
||||
pluginLog?.Verbose($"{typeof(T).Name}.{key} => {text}");
|
||||
return text;
|
||||
}
|
||||
|
||||
public static Regex? GetRegex<T>(this IDataManager dataManager, string key, IPluginLog? pluginLog) where T : struct, IQuestDialogueText, IExcelRow<T>
|
||||
{
|
||||
ReadOnlySeString? seString = dataManager.GetSeString<T>(key);
|
||||
if (!seString.HasValue)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
string text = string.Join("", seString.Select((ReadOnlySePayload payload) => (payload.Type == ReadOnlySePayloadType.Text) ? Regex.Escape(payload.ToString()) : "(.*)"));
|
||||
pluginLog?.Verbose($"{typeof(T).Name}.{key} => /{text}/");
|
||||
return new Regex(text);
|
||||
}
|
||||
|
||||
public static ReadOnlySeString? GetSeString<T>(this IDataManager dataManager, uint rowId, Func<T, ReadOnlySeString?> mapper) where T : struct, IExcelRow<T>
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(dataManager, "dataManager");
|
||||
ArgumentNullException.ThrowIfNull(mapper, "mapper");
|
||||
T? rowOrDefault = dataManager.GetExcelSheet<T>().GetRowOrDefault(rowId);
|
||||
if (!rowOrDefault.HasValue)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return mapper(rowOrDefault.Value);
|
||||
}
|
||||
|
||||
public static string? GetString<T>(this IDataManager dataManager, uint rowId, Func<T, ReadOnlySeString?> mapper, IPluginLog? pluginLog = null) where T : struct, IExcelRow<T>
|
||||
{
|
||||
string text = dataManager.GetSeString(rowId, mapper)?.WithCertainMacroCodeReplacements();
|
||||
pluginLog?.Verbose($"{typeof(T).Name}.{rowId} => {text}");
|
||||
return text;
|
||||
}
|
||||
|
||||
public static Regex? GetRegex<T>(this IDataManager dataManager, uint rowId, Func<T, ReadOnlySeString?> mapper, IPluginLog? pluginLog = null) where T : struct, IExcelRow<T>
|
||||
{
|
||||
ReadOnlySeString? seString = dataManager.GetSeString(rowId, mapper);
|
||||
if (!seString.HasValue)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
Regex regex = seString.ToRegex();
|
||||
pluginLog?.Verbose($"{typeof(T).Name}.{rowId} => /{regex}/");
|
||||
return regex;
|
||||
}
|
||||
|
||||
public static Regex? GetRegex<T>(this T excelRow, Func<T, ReadOnlySeString?> mapper, IPluginLog? pluginLog) where T : struct, IExcelRow<T>
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(mapper, "mapper");
|
||||
ReadOnlySeString? text = mapper(excelRow);
|
||||
if (!text.HasValue)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
Regex regex = text.ToRegex();
|
||||
pluginLog?.Verbose($"{typeof(T).Name}.regex => /{regex}/");
|
||||
return regex;
|
||||
}
|
||||
|
||||
public static Regex ToRegex(this ReadOnlySeString? text)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(text, "text");
|
||||
return new Regex(string.Join("", text.Value.Select((ReadOnlySePayload payload) => (payload.Type == ReadOnlySePayloadType.Text) ? Regex.Escape(payload.ToString()) : "(.*)")));
|
||||
}
|
||||
|
||||
public static string WithCertainMacroCodeReplacements(this ReadOnlySeString text)
|
||||
{
|
||||
return string.Join("", text.Select((ReadOnlySePayload payload) => payload.Type switch
|
||||
{
|
||||
ReadOnlySePayloadType.Text => payload.ToString(),
|
||||
ReadOnlySePayloadType.Macro => payload.MacroCode switch
|
||||
{
|
||||
MacroCode.NewLine => "",
|
||||
MacroCode.NonBreakingSpace => " ",
|
||||
MacroCode.Hyphen => "-",
|
||||
MacroCode.SoftHyphen => "",
|
||||
_ => payload.ToString(),
|
||||
},
|
||||
_ => payload.ToString(),
|
||||
}));
|
||||
}
|
||||
}
|
10
LLib/LLib/IQuestDialogueText.cs
Normal file
10
LLib/LLib/IQuestDialogueText.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
using Lumina.Text.ReadOnly;
|
||||
|
||||
namespace LLib;
|
||||
|
||||
public interface IQuestDialogueText
|
||||
{
|
||||
ReadOnlySeString Key { get; }
|
||||
|
||||
ReadOnlySeString Value { get; }
|
||||
}
|
26
LLib/LLib/QuestDialogueText.cs
Normal file
26
LLib/LLib/QuestDialogueText.cs
Normal file
|
@ -0,0 +1,26 @@
|
|||
using Lumina.Excel;
|
||||
using Lumina.Text.ReadOnly;
|
||||
|
||||
namespace LLib;
|
||||
|
||||
[Sheet("PleaseSpecifyTheSheetExplicitly")]
|
||||
public readonly struct QuestDialogueText : IQuestDialogueText, IExcelRow<QuestDialogueText>
|
||||
{
|
||||
public uint RowId => _003Crow_003EP;
|
||||
|
||||
public ReadOnlySeString Key => _003Cpage_003EP.ReadString(_003Coffset_003EP, _003Coffset_003EP);
|
||||
|
||||
public ReadOnlySeString Value => _003Cpage_003EP.ReadString(_003Coffset_003EP + 4, _003Coffset_003EP);
|
||||
|
||||
public QuestDialogueText(ExcelPage page, uint offset, uint row)
|
||||
{
|
||||
_003Cpage_003EP = page;
|
||||
_003Coffset_003EP = offset;
|
||||
_003Crow_003EP = row;
|
||||
}
|
||||
|
||||
static QuestDialogueText IExcelRow<QuestDialogueText>.Create(ExcelPage page, uint offset, uint row)
|
||||
{
|
||||
return new QuestDialogueText(page, offset, row);
|
||||
}
|
||||
}
|
24
QuestPaths/QuestPaths.csproj
Normal file
24
QuestPaths/QuestPaths.csproj
Normal file
|
@ -0,0 +1,24 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<AssemblyName>QuestPaths</AssemblyName>
|
||||
<GenerateAssemblyInfo>False</GenerateAssemblyInfo>
|
||||
<TargetFramework>netcoreapp9.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<LangVersion>12.0</LangVersion>
|
||||
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<RootNamespace />
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<None Remove="Questionable.QuestPaths.QuestSchema" />
|
||||
<EmbeddedResource Include="Questionable.QuestPaths.QuestSchema" LogicalName="Questionable.QuestPaths.QuestSchema" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Questionable.Model">
|
||||
<HintPath>..\..\Questionable.Model.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
</Project>
|
1865
QuestPaths/Questionable.QuestPaths.QuestSchema
Normal file
1865
QuestPaths/Questionable.QuestPaths.QuestSchema
Normal file
File diff suppressed because it is too large
Load diff
486060
QuestPaths/Questionable.QuestPaths/AssemblyQuestLoader.cs
Normal file
486060
QuestPaths/Questionable.QuestPaths/AssemblyQuestLoader.cs
Normal file
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,448 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace Questionable.Model.Common.Converter;
|
||||
|
||||
public sealed class AetheryteConverter : EnumConverter<EAetheryteLocation>
|
||||
{
|
||||
private static readonly Dictionary<EAetheryteLocation, string> Values = new Dictionary<EAetheryteLocation, string>
|
||||
{
|
||||
{
|
||||
EAetheryteLocation.Gridania,
|
||||
"Gridania"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.CentralShroudBentbranchMeadows,
|
||||
"Central Shroud - Bentbranch Meadows"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.EastShroudHawthorneHut,
|
||||
"East Shroud - Hawthorne Hut"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.SouthShroudQuarrymill,
|
||||
"South Shroud - Quarrymill"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.SouthShroudCampTranquil,
|
||||
"South Shroud - Camp Tranquil"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.NorthShroudFallgourdFloat,
|
||||
"North Shroud - Fallgourd Float"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.Uldah,
|
||||
"Ul'dah"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.WesternThanalanHorizon,
|
||||
"Western Thanalan - Horizon"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.CentralThanalanBlackBrushStation,
|
||||
"Central Thanalan - Black Brush Station"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.EasternThanalanCampDrybone,
|
||||
"Eastern Thanalan - Camp Drybone"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.SouthernThanalanLittleAlaMhigo,
|
||||
"Southern Thanalan - Little Ala Mhigo"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.SouthernThanalanForgottenSprings,
|
||||
"Southern Thanalan - Forgotten Springs"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.NorthernThanalanCampBluefog,
|
||||
"Northern Thanalan - Camp Bluefog"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.NorthernThanalanCeruleumProcessingPlant,
|
||||
"Northern Thanalan - Ceruleum Processing Plant"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.Limsa,
|
||||
"Limsa Lominsa"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.MiddleLaNosceaSummerfordFarms,
|
||||
"Middle La Noscea - Summerford Farms"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.LowerLaNosceaMorabyDrydocks,
|
||||
"Lower La Noscea - Moraby Drydocks"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.EasternLaNosceaCostaDelSol,
|
||||
"Eastern La Noscea - Costa Del Sol"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.EasternLaNosceaWineport,
|
||||
"Eastern La Noscea - Wineport"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.WesternLaNosceaSwiftperch,
|
||||
"Western La Noscea - Swiftperch"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.WesternLaNosceaAleport,
|
||||
"Western La Noscea - Aleport"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.UpperLaNosceaCampBronzeLake,
|
||||
"Upper La Noscea - Camp Bronze Lake"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.OuterLaNosceaCampOverlook,
|
||||
"Outer La Noscea - Camp Overlook"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.CoerthasCentralHighlandsCampDragonhead,
|
||||
"Coerthas Central Highlands - Camp Dragonhead"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.MorDhona,
|
||||
"Mor Dhona"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.WolvesDenPier,
|
||||
"Wolves' Den Pier"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.GoldSaucer,
|
||||
"Gold Saucer"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.Ishgard,
|
||||
"Ishgard"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.Idyllshire,
|
||||
"Idyllshire"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.CoerthasWesternHighlandsFalconsNest,
|
||||
"Coerthas Western Highlands - Falcon's Nest"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.SeaOfCloudsCampCloudtop,
|
||||
"The Sea of Clouds - Camp Cloudtop"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.SeaOfCloudsOkZundu,
|
||||
"The Sea of Clouds - Ok' Zundu"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.AzysLlaHelix,
|
||||
"Azys Lla - Helix"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.DravanianForelandsTailfeather,
|
||||
"The Dravanian Forelands - Tailfeather"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.DravanianForelandsAnyxTrine,
|
||||
"The Dravanian Forelands - Anyx Trine"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.ChurningMistsMoghome,
|
||||
"The Churning Mists - Moghome"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.ChurningMistsZenith,
|
||||
"The Churning Mists - Zenith"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.RhalgrsReach,
|
||||
"Rhalgr's Reach"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.FringesCastrumOriens,
|
||||
"Fringes - Castrum Oriens"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.FringesPeeringStones,
|
||||
"Fringes - Peering Stones"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.PeaksAlaGannha,
|
||||
"Peaks - Ala Gannha"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.PeaksAlaGhiri,
|
||||
"Peaks - Ala Ghiri"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.LochsPortaPraetoria,
|
||||
"Lochs - Porta Praetoria"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.LochsAlaMhiganQuarter,
|
||||
"Lochs - Ala Mhigan Quarter"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.Kugane,
|
||||
"Kugane"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.RubySeaTamamizu,
|
||||
"Ruby Sea - Tamamizu"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.RubySeaOnokoro,
|
||||
"Ruby Sea - Onokoro"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.YanxiaNamai,
|
||||
"Yanxia - Namai"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.YanxiaHouseOfTheFierce,
|
||||
"Yanxia - House of the Fierce"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.AzimSteppeReunion,
|
||||
"Azim Steppe - Reunion"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.AzimSteppeDawnThrone,
|
||||
"Azim Steppe - Dawn Throne"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.AzimSteppeDhoroIloh,
|
||||
"Azim Steppe - Dhoro Iloh"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.DomanEnclave,
|
||||
"Doman Enclave"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.Crystarium,
|
||||
"Crystarium"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.Eulmore,
|
||||
"Eulmore"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.LakelandFortJobb,
|
||||
"Lakeland - Fort Jobb"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.LakelandOstallImperative,
|
||||
"Lakeland - Ostall Imperative"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.KholusiaStilltide,
|
||||
"Kholusia - Stilltide"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.KholusiaWright,
|
||||
"Kholusia - Wright"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.KholusiaTomra,
|
||||
"Kholusia - Tomra"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.AmhAraengMordSouq,
|
||||
"Amh Araeng - Mord Souq"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.AmhAraengInnAtJourneysHead,
|
||||
"Amh Araeng - Inn at Journey's Head"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.AmhAraengTwine,
|
||||
"Amh Araeng - Twine"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.RaktikaSlitherbough,
|
||||
"Rak'tika - Slitherbough"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.RaktikaFanow,
|
||||
"Rak'tika - Fanow"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.IlMhegLydhaLran,
|
||||
"Il Mheg - Lydha Lran"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.IlMhegPlaEnni,
|
||||
"Il Mheg - Pla Enni"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.IlMhegWolekdorf,
|
||||
"Il Mheg - Wolekdorf"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.TempestOndoCups,
|
||||
"Tempest - Ondo Cups"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.TempestMacarensesAngle,
|
||||
"Tempest - Macarenses Angle"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.OldSharlayan,
|
||||
"Old Sharlayan"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.RadzAtHan,
|
||||
"Radz-at-Han"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.LabyrinthosArcheion,
|
||||
"Labyrinthos - Archeion"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.LabyrinthosSharlayanHamlet,
|
||||
"Labyrinthos - Sharlayan Hamlet"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.LabyrinthosAporia,
|
||||
"Labyrinthos - Aporia"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.ThavnairYedlihmad,
|
||||
"Thavnair - Yedlihmad"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.ThavnairGreatWork,
|
||||
"Thavnair - Great Work"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.ThavnairPalakasStand,
|
||||
"Thavnair - Palaka's Stand"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.GarlemaldCampBrokenGlass,
|
||||
"Garlemald - Camp Broken Glass"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.GarlemaldTertium,
|
||||
"Garlemald - Tertium"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.MareLamentorumSinusLacrimarum,
|
||||
"Mare Lamentorum - Sinus Lacrimarum"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.MareLamentorumBestwaysBurrow,
|
||||
"Mare Lamentorum - Bestways Burrow"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.ElpisAnagnorisis,
|
||||
"Elpis - Anagnorisis"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.ElpisTwelveWonders,
|
||||
"Elpis - Twelve Wonders"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.ElpisPoietenOikos,
|
||||
"Elpis - Poieten Oikos"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.UltimaThuleReahTahra,
|
||||
"Ultima Thule - Reah Tahra"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.UltimaThuleAbodeOfTheEa,
|
||||
"Ultima Thule - Abode of the Ea"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.UltimaThuleBaseOmicron,
|
||||
"Ultima Thule - Base Omicron"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.Tuliyollal,
|
||||
"Tuliyollal"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.SolutionNine,
|
||||
"Solution Nine"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.UrqopachaWachunpelo,
|
||||
"Urqopacha - Wachunpelo"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.UrqopachaWorlarsEcho,
|
||||
"Urqopacha - Worlar's Echo"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.KozamaukaOkHanu,
|
||||
"Kozama'uka - Ok'hanu"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.KozamaukaManyFires,
|
||||
"Kozama'uka - Many Fires"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.KozamaukaEarthenshire,
|
||||
"Kozama'uka - Earthenshire"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.KozamaukaDockPoga,
|
||||
"Kozama'uka - Dock Poga"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.YakTelIqBraax,
|
||||
"Yak T'el - Iq Br'aax"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.YakTelMamook,
|
||||
"Yak T'el - Mamook"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.ShaaloaniHhusatahwi,
|
||||
"Shaaloani - Hhusatahwi"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.ShaaloaniShesheneweziSprings,
|
||||
"Shaaloani - Sheshenewezi Springs"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.ShaaloaniMehwahhetsoan,
|
||||
"Shaaloani - Mehwahhetsoan"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.HeritageFoundYyasulaniStation,
|
||||
"Heritage Found - Yyasulani Station"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.HeritageFoundTheOutskirts,
|
||||
"Heritage Found - The Outskirts"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.HeritageFoundElectropeStrike,
|
||||
"Heritage Found - Electrope Strike"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.LivingMemoryLeynodeMnemo,
|
||||
"Living Memory - Leynode Mnemo"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.LivingMemoryLeynodePyro,
|
||||
"Living Memory - Leynode Pyro"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.LivingMemoryLeynodeAero,
|
||||
"Living Memory - Leynode Aero"
|
||||
}
|
||||
};
|
||||
|
||||
public AetheryteConverter()
|
||||
: base((IReadOnlyDictionary<EAetheryteLocation, string>)Values)
|
||||
{
|
||||
}
|
||||
|
||||
public static bool IsLargeAetheryte(EAetheryteLocation aetheryte)
|
||||
{
|
||||
return Values.ContainsKey(aetheryte);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Questionable.Model.Common.Converter;
|
||||
|
||||
public abstract class EnumConverter<T> : JsonConverter<T> where T : Enum
|
||||
{
|
||||
private readonly ReadOnlyDictionary<T, string> _enumToString;
|
||||
|
||||
private readonly ReadOnlyDictionary<string, T> _stringToEnum;
|
||||
|
||||
protected EnumConverter(IReadOnlyDictionary<T, string> values)
|
||||
{
|
||||
_enumToString = ((values is IDictionary<T, string> dictionary) ? new ReadOnlyDictionary<T, string>(dictionary) : new ReadOnlyDictionary<T, string>(values.ToDictionary<KeyValuePair<T, string>, T, string>((KeyValuePair<T, string> x) => x.Key, (KeyValuePair<T, string> x) => x.Value)));
|
||||
_stringToEnum = new ReadOnlyDictionary<string, T>(_enumToString.ToDictionary<KeyValuePair<T, string>, string, T>((KeyValuePair<T, string> x) => x.Value, (KeyValuePair<T, string> x) => x.Key));
|
||||
}
|
||||
|
||||
public override T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
if (reader.TokenType != JsonTokenType.String)
|
||||
{
|
||||
throw new JsonException();
|
||||
}
|
||||
string text = reader.GetString();
|
||||
if (text == null)
|
||||
{
|
||||
throw new JsonException();
|
||||
}
|
||||
if (!_stringToEnum.TryGetValue(text, out var value))
|
||||
{
|
||||
throw new JsonException();
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options)
|
||||
{
|
||||
writer.WriteStringValue(_enumToString[value]);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Questionable.Model.Common.Converter;
|
||||
|
||||
public sealed class StringListOrValueConverter : JsonConverter<List<string>>
|
||||
{
|
||||
public override List<string> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
if (reader.TokenType == JsonTokenType.String)
|
||||
{
|
||||
return new List<string>(1) { reader.GetString() };
|
||||
}
|
||||
if (reader.TokenType != JsonTokenType.StartArray)
|
||||
{
|
||||
throw new JsonException();
|
||||
}
|
||||
reader.Read();
|
||||
List<string> list = new List<string>();
|
||||
while (reader.TokenType != JsonTokenType.EndArray)
|
||||
{
|
||||
list.Add(reader.GetString());
|
||||
reader.Read();
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
public override void Write(Utf8JsonWriter writer, List<string>? value, JsonSerializerOptions options)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
writer.WriteNullValue();
|
||||
return;
|
||||
}
|
||||
if (value.Count == 1)
|
||||
{
|
||||
writer.WriteStringValue(value[0]);
|
||||
return;
|
||||
}
|
||||
writer.WriteStartArray();
|
||||
foreach (string item in value)
|
||||
{
|
||||
writer.WriteStringValue(item);
|
||||
}
|
||||
writer.WriteEndArray();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
using System;
|
||||
using System.Numerics;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Questionable.Model.Common.Converter;
|
||||
|
||||
public sealed class VectorConverter : JsonConverter<Vector3>
|
||||
{
|
||||
public override Vector3 Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
if (reader.TokenType != JsonTokenType.StartObject)
|
||||
{
|
||||
throw new JsonException();
|
||||
}
|
||||
Vector3 result = default(Vector3);
|
||||
while (reader.Read())
|
||||
{
|
||||
switch (reader.TokenType)
|
||||
{
|
||||
case JsonTokenType.PropertyName:
|
||||
{
|
||||
string text = reader.GetString();
|
||||
if (text == null || !reader.Read())
|
||||
{
|
||||
throw new JsonException();
|
||||
}
|
||||
switch (text)
|
||||
{
|
||||
case "X":
|
||||
result.X = reader.GetSingle();
|
||||
break;
|
||||
case "Y":
|
||||
result.Y = reader.GetSingle();
|
||||
break;
|
||||
case "Z":
|
||||
result.Z = reader.GetSingle();
|
||||
break;
|
||||
default:
|
||||
throw new JsonException();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case JsonTokenType.EndObject:
|
||||
return result;
|
||||
default:
|
||||
throw new JsonException();
|
||||
}
|
||||
}
|
||||
throw new JsonException();
|
||||
}
|
||||
|
||||
public override void Write(Utf8JsonWriter writer, Vector3 value, JsonSerializerOptions options)
|
||||
{
|
||||
writer.WriteStartObject();
|
||||
writer.WriteNumber("X", value.X);
|
||||
writer.WriteNumber("Y", value.Y);
|
||||
writer.WriteNumber("Z", value.Z);
|
||||
writer.WriteEndObject();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
using System.Text.Json.Serialization;
|
||||
using Questionable.Model.Questing.Converter;
|
||||
|
||||
namespace Questionable.Model.Common;
|
||||
|
||||
[JsonConverter(typeof(AethernetShortcutConverter))]
|
||||
public sealed class AethernetShortcut
|
||||
{
|
||||
public EAetheryteLocation From { get; set; }
|
||||
|
||||
public EAetheryteLocation To { get; set; }
|
||||
}
|
|
@ -0,0 +1,246 @@
|
|||
using System.Text.Json.Serialization;
|
||||
using Questionable.Model.Common.Converter;
|
||||
|
||||
namespace Questionable.Model.Common;
|
||||
|
||||
[JsonConverter(typeof(AetheryteConverter))]
|
||||
public enum EAetheryteLocation
|
||||
{
|
||||
None = 0,
|
||||
Gridania = 2,
|
||||
GridaniaArcher = 25,
|
||||
GridaniaLeatherworker = 26,
|
||||
GridaniaLancer = 27,
|
||||
GridaniaConjurer = 28,
|
||||
GridaniaBotanist = 29,
|
||||
GridaniaAmphitheatre = 30,
|
||||
GridaniaBlueBadgerGate = 31,
|
||||
GridaniaYellowSerpentGate = 32,
|
||||
GridaniaWhiteWolfGate = 54,
|
||||
GridaniaAirship = 94,
|
||||
CentralShroudBentbranchMeadows = 3,
|
||||
EastShroudHawthorneHut = 4,
|
||||
SouthShroudQuarrymill = 5,
|
||||
SouthShroudCampTranquil = 6,
|
||||
NorthShroudFallgourdFloat = 7,
|
||||
Uldah = 9,
|
||||
UldahAdventurers = 33,
|
||||
UldahThaumaturge = 34,
|
||||
UldahGladiator = 35,
|
||||
UldahMiner = 36,
|
||||
UldahAlchemist = 37,
|
||||
UldahWeaver = 47,
|
||||
UldahGoldsmith = 50,
|
||||
UldahChamberOfRule = 51,
|
||||
UldahAirship = 95,
|
||||
UldahGateOfTheSultana = 38,
|
||||
UldahGateOfNald = 39,
|
||||
UldahGateOfThal = 40,
|
||||
UldahSapphireAvenue = 125,
|
||||
WesternThanalanHorizon = 17,
|
||||
EasternThanalanCampDrybone = 18,
|
||||
SouthernThanalanLittleAlaMhigo = 19,
|
||||
SouthernThanalanForgottenSprings = 20,
|
||||
NorthernThanalanCampBluefog = 21,
|
||||
NorthernThanalanCeruleumProcessingPlant = 22,
|
||||
CentralThanalanBlackBrushStation = 53,
|
||||
Limsa = 8,
|
||||
LimsaAftcastle = 41,
|
||||
LimsaCulinarian = 42,
|
||||
LimsaArcanist = 43,
|
||||
LimsaFisher = 44,
|
||||
LimsaMarauder = 48,
|
||||
LimsaHawkersAlley = 49,
|
||||
LimsaZephyrGate = 45,
|
||||
LimsaTempestGate = 46,
|
||||
LimsaAirship = 93,
|
||||
LowerLaNosceaMorabyDrydocks = 10,
|
||||
EasternLaNosceaCostaDelSol = 11,
|
||||
EasternLaNosceaWineport = 12,
|
||||
WesternLaNosceaSwiftperch = 13,
|
||||
WesternLaNosceaAleport = 14,
|
||||
UpperLaNosceaCampBronzeLake = 15,
|
||||
OuterLaNosceaCampOverlook = 16,
|
||||
MiddleLaNosceaSummerfordFarms = 52,
|
||||
CoerthasCentralHighlandsCampDragonhead = 23,
|
||||
MorDhona = 24,
|
||||
WolvesDenPier = 55,
|
||||
GoldSaucer = 62,
|
||||
GoldSaucerEntranceCardSquares = 63,
|
||||
GoldSaucerWonderSquareEast = 64,
|
||||
GoldSaucerWonderSquareWest = 65,
|
||||
GoldSaucerEventSquare = 66,
|
||||
GoldSaucerCactpotBoard = 67,
|
||||
GoldSaucerRoundSquare = 68,
|
||||
GoldSaucerChocoboSquare = 69,
|
||||
GoldSaucerMinionSquare = 89,
|
||||
Ishgard = 70,
|
||||
IshgardForgottenKnight = 80,
|
||||
IshgardSkysteelManufactory = 81,
|
||||
IshgardBrume = 82,
|
||||
IshgardAthenaeumAstrologicum = 83,
|
||||
IshgardJeweledCrozier = 84,
|
||||
IshgardSaintReymanaudsCathedral = 85,
|
||||
IshgardTribunal = 86,
|
||||
IshgardLastVigil = 87,
|
||||
IshgardGatesOfJudgement = 88,
|
||||
IshgardFirmament = 100001,
|
||||
FirmamentMendicantsCourt = 2011373,
|
||||
FirmamentMattock = 2011374,
|
||||
FirmamentNewNest = 2011384,
|
||||
FirmanentSaintRoellesDais = 2011385,
|
||||
FirmamentFeatherfall = 2011386,
|
||||
FirmamentHoarfrostHall = 2011387,
|
||||
FirmamentWesternRisensongQuarter = 2011388,
|
||||
FIrmamentEasternRisensongQuarter = 2011389,
|
||||
Idyllshire = 75,
|
||||
IdyllshireWest = 90,
|
||||
IdyllshirePrologueGate = 91,
|
||||
IdyllshireEpilogueGate = 92,
|
||||
CoerthasWesternHighlandsFalconsNest = 71,
|
||||
SeaOfCloudsCampCloudtop = 72,
|
||||
SeaOfCloudsOkZundu = 73,
|
||||
AzysLlaHelix = 74,
|
||||
DravanianForelandsTailfeather = 76,
|
||||
DravanianForelandsAnyxTrine = 77,
|
||||
ChurningMistsMoghome = 78,
|
||||
ChurningMistsZenith = 79,
|
||||
RhalgrsReach = 104,
|
||||
RhalgrsReachWest = 121,
|
||||
RhalgrsReachNorthEast = 122,
|
||||
RhalgrsReachFringesGate = 123,
|
||||
RhalgrsReachPeaksGate = 124,
|
||||
Kugane = 111,
|
||||
KuganeShiokazeHostelry = 112,
|
||||
KuganePier1 = 113,
|
||||
KuganeThavnairianConsulate = 114,
|
||||
KuganeMarkets = 115,
|
||||
KuganeBokairoInn = 116,
|
||||
KuganeRubyBazaar = 117,
|
||||
KuganeSekiseigumiBarracks = 118,
|
||||
KuganeRakuzaDistrict = 119,
|
||||
KuganeRubyPrice = 120,
|
||||
KuganeAirship = 126,
|
||||
FringesCastrumOriens = 98,
|
||||
FringesPeeringStones = 99,
|
||||
PeaksAlaGannha = 100,
|
||||
PeaksAlaGhiri = 101,
|
||||
LochsPortaPraetoria = 102,
|
||||
LochsAlaMhiganQuarter = 103,
|
||||
RubySeaTamamizu = 105,
|
||||
RubySeaOnokoro = 106,
|
||||
YanxiaNamai = 107,
|
||||
YanxiaHouseOfTheFierce = 108,
|
||||
AzimSteppeReunion = 109,
|
||||
AzimSteppeDawnThrone = 110,
|
||||
AzimSteppeDhoroIloh = 128,
|
||||
DomanEnclave = 127,
|
||||
DomanEnclaveNorthern = 129,
|
||||
DomanEnclaveSouthern = 130,
|
||||
DomanEnclaveOneRiver = 131,
|
||||
DomanEnclaveDocks = 162,
|
||||
DomanEnclaveGangos = 163,
|
||||
Crystarium = 133,
|
||||
CrystariumMarkets = 149,
|
||||
CrystariumTemenosRookery = 150,
|
||||
CrystariumDossalGate = 151,
|
||||
CrystariumPendants = 152,
|
||||
CrystariumAmaroLaunch = 153,
|
||||
CrystariumCrystallineMean = 154,
|
||||
CrystariumCabinetOfCuriosity = 155,
|
||||
CrystariumTessellation = 156,
|
||||
Eulmore = 134,
|
||||
EulmoreMainstay = 157,
|
||||
EulmoreNightsoilPots = 158,
|
||||
EulmoreGloryGate = 159,
|
||||
EulmoreSoutheastDerelict = 135,
|
||||
EulmorePathToGlory = 160,
|
||||
LakelandFortJobb = 132,
|
||||
LakelandOstallImperative = 136,
|
||||
KholusiaStilltide = 137,
|
||||
KholusiaWright = 138,
|
||||
KholusiaTomra = 139,
|
||||
AmhAraengMordSouq = 140,
|
||||
AmhAraengInnAtJourneysHead = 161,
|
||||
AmhAraengTwine = 141,
|
||||
RaktikaSlitherbough = 142,
|
||||
RaktikaFanow = 143,
|
||||
IlMhegLydhaLran = 144,
|
||||
IlMhegPlaEnni = 145,
|
||||
IlMhegWolekdorf = 146,
|
||||
TempestOndoCups = 147,
|
||||
TempestMacarensesAngle = 148,
|
||||
OldSharlayan = 182,
|
||||
OldSharlayanStudium = 184,
|
||||
OldSharlayanBaldesionAnnex = 185,
|
||||
OldSharlayanRostra = 186,
|
||||
OldSharlayanLeveilleurEstate = 187,
|
||||
OldSharlayanJourneysEnd = 188,
|
||||
OldSharlayanScholarsHarbor = 189,
|
||||
OldSharlayanHallOfArtifice = 190,
|
||||
RadzAtHan = 183,
|
||||
RadzAtHanMeghaduta = 191,
|
||||
RadzAtHanRuveydahFibers = 192,
|
||||
RadzAtHanAirship = 193,
|
||||
RadzAtHanAlzadaalsPeace = 194,
|
||||
RadzAtHanHallOfTheRadiantHost = 195,
|
||||
RadzAtHanMehrydesMeyhane = 196,
|
||||
RadzAtHanKama = 198,
|
||||
RadzAtHanHighCrucible = 199,
|
||||
RadzAtHanGateOfFirstSight = 197,
|
||||
LabyrinthosArcheion = 166,
|
||||
LabyrinthosSharlayanHamlet = 167,
|
||||
LabyrinthosAporia = 168,
|
||||
ThavnairYedlihmad = 169,
|
||||
ThavnairGreatWork = 170,
|
||||
ThavnairPalakasStand = 171,
|
||||
GarlemaldCampBrokenGlass = 172,
|
||||
GarlemaldTertium = 173,
|
||||
MareLamentorumSinusLacrimarum = 174,
|
||||
MareLamentorumBestwaysBurrow = 175,
|
||||
ElpisAnagnorisis = 176,
|
||||
ElpisTwelveWonders = 177,
|
||||
ElpisPoietenOikos = 178,
|
||||
UltimaThuleReahTahra = 179,
|
||||
UltimaThuleAbodeOfTheEa = 180,
|
||||
UltimaThuleBaseOmicron = 181,
|
||||
Tuliyollal = 216,
|
||||
TuliyollalDirigibleLanding = 218,
|
||||
TuliyollalTheResplendentQuarter = 219,
|
||||
TuliyollalTheForardCabins = 220,
|
||||
TuliyollalBaysideBevyMarketplace = 221,
|
||||
TuliyollalVollokShoonsa = 222,
|
||||
TuliyollalWachumeqimeqi = 223,
|
||||
TuliyollalBrightploomPost = 224,
|
||||
TuliyollalArchOfTheDawnUrqopacha = 225,
|
||||
TuliyollalArchOfTheDawnKozamauka = 226,
|
||||
TuliyollalIhuykatumu = 227,
|
||||
TuliyollalDirigibleLandingYakTel = 228,
|
||||
TuliyollalXakTuralSkygate = 229,
|
||||
SolutionNine = 217,
|
||||
SolutionNineInformationCenter = 230,
|
||||
SolutionNineTrueVue = 231,
|
||||
SolutionNineNeonStein = 232,
|
||||
SolutionNineTheArcadion = 233,
|
||||
SolutionNineResolution = 234,
|
||||
SolutionNineNexusArcade = 235,
|
||||
SolutionNineResidentialSector = 236,
|
||||
SolutionNineScanningPortNine = 237,
|
||||
UrqopachaWachunpelo = 200,
|
||||
UrqopachaWorlarsEcho = 201,
|
||||
KozamaukaOkHanu = 202,
|
||||
KozamaukaManyFires = 203,
|
||||
KozamaukaEarthenshire = 204,
|
||||
KozamaukaDockPoga = 238,
|
||||
YakTelIqBraax = 205,
|
||||
YakTelMamook = 206,
|
||||
ShaaloaniHhusatahwi = 207,
|
||||
ShaaloaniShesheneweziSprings = 208,
|
||||
ShaaloaniMehwahhetsoan = 209,
|
||||
HeritageFoundYyasulaniStation = 210,
|
||||
HeritageFoundTheOutskirts = 211,
|
||||
HeritageFoundElectropeStrike = 212,
|
||||
LivingMemoryLeynodeMnemo = 213,
|
||||
LivingMemoryLeynodePyro = 214,
|
||||
LivingMemoryLeynodeAero = 215
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
namespace Questionable.Model.Common;
|
||||
|
||||
public static class EAetheryteLocationExtensions
|
||||
{
|
||||
public static bool IsFirmamentAetheryte(this EAetheryteLocation aetheryteLocation)
|
||||
{
|
||||
if (aetheryteLocation == EAetheryteLocation.IshgardFirmament || (uint)(aetheryteLocation - 2011373) <= 1u || (uint)(aetheryteLocation - 2011384) <= 5u)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
151
Questionable.Model/Questionable.Model.CommonAethernetShard
Normal file
151
Questionable.Model/Questionable.Model.CommonAethernetShard
Normal file
|
@ -0,0 +1,151 @@
|
|||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://qstxiv.github.io/schema/common-aethernetshard.json",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"[Gridania] Aetheryte Plaza",
|
||||
"[Gridania] Archers' Guild",
|
||||
"[Gridania] Leatherworkers' Guild & Shaded Bower",
|
||||
"[Gridania] Lancers' Guild",
|
||||
"[Gridania] Conjurers' Guild",
|
||||
"[Gridania] Botanists' Guild",
|
||||
"[Gridania] Mih Khetto's Amphitheatre",
|
||||
"[Gridania] Blue Badger Gate (Central Shroud)",
|
||||
"[Gridania] Yellow Serpent Gate (North Shroud)",
|
||||
"[Gridania] White Wolf Gate (Central Shroud)",
|
||||
"[Gridania] Airship Landing",
|
||||
"[Ul'dah] Aetheryte Plaza",
|
||||
"[Ul'dah] Adventurers' Guild",
|
||||
"[Ul'dah] Thaumaturges' Guild",
|
||||
"[Ul'dah] Gladiators' Guild",
|
||||
"[Ul'dah] Miners' Guild",
|
||||
"[Ul'dah] Weavers' Guild",
|
||||
"[Ul'dah] Goldsmiths' Guild",
|
||||
"[Ul'dah] Sapphire Avenue Exchange",
|
||||
"[Ul'dah] Alchemists' Guild",
|
||||
"[Ul'dah] Gate of the Sultana (Western Thanalan)",
|
||||
"[Ul'dah] Gate of Nald (Central Thanalan)",
|
||||
"[Ul'dah] Gate of Thal (Central Thanalan)",
|
||||
"[Ul'dah] The Chamber of Rule",
|
||||
"[Ul'dah] Airship Landing",
|
||||
"[Limsa Lominsa] Aetheryte Plaza",
|
||||
"[Limsa Lominsa] Arcanists' Guild",
|
||||
"[Limsa Lominsa] Fishermen's Guild",
|
||||
"[Limsa Lominsa] Hawkers' Alley",
|
||||
"[Limsa Lominsa] The Aftcastle",
|
||||
"[Limsa Lominsa] Culinarians' Guild",
|
||||
"[Limsa Lominsa] Marauders' Guild",
|
||||
"[Limsa Lominsa] Zephyr Gate (Middle La Noscea)",
|
||||
"[Limsa Lominsa] Tempest Gate (Lower La Noscea)",
|
||||
"[Limsa Lominsa] Airship Landing",
|
||||
"[Gold Saucer] Aetheryte Plaza",
|
||||
"[Gold Saucer] Entrance & Card Squares",
|
||||
"[Gold Saucer] Wonder Square East",
|
||||
"[Gold Saucer] Wonder Square West",
|
||||
"[Gold Saucer] Event Square",
|
||||
"[Gold Saucer] Cactpot Board",
|
||||
"[Gold Saucer] Round Square",
|
||||
"[Gold Saucer] Chocobo Square",
|
||||
"[Gold Saucer] Minion Square",
|
||||
"[Ishgard] Aetheryte Plaza",
|
||||
"[Ishgard] The Forgotten Knight",
|
||||
"[Ishgard] Skysteel Manufactory",
|
||||
"[Ishgard] The Brume",
|
||||
"[Ishgard] Athenaeum Astrologicum",
|
||||
"[Ishgard] The Jeweled Crozier",
|
||||
"[Ishgard] Saint Reymanaud's Cathedral",
|
||||
"[Ishgard] The Tribunal",
|
||||
"[Ishgard] The Last Vigil",
|
||||
"[Ishgard] The Gates of Judgement (Coerthas Central Highlands)",
|
||||
"[Ishgard] Firmament",
|
||||
"[Firmament] The Mendicant's Court",
|
||||
"[Firmament] The Mattock",
|
||||
"[Firmament] The New Nest",
|
||||
"[Firmament] Saint Roelle's Dais",
|
||||
"[Firmament] Featherfall",
|
||||
"[Firmament] Hoarfrost Hall",
|
||||
"[Firmament] Western Risensong Quarter",
|
||||
"[Firmament] Eastern Risensong Quarter",
|
||||
"[Idyllshire] Aetheryte Plaza",
|
||||
"[Idyllshire] West Idyllshire",
|
||||
"[Idyllshire] Prologue Gate (Western Hinterlands)",
|
||||
"[Idyllshire] Epilogue Gate (Eastern Hinterlands)",
|
||||
"[Rhalgr's Reach] Aetheryte Plaza",
|
||||
"[Rhalgr's Reach] Western Rhalgr's Reach",
|
||||
"[Rhalgr's Reach] Northeastern Rhalgr's Reach",
|
||||
"[Rhalgr's Reach] Fringes Gate",
|
||||
"[Rhalgr's Reach] Peaks Gate",
|
||||
"[Kugane] Aetheryte Plaza",
|
||||
"[Kugane] Shiokaze Hostelry",
|
||||
"[Kugane] Pier #1",
|
||||
"[Kugane] Thavnairian Consulate",
|
||||
"[Kugane] Kogane Dori Markets",
|
||||
"[Kugane] Bokairo Inn",
|
||||
"[Kugane] The Ruby Bazaar",
|
||||
"[Kugane] Sekiseigumi Barracks",
|
||||
"[Kugane] Rakuza District",
|
||||
"[Kugane] The Ruby Price",
|
||||
"[Kugane] Airship Landing",
|
||||
"[Doman Enclave] Aetheryte Plaza",
|
||||
"[Doman Enclave] The Northern Enclave",
|
||||
"[Doman Enclave] The Southern Enclave",
|
||||
"[Doman Enclave] Ferry Docks",
|
||||
"[Doman Enclave] The One River",
|
||||
"[Doman Enclave] Gangos",
|
||||
"[Crystarium] Aetheryte Plaza",
|
||||
"[Crystarium] Musica Universalis Markets",
|
||||
"[Crystarium] Temenos Rookery",
|
||||
"[Crystarium] The Dossal Gate",
|
||||
"[Crystarium] The Pendants",
|
||||
"[Crystarium] The Amaro Launch",
|
||||
"[Crystarium] The Crystalline Mean",
|
||||
"[Crystarium] The Cabinet of Curiosity",
|
||||
"[Crystarium] Tessellation (Lakeland)",
|
||||
"[Eulmore] Aetheryte Plaza",
|
||||
"[Eulmore] Southeast Derelicts",
|
||||
"[Eulmore] Nightsoil Pots",
|
||||
"[Eulmore] The Glory Gate",
|
||||
"[Eulmore] The Mainstay",
|
||||
"[Eulmore] The Path to Glory (Kholusia)",
|
||||
"[Old Sharlayan] Aetheryte Plaza",
|
||||
"[Old Sharlayan] The Studium",
|
||||
"[Old Sharlayan] The Baldesion Annex",
|
||||
"[Old Sharlayan] The Rostra",
|
||||
"[Old Sharlayan] The Leveilleur Estate",
|
||||
"[Old Sharlayan] Journey's End",
|
||||
"[Old Sharlayan] Scholar's Harbor",
|
||||
"[Old Sharlayan] The Hall of Artifice (Labyrinthos)",
|
||||
"[Radz-at-Han] Aetheryte Plaza",
|
||||
"[Radz-at-Han] Meghaduta",
|
||||
"[Radz-at-Han] Ruveydah Fibers",
|
||||
"[Radz-at-Han] Airship Landing",
|
||||
"[Radz-at-Han] Alzadaal's Peace",
|
||||
"[Radz-at-Han] Hall of the Radiant Host",
|
||||
"[Radz-at-Han] Mehryde's Meyhane",
|
||||
"[Radz-at-Han] Kama",
|
||||
"[Radz-at-Han] The High Crucible of Al-Kimiya",
|
||||
"[Radz-at-Han] The Gate of First Sight (Thavnair)",
|
||||
"[Tuliyollal] Aetheryte Plaza",
|
||||
"[Tuliyollal] Dirigible Landing",
|
||||
"[Tuliyollal] The Resplendent Quarter",
|
||||
"[Tuliyollal] The For'ard Cabins",
|
||||
"[Tuliyollal] Bayside Bevy Marketplace",
|
||||
"[Tuliyollal] Vollok Shoonsa",
|
||||
"[Tuliyollal] Wachumeqimeqi",
|
||||
"[Tuliyollal] Brightploom Post",
|
||||
"[Tuliyollal] Arch of the Dawn (Urqopacha)",
|
||||
"[Tuliyollal] Arch of the Dawn (Kozama'uka)",
|
||||
"[Tuliyollal] Ihuykatumu (Kozama'uka)",
|
||||
"[Tuliyollal] Dirigible Landing (Yak T'el)",
|
||||
"[Tuliyollal] Xak Tural Skygate (Shaaloani)",
|
||||
"[Solution Nine] Aetheryte Plaza",
|
||||
"[Solution Nine] Information Center",
|
||||
"[Solution Nine] True Vue",
|
||||
"[Solution Nine] Neon Stein",
|
||||
"[Solution Nine] The Arcadion",
|
||||
"[Solution Nine] Resolution",
|
||||
"[Solution Nine] Nexus Arcade",
|
||||
"[Solution Nine] Residential Sector",
|
||||
"[Solution Nine] Scanning Port Nine (Heritage Found)"
|
||||
]
|
||||
}
|
114
Questionable.Model/Questionable.Model.CommonAetheryte
Normal file
114
Questionable.Model/Questionable.Model.CommonAetheryte
Normal file
|
@ -0,0 +1,114 @@
|
|||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://qstxiv.github.io/schema/common-aetheryte.json",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Gridania",
|
||||
"Central Shroud - Bentbranch Meadows",
|
||||
"East Shroud - Hawthorne Hut",
|
||||
"South Shroud - Quarrymill",
|
||||
"South Shroud - Camp Tranquil",
|
||||
"North Shroud - Fallgourd Float",
|
||||
"Ul'dah",
|
||||
"Western Thanalan - Horizon",
|
||||
"Central Thanalan - Black Brush Station",
|
||||
"Eastern Thanalan - Camp Drybone",
|
||||
"Southern Thanalan - Little Ala Mhigo",
|
||||
"Southern Thanalan - Forgotten Springs",
|
||||
"Northern Thanalan - Camp Bluefog",
|
||||
"Northern Thanalan - Ceruleum Processing Plant",
|
||||
"Limsa Lominsa",
|
||||
"Middle La Noscea - Summerford Farms",
|
||||
"Lower La Noscea - Moraby Drydocks",
|
||||
"Eastern La Noscea - Costa Del Sol",
|
||||
"Eastern La Noscea - Wineport",
|
||||
"Western La Noscea - Swiftperch",
|
||||
"Western La Noscea - Aleport",
|
||||
"Upper La Noscea - Camp Bronze Lake",
|
||||
"Outer La Noscea - Camp Overlook",
|
||||
"Coerthas Central Highlands - Camp Dragonhead",
|
||||
"Mor Dhona",
|
||||
"Wolves' Den Pier",
|
||||
"Gold Saucer",
|
||||
"Ishgard",
|
||||
"Idyllshire",
|
||||
"Coerthas Western Highlands - Falcon's Nest",
|
||||
"The Sea of Clouds - Camp Cloudtop",
|
||||
"The Sea of Clouds - Ok' Zundu",
|
||||
"Azys Lla - Helix",
|
||||
"The Dravanian Forelands - Tailfeather",
|
||||
"The Dravanian Forelands - Anyx Trine",
|
||||
"The Churning Mists - Moghome",
|
||||
"The Churning Mists - Zenith",
|
||||
"Rhalgr's Reach",
|
||||
"Fringes - Castrum Oriens",
|
||||
"Fringes - Peering Stones",
|
||||
"Peaks - Ala Gannha",
|
||||
"Peaks - Ala Ghiri",
|
||||
"Lochs - Porta Praetoria",
|
||||
"Lochs - Ala Mhigan Quarter",
|
||||
"Kugane",
|
||||
"Ruby Sea - Tamamizu",
|
||||
"Ruby Sea - Onokoro",
|
||||
"Yanxia - Namai",
|
||||
"Yanxia - House of the Fierce",
|
||||
"Azim Steppe - Reunion",
|
||||
"Azim Steppe - Dawn Throne",
|
||||
"Azim Steppe - Dhoro Iloh",
|
||||
"Doman Enclave",
|
||||
"Crystarium",
|
||||
"Eulmore",
|
||||
"Lakeland - Fort Jobb",
|
||||
"Lakeland - Ostall Imperative",
|
||||
"Kholusia - Stilltide",
|
||||
"Kholusia - Wright",
|
||||
"Kholusia - Tomra",
|
||||
"Amh Araeng - Mord Souq",
|
||||
"Amh Araeng - Inn at Journey's Head",
|
||||
"Amh Araeng - Twine",
|
||||
"Rak'tika - Slitherbough",
|
||||
"Rak'tika - Fanow",
|
||||
"Il Mheg - Lydha Lran",
|
||||
"Il Mheg - Pla Enni",
|
||||
"Il Mheg - Wolekdorf",
|
||||
"Tempest - Ondo Cups",
|
||||
"Tempest - Macarenses Angle",
|
||||
"Old Sharlayan",
|
||||
"Radz-at-Han",
|
||||
"Labyrinthos - Archeion",
|
||||
"Labyrinthos - Sharlayan Hamlet",
|
||||
"Labyrinthos - Aporia",
|
||||
"Thavnair - Yedlihmad",
|
||||
"Thavnair - Great Work",
|
||||
"Thavnair - Palaka's Stand",
|
||||
"Garlemald - Camp Broken Glass",
|
||||
"Garlemald - Tertium",
|
||||
"Mare Lamentorum - Sinus Lacrimarum",
|
||||
"Mare Lamentorum - Bestways Burrow",
|
||||
"Elpis - Anagnorisis",
|
||||
"Elpis - Twelve Wonders",
|
||||
"Elpis - Poieten Oikos",
|
||||
"Ultima Thule - Reah Tahra",
|
||||
"Ultima Thule - Abode of the Ea",
|
||||
"Ultima Thule - Base Omicron",
|
||||
"Tuliyollal",
|
||||
"Solution Nine",
|
||||
"Urqopacha - Wachunpelo",
|
||||
"Urqopacha - Worlar's Echo",
|
||||
"Kozama'uka - Ok'hanu",
|
||||
"Kozama'uka - Many Fires",
|
||||
"Kozama'uka - Earthenshire",
|
||||
"Kozama'uka - Dock Poga",
|
||||
"Yak T'el - Iq Br'aax",
|
||||
"Yak T'el - Mamook",
|
||||
"Shaaloani - Hhusatahwi",
|
||||
"Shaaloani - Sheshenewezi Springs",
|
||||
"Shaaloani - Mehwahhetsoan",
|
||||
"Heritage Found - Yyasulani Station",
|
||||
"Heritage Found - The Outskirts",
|
||||
"Heritage Found - Electrope Strike",
|
||||
"Living Memory - Leynode Mnemo",
|
||||
"Living Memory - Leynode Pyro",
|
||||
"Living Memory - Leynode Aero"
|
||||
]
|
||||
}
|
55
Questionable.Model/Questionable.Model.CommonClassJob
Normal file
55
Questionable.Model/Questionable.Model.CommonClassJob
Normal file
|
@ -0,0 +1,55 @@
|
|||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://qstxiv.github.io/schema/common-classjob.json",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Gladiator",
|
||||
"Pugilist",
|
||||
"Marauder",
|
||||
"Lancer",
|
||||
"Archer",
|
||||
"Conjurer",
|
||||
"Thaumaturge",
|
||||
"Carpenter",
|
||||
"Blacksmith",
|
||||
"Armorer",
|
||||
"Goldsmith",
|
||||
"Leatherworker",
|
||||
"Weaver",
|
||||
"Alchemist",
|
||||
"Culinarian",
|
||||
"Miner",
|
||||
"Botanist",
|
||||
"Fisher",
|
||||
"Paladin",
|
||||
"Monk",
|
||||
"Warrior",
|
||||
"Dragoon",
|
||||
"Bard",
|
||||
"White Mage",
|
||||
"Black Mage",
|
||||
"Arcanist",
|
||||
"Summoner",
|
||||
"Scholar",
|
||||
"Rogue",
|
||||
"Ninja",
|
||||
"Machinist",
|
||||
"Dark Knight",
|
||||
"Astrologian",
|
||||
"Samurai",
|
||||
"Red Mage",
|
||||
"Blue Mage",
|
||||
"Gunbreaker",
|
||||
"Dancer",
|
||||
"Reaper",
|
||||
"Sage",
|
||||
"Viper",
|
||||
"Pictomancer",
|
||||
"DoW",
|
||||
"DoM",
|
||||
"DoH",
|
||||
"DoL",
|
||||
"ConfiguredCombatJob",
|
||||
"QuestStartJob"
|
||||
]
|
||||
}
|
56
Questionable.Model/Questionable.Model.CommonCompletionFlags
Normal file
56
Questionable.Model/Questionable.Model.CommonCompletionFlags
Normal file
|
@ -0,0 +1,56 @@
|
|||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://qstxiv.github.io/schema/common-completionflags.json",
|
||||
"type": "array",
|
||||
"description": "Quest Variables that dictate whether or not this step is skipped: null is don't check, positive values need to be set, negative values need to be unset",
|
||||
"items": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"High": {
|
||||
"type": [
|
||||
"number",
|
||||
"null"
|
||||
],
|
||||
"minimum": 0,
|
||||
"maximum": 15
|
||||
},
|
||||
"Low": {
|
||||
"type": [
|
||||
"number",
|
||||
"null"
|
||||
],
|
||||
"minimum": 0,
|
||||
"maximum": 15
|
||||
},
|
||||
"Mode": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Bitwise",
|
||||
"Exact"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "number",
|
||||
"enum": [
|
||||
1,
|
||||
2,
|
||||
4,
|
||||
8,
|
||||
16,
|
||||
32,
|
||||
64,
|
||||
128
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"minItems": 6,
|
||||
"maxItems": 6
|
||||
}
|
22
Questionable.Model/Questionable.Model.CommonVector3
Normal file
22
Questionable.Model/Questionable.Model.CommonVector3
Normal file
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://qstxiv.github.io/schema/common-vector3.json",
|
||||
"type": "object",
|
||||
"description": "Position in the world",
|
||||
"properties": {
|
||||
"X": {
|
||||
"type": "number"
|
||||
},
|
||||
"Y": {
|
||||
"type": "number"
|
||||
},
|
||||
"Z": {
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"X",
|
||||
"Y",
|
||||
"Z"
|
||||
]
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
using System;
|
||||
using System.Numerics;
|
||||
using System.Text.Json.Serialization;
|
||||
using Questionable.Model.Common.Converter;
|
||||
|
||||
namespace Questionable.Model.Gathering;
|
||||
|
||||
public sealed class GatheringLocation
|
||||
{
|
||||
[JsonIgnore]
|
||||
public Guid InternalId { get; } = Guid.NewGuid();
|
||||
|
||||
[JsonConverter(typeof(VectorConverter))]
|
||||
public Vector3 Position { get; set; }
|
||||
|
||||
public int? MinimumAngle { get; set; }
|
||||
|
||||
public int? MaximumAngle { get; set; }
|
||||
|
||||
public float? MinimumDistance { get; set; }
|
||||
|
||||
public float? MaximumDistance { get; set; }
|
||||
|
||||
public bool IsCone()
|
||||
{
|
||||
if (MinimumAngle.HasValue)
|
||||
{
|
||||
return MaximumAngle.HasValue;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public float CalculateMinimumDistance()
|
||||
{
|
||||
return MinimumDistance ?? 1f;
|
||||
}
|
||||
|
||||
public float CalculateMaximumDistance()
|
||||
{
|
||||
return MaximumDistance ?? 3f;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace Questionable.Model.Gathering;
|
||||
|
||||
public sealed class GatheringNode
|
||||
{
|
||||
public uint DataId { get; set; }
|
||||
|
||||
public bool? Fly { get; set; }
|
||||
|
||||
public List<GatheringLocation> Locations { get; set; } = new List<GatheringLocation>();
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace Questionable.Model.Gathering;
|
||||
|
||||
public sealed class GatheringNodeGroup
|
||||
{
|
||||
public List<GatheringNode> Nodes { get; set; } = new List<GatheringNode>();
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
|
||||
namespace Questionable.Model.Gathering;
|
||||
|
||||
public class GatheringPointId : IComparable<GatheringPointId>, IEquatable<GatheringPointId>
|
||||
{
|
||||
public ushort Value { get; }
|
||||
|
||||
public GatheringPointId(ushort value)
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public int CompareTo(GatheringPointId? other)
|
||||
{
|
||||
if ((object)this == other)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if ((object)other == null)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
return Value.CompareTo(other.Value);
|
||||
}
|
||||
|
||||
public bool Equals(GatheringPointId? other)
|
||||
{
|
||||
if ((object)other == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if ((object)this == other)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return Value == other.Value;
|
||||
}
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
if (obj == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (this == obj)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (obj.GetType() != GetType())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return Equals((GatheringPointId)obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Value.GetHashCode();
|
||||
}
|
||||
|
||||
public static bool operator ==(GatheringPointId? left, GatheringPointId? right)
|
||||
{
|
||||
return object.Equals(left, right);
|
||||
}
|
||||
|
||||
public static bool operator !=(GatheringPointId? left, GatheringPointId? right)
|
||||
{
|
||||
return !object.Equals(left, right);
|
||||
}
|
||||
|
||||
public static GatheringPointId FromString(string value)
|
||||
{
|
||||
return new GatheringPointId(ushort.Parse(value, CultureInfo.InvariantCulture));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Text.Json.Serialization;
|
||||
using Questionable.Model.Common.Converter;
|
||||
using Questionable.Model.Questing;
|
||||
|
||||
namespace Questionable.Model.Gathering;
|
||||
|
||||
public sealed class GatheringRoot
|
||||
{
|
||||
[JsonConverter(typeof(StringListOrValueConverter))]
|
||||
public List<string> Author { get; set; } = new List<string>();
|
||||
|
||||
public List<QuestStep> Steps { get; set; } = new List<QuestStep>();
|
||||
|
||||
public bool? FlyBetweenNodes { get; set; }
|
||||
|
||||
public List<uint> ExtraQuestItems { get; set; } = new List<uint>();
|
||||
|
||||
public List<GatheringNodeGroup> Groups { get; set; } = new List<GatheringNodeGroup>();
|
||||
}
|
|
@ -0,0 +1,200 @@
|
|||
using System.Collections.Generic;
|
||||
using Questionable.Model.Common.Converter;
|
||||
|
||||
namespace Questionable.Model.Questing.Converter;
|
||||
|
||||
public sealed class ActionConverter : EnumConverter<EAction>
|
||||
{
|
||||
private static readonly Dictionary<EAction, string> Values = new Dictionary<EAction, string>
|
||||
{
|
||||
{
|
||||
EAction.DutyAction1,
|
||||
"Duty Action I"
|
||||
},
|
||||
{
|
||||
EAction.DutyAction2,
|
||||
"Duty Action II"
|
||||
},
|
||||
{
|
||||
EAction.HeavySwing,
|
||||
"Heavy Swing"
|
||||
},
|
||||
{
|
||||
EAction.Bootshine,
|
||||
"Bootshine"
|
||||
},
|
||||
{
|
||||
EAction.TwinSnakes,
|
||||
"Twin Snakes"
|
||||
},
|
||||
{
|
||||
EAction.Demolish,
|
||||
"Demolish"
|
||||
},
|
||||
{
|
||||
EAction.DragonKick,
|
||||
"Dragon Kick"
|
||||
},
|
||||
{
|
||||
EAction.HeavyShot,
|
||||
"Heavy Shot"
|
||||
},
|
||||
{
|
||||
EAction.Cure,
|
||||
"Cure"
|
||||
},
|
||||
{
|
||||
EAction.Cure2,
|
||||
"Cure II"
|
||||
},
|
||||
{
|
||||
EAction.Eukrasia,
|
||||
"Eukrasia"
|
||||
},
|
||||
{
|
||||
EAction.Diagnosis,
|
||||
"Diagnosis"
|
||||
},
|
||||
{
|
||||
EAction.EukrasianDiagnosis,
|
||||
"Eukrasian Diagnosis"
|
||||
},
|
||||
{
|
||||
EAction.Esuna,
|
||||
"Esuna"
|
||||
},
|
||||
{
|
||||
EAction.Physick,
|
||||
"Physick"
|
||||
},
|
||||
{
|
||||
EAction.AspectedBenefic,
|
||||
"Aspected Benefic"
|
||||
},
|
||||
{
|
||||
EAction.FormShift,
|
||||
"Form Shift"
|
||||
},
|
||||
{
|
||||
EAction.FieryBreath,
|
||||
"Fiery Breath"
|
||||
},
|
||||
{
|
||||
EAction.BuffetSanuwa,
|
||||
"Buffet (Sanuwa)"
|
||||
},
|
||||
{
|
||||
EAction.BuffetGriffin,
|
||||
"Buffet (Griffin)"
|
||||
},
|
||||
{
|
||||
EAction.Trample,
|
||||
"Trample"
|
||||
},
|
||||
{
|
||||
EAction.Fumigate,
|
||||
"Fumigate"
|
||||
},
|
||||
{
|
||||
EAction.Roar,
|
||||
"Roar"
|
||||
},
|
||||
{
|
||||
EAction.Seed,
|
||||
"Seed"
|
||||
},
|
||||
{
|
||||
EAction.Inhale,
|
||||
"Inhale"
|
||||
},
|
||||
{
|
||||
EAction.SiphonSnout,
|
||||
"Siphon Snout"
|
||||
},
|
||||
{
|
||||
EAction.PeculiarLight,
|
||||
"Peculiar Light"
|
||||
},
|
||||
{
|
||||
EAction.Cannonfire,
|
||||
"Cannonfire"
|
||||
},
|
||||
{
|
||||
EAction.RedGulal,
|
||||
"Red Gulal"
|
||||
},
|
||||
{
|
||||
EAction.YellowGulal,
|
||||
"Yellow Gulal"
|
||||
},
|
||||
{
|
||||
EAction.BlueGulal,
|
||||
"Blue Gulal"
|
||||
},
|
||||
{
|
||||
EAction.ElectrixFlux,
|
||||
"Electric Flux"
|
||||
},
|
||||
{
|
||||
EAction.HopStep,
|
||||
"Hop-step"
|
||||
},
|
||||
{
|
||||
EAction.Hide,
|
||||
"Hide"
|
||||
},
|
||||
{
|
||||
EAction.FumaShuriken,
|
||||
"Fuma Shuriken"
|
||||
},
|
||||
{
|
||||
EAction.Katon,
|
||||
"Katon"
|
||||
},
|
||||
{
|
||||
EAction.Raiton,
|
||||
"Raiton"
|
||||
},
|
||||
{
|
||||
EAction.SlugShot,
|
||||
"Slug Shot"
|
||||
},
|
||||
{
|
||||
EAction.BosomBrook,
|
||||
"Bosom Brook"
|
||||
},
|
||||
{
|
||||
EAction.Souleater,
|
||||
"Souleater"
|
||||
},
|
||||
{
|
||||
EAction.Fire3,
|
||||
"Fire III"
|
||||
},
|
||||
{
|
||||
EAction.Adloquium,
|
||||
"Adloquium"
|
||||
},
|
||||
{
|
||||
EAction.WaterCannon,
|
||||
"Water Cannon"
|
||||
},
|
||||
{
|
||||
EAction.Wasshoi,
|
||||
"Wasshoi"
|
||||
},
|
||||
{
|
||||
EAction.ShroudedLuminescence,
|
||||
"Shrouded Luminescence"
|
||||
},
|
||||
{
|
||||
EAction.BigSneeze,
|
||||
"Big Sneeze"
|
||||
}
|
||||
};
|
||||
|
||||
public ActionConverter()
|
||||
: base((IReadOnlyDictionary<EAction, string>)Values)
|
||||
{
|
||||
}
|
||||
}
|
|
@ -0,0 +1,593 @@
|
|||
using System.Collections.Generic;
|
||||
using Questionable.Model.Common;
|
||||
using Questionable.Model.Common.Converter;
|
||||
|
||||
namespace Questionable.Model.Questing.Converter;
|
||||
|
||||
public sealed class AethernetShardConverter : EnumConverter<EAetheryteLocation>
|
||||
{
|
||||
public static readonly Dictionary<EAetheryteLocation, string> Values = new Dictionary<EAetheryteLocation, string>
|
||||
{
|
||||
{
|
||||
EAetheryteLocation.Gridania,
|
||||
"[Gridania] Aetheryte Plaza"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.GridaniaArcher,
|
||||
"[Gridania] Archers' Guild"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.GridaniaLeatherworker,
|
||||
"[Gridania] Leatherworkers' Guild & Shaded Bower"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.GridaniaLancer,
|
||||
"[Gridania] Lancers' Guild"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.GridaniaConjurer,
|
||||
"[Gridania] Conjurers' Guild"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.GridaniaBotanist,
|
||||
"[Gridania] Botanists' Guild"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.GridaniaAmphitheatre,
|
||||
"[Gridania] Mih Khetto's Amphitheatre"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.GridaniaBlueBadgerGate,
|
||||
"[Gridania] Blue Badger Gate (Central Shroud)"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.GridaniaYellowSerpentGate,
|
||||
"[Gridania] Yellow Serpent Gate (North Shroud)"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.GridaniaWhiteWolfGate,
|
||||
"[Gridania] White Wolf Gate (Central Shroud)"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.GridaniaAirship,
|
||||
"[Gridania] Airship Landing"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.Uldah,
|
||||
"[Ul'dah] Aetheryte Plaza"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.UldahAdventurers,
|
||||
"[Ul'dah] Adventurers' Guild"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.UldahThaumaturge,
|
||||
"[Ul'dah] Thaumaturges' Guild"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.UldahGladiator,
|
||||
"[Ul'dah] Gladiators' Guild"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.UldahMiner,
|
||||
"[Ul'dah] Miners' Guild"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.UldahWeaver,
|
||||
"[Ul'dah] Weavers' Guild"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.UldahGoldsmith,
|
||||
"[Ul'dah] Goldsmiths' Guild"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.UldahSapphireAvenue,
|
||||
"[Ul'dah] Sapphire Avenue Exchange"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.UldahAlchemist,
|
||||
"[Ul'dah] Alchemists' Guild"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.UldahChamberOfRule,
|
||||
"[Ul'dah] The Chamber of Rule"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.UldahGateOfTheSultana,
|
||||
"[Ul'dah] Gate of the Sultana (Western Thanalan)"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.UldahGateOfNald,
|
||||
"[Ul'dah] Gate of Nald (Central Thanalan)"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.UldahGateOfThal,
|
||||
"[Ul'dah] Gate of Thal (Central Thanalan)"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.UldahAirship,
|
||||
"[Ul'dah] Airship Landing"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.Limsa,
|
||||
"[Limsa Lominsa] Aetheryte Plaza"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.LimsaArcanist,
|
||||
"[Limsa Lominsa] Arcanists' Guild"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.LimsaFisher,
|
||||
"[Limsa Lominsa] Fishermen's Guild"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.LimsaHawkersAlley,
|
||||
"[Limsa Lominsa] Hawkers' Alley"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.LimsaAftcastle,
|
||||
"[Limsa Lominsa] The Aftcastle"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.LimsaCulinarian,
|
||||
"[Limsa Lominsa] Culinarians' Guild"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.LimsaMarauder,
|
||||
"[Limsa Lominsa] Marauders' Guild"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.LimsaZephyrGate,
|
||||
"[Limsa Lominsa] Zephyr Gate (Middle La Noscea)"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.LimsaTempestGate,
|
||||
"[Limsa Lominsa] Tempest Gate (Lower La Noscea)"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.LimsaAirship,
|
||||
"[Limsa Lominsa] Airship Landing"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.GoldSaucer,
|
||||
"[Gold Saucer] Aetheryte Plaza"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.GoldSaucerEntranceCardSquares,
|
||||
"[Gold Saucer] Entrance & Card Squares"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.GoldSaucerWonderSquareEast,
|
||||
"[Gold Saucer] Wonder Square East"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.GoldSaucerWonderSquareWest,
|
||||
"[Gold Saucer] Wonder Square West"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.GoldSaucerEventSquare,
|
||||
"[Gold Saucer] Event Square"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.GoldSaucerCactpotBoard,
|
||||
"[Gold Saucer] Cactpot Board"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.GoldSaucerRoundSquare,
|
||||
"[Gold Saucer] Round Square"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.GoldSaucerChocoboSquare,
|
||||
"[Gold Saucer] Chocobo Square"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.GoldSaucerMinionSquare,
|
||||
"[Gold Saucer] Minion Square"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.Ishgard,
|
||||
"[Ishgard] Aetheryte Plaza"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.IshgardForgottenKnight,
|
||||
"[Ishgard] The Forgotten Knight"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.IshgardSkysteelManufactory,
|
||||
"[Ishgard] Skysteel Manufactory"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.IshgardBrume,
|
||||
"[Ishgard] The Brume"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.IshgardAthenaeumAstrologicum,
|
||||
"[Ishgard] Athenaeum Astrologicum"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.IshgardJeweledCrozier,
|
||||
"[Ishgard] The Jeweled Crozier"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.IshgardSaintReymanaudsCathedral,
|
||||
"[Ishgard] Saint Reymanaud's Cathedral"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.IshgardTribunal,
|
||||
"[Ishgard] The Tribunal"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.IshgardLastVigil,
|
||||
"[Ishgard] The Last Vigil"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.IshgardGatesOfJudgement,
|
||||
"[Ishgard] The Gates of Judgement (Coerthas Central Highlands)"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.IshgardFirmament,
|
||||
"[Ishgard] Firmament"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.FirmamentMendicantsCourt,
|
||||
"[Firmament] The Mendicant's Court"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.FirmamentMattock,
|
||||
"[Firmament] The Mattock"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.FirmamentNewNest,
|
||||
"[Firmament] The New Nest"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.FirmanentSaintRoellesDais,
|
||||
"[Firmament] Saint Roelle's Dais"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.FirmamentFeatherfall,
|
||||
"[Firmament] Featherfall"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.FirmamentHoarfrostHall,
|
||||
"[Firmament] Hoarfrost Hall"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.FirmamentWesternRisensongQuarter,
|
||||
"[Firmament] Western Risensong Quarter"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.FIrmamentEasternRisensongQuarter,
|
||||
"[Firmament] Eastern Risensong Quarter"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.Idyllshire,
|
||||
"[Idyllshire] Aetheryte Plaza"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.IdyllshireWest,
|
||||
"[Idyllshire] West Idyllshire"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.IdyllshirePrologueGate,
|
||||
"[Idyllshire] Prologue Gate (Western Hinterlands)"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.IdyllshireEpilogueGate,
|
||||
"[Idyllshire] Epilogue Gate (Eastern Hinterlands)"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.RhalgrsReach,
|
||||
"[Rhalgr's Reach] Aetheryte Plaza"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.RhalgrsReachWest,
|
||||
"[Rhalgr's Reach] Western Rhalgr's Reach"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.RhalgrsReachNorthEast,
|
||||
"[Rhalgr's Reach] Northeastern Rhalgr's Reach"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.RhalgrsReachFringesGate,
|
||||
"[Rhalgr's Reach] Fringes Gate"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.RhalgrsReachPeaksGate,
|
||||
"[Rhalgr's Reach] Peaks Gate"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.Kugane,
|
||||
"[Kugane] Aetheryte Plaza"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.KuganeShiokazeHostelry,
|
||||
"[Kugane] Shiokaze Hostelry"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.KuganePier1,
|
||||
"[Kugane] Pier #1"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.KuganeThavnairianConsulate,
|
||||
"[Kugane] Thavnairian Consulate"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.KuganeMarkets,
|
||||
"[Kugane] Kogane Dori Markets"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.KuganeBokairoInn,
|
||||
"[Kugane] Bokairo Inn"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.KuganeRubyBazaar,
|
||||
"[Kugane] The Ruby Bazaar"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.KuganeSekiseigumiBarracks,
|
||||
"[Kugane] Sekiseigumi Barracks"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.KuganeRakuzaDistrict,
|
||||
"[Kugane] Rakuza District"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.KuganeRubyPrice,
|
||||
"[Kugane] The Ruby Price"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.KuganeAirship,
|
||||
"[Kugane] Airship Landing"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.DomanEnclave,
|
||||
"[Doman Enclave] Aetheryte Plaza"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.DomanEnclaveNorthern,
|
||||
"[Doman Enclave] The Northern Enclave"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.DomanEnclaveSouthern,
|
||||
"[Doman Enclave] The Southern Enclave"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.DomanEnclaveDocks,
|
||||
"[Doman Enclave] Ferry Docks"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.DomanEnclaveOneRiver,
|
||||
"[Doman Enclave] The One River"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.DomanEnclaveGangos,
|
||||
"[Doman Enclave] Gangos"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.Crystarium,
|
||||
"[Crystarium] Aetheryte Plaza"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.CrystariumMarkets,
|
||||
"[Crystarium] Musica Universalis Markets"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.CrystariumTemenosRookery,
|
||||
"[Crystarium] Temenos Rookery"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.CrystariumDossalGate,
|
||||
"[Crystarium] The Dossal Gate"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.CrystariumPendants,
|
||||
"[Crystarium] The Pendants"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.CrystariumAmaroLaunch,
|
||||
"[Crystarium] The Amaro Launch"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.CrystariumCrystallineMean,
|
||||
"[Crystarium] The Crystalline Mean"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.CrystariumCabinetOfCuriosity,
|
||||
"[Crystarium] The Cabinet of Curiosity"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.CrystariumTessellation,
|
||||
"[Crystarium] Tessellation (Lakeland)"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.Eulmore,
|
||||
"[Eulmore] Aetheryte Plaza"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.EulmoreSoutheastDerelict,
|
||||
"[Eulmore] Southeast Derelicts"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.EulmoreNightsoilPots,
|
||||
"[Eulmore] Nightsoil Pots"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.EulmoreGloryGate,
|
||||
"[Eulmore] The Glory Gate"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.EulmoreMainstay,
|
||||
"[Eulmore] The Mainstay"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.EulmorePathToGlory,
|
||||
"[Eulmore] The Path to Glory (Kholusia)"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.OldSharlayan,
|
||||
"[Old Sharlayan] Aetheryte Plaza"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.OldSharlayanStudium,
|
||||
"[Old Sharlayan] The Studium"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.OldSharlayanBaldesionAnnex,
|
||||
"[Old Sharlayan] The Baldesion Annex"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.OldSharlayanRostra,
|
||||
"[Old Sharlayan] The Rostra"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.OldSharlayanLeveilleurEstate,
|
||||
"[Old Sharlayan] The Leveilleur Estate"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.OldSharlayanJourneysEnd,
|
||||
"[Old Sharlayan] Journey's End"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.OldSharlayanScholarsHarbor,
|
||||
"[Old Sharlayan] Scholar's Harbor"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.OldSharlayanHallOfArtifice,
|
||||
"[Old Sharlayan] The Hall of Artifice (Labyrinthos)"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.RadzAtHan,
|
||||
"[Radz-at-Han] Aetheryte Plaza"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.RadzAtHanMeghaduta,
|
||||
"[Radz-at-Han] Meghaduta"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.RadzAtHanRuveydahFibers,
|
||||
"[Radz-at-Han] Ruveydah Fibers"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.RadzAtHanAirship,
|
||||
"[Radz-at-Han] Airship Landing"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.RadzAtHanAlzadaalsPeace,
|
||||
"[Radz-at-Han] Alzadaal's Peace"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.RadzAtHanHallOfTheRadiantHost,
|
||||
"[Radz-at-Han] Hall of the Radiant Host"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.RadzAtHanMehrydesMeyhane,
|
||||
"[Radz-at-Han] Mehryde's Meyhane"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.RadzAtHanKama,
|
||||
"[Radz-at-Han] Kama"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.RadzAtHanHighCrucible,
|
||||
"[Radz-at-Han] The High Crucible of Al-Kimiya"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.RadzAtHanGateOfFirstSight,
|
||||
"[Radz-at-Han] The Gate of First Sight (Thavnair)"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.Tuliyollal,
|
||||
"[Tuliyollal] Aetheryte Plaza"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.TuliyollalDirigibleLanding,
|
||||
"[Tuliyollal] Dirigible Landing"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.TuliyollalTheResplendentQuarter,
|
||||
"[Tuliyollal] The Resplendent Quarter"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.TuliyollalTheForardCabins,
|
||||
"[Tuliyollal] The For'ard Cabins"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.TuliyollalBaysideBevyMarketplace,
|
||||
"[Tuliyollal] Bayside Bevy Marketplace"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.TuliyollalVollokShoonsa,
|
||||
"[Tuliyollal] Vollok Shoonsa"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.TuliyollalWachumeqimeqi,
|
||||
"[Tuliyollal] Wachumeqimeqi"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.TuliyollalBrightploomPost,
|
||||
"[Tuliyollal] Brightploom Post"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.TuliyollalArchOfTheDawnUrqopacha,
|
||||
"[Tuliyollal] Arch of the Dawn (Urqopacha)"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.TuliyollalArchOfTheDawnKozamauka,
|
||||
"[Tuliyollal] Arch of the Dawn (Kozama'uka)"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.TuliyollalIhuykatumu,
|
||||
"[Tuliyollal] Ihuykatumu (Kozama'uka)"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.TuliyollalDirigibleLandingYakTel,
|
||||
"[Tuliyollal] Dirigible Landing (Yak T'el)"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.TuliyollalXakTuralSkygate,
|
||||
"[Tuliyollal] Xak Tural Skygate (Shaaloani)"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.SolutionNine,
|
||||
"[Solution Nine] Aetheryte Plaza"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.SolutionNineInformationCenter,
|
||||
"[Solution Nine] Information Center"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.SolutionNineTrueVue,
|
||||
"[Solution Nine] True Vue"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.SolutionNineNeonStein,
|
||||
"[Solution Nine] Neon Stein"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.SolutionNineTheArcadion,
|
||||
"[Solution Nine] The Arcadion"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.SolutionNineResolution,
|
||||
"[Solution Nine] Resolution"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.SolutionNineNexusArcade,
|
||||
"[Solution Nine] Nexus Arcade"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.SolutionNineResidentialSector,
|
||||
"[Solution Nine] Residential Sector"
|
||||
},
|
||||
{
|
||||
EAetheryteLocation.SolutionNineScanningPortNine,
|
||||
"[Solution Nine] Scanning Port Nine (Heritage Found)"
|
||||
}
|
||||
};
|
||||
|
||||
public AethernetShardConverter()
|
||||
: base((IReadOnlyDictionary<EAetheryteLocation, string>)Values)
|
||||
{
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using Questionable.Model.Common;
|
||||
|
||||
namespace Questionable.Model.Questing.Converter;
|
||||
|
||||
public sealed class AethernetShortcutConverter : JsonConverter<AethernetShortcut>
|
||||
{
|
||||
private static readonly Dictionary<EAetheryteLocation, string> EnumToString = AethernetShardConverter.Values;
|
||||
|
||||
private static readonly Dictionary<string, EAetheryteLocation> StringToEnum = EnumToString.ToDictionary<KeyValuePair<EAetheryteLocation, string>, string, EAetheryteLocation>((KeyValuePair<EAetheryteLocation, string> x) => x.Value, (KeyValuePair<EAetheryteLocation, string> x) => x.Key);
|
||||
|
||||
public override AethernetShortcut Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
if (reader.TokenType != JsonTokenType.StartArray)
|
||||
{
|
||||
throw new JsonException();
|
||||
}
|
||||
if (!reader.Read() || reader.TokenType != JsonTokenType.String)
|
||||
{
|
||||
throw new JsonException();
|
||||
}
|
||||
string key = reader.GetString() ?? throw new JsonException();
|
||||
if (!reader.Read() || reader.TokenType != JsonTokenType.String)
|
||||
{
|
||||
throw new JsonException();
|
||||
}
|
||||
string key2 = reader.GetString() ?? throw new JsonException();
|
||||
if (!reader.Read() || reader.TokenType != JsonTokenType.EndArray)
|
||||
{
|
||||
throw new JsonException();
|
||||
}
|
||||
AethernetShortcut aethernetShortcut = new AethernetShortcut();
|
||||
if (!StringToEnum.TryGetValue(key, out var value))
|
||||
{
|
||||
throw new JsonException();
|
||||
}
|
||||
aethernetShortcut.From = value;
|
||||
if (!StringToEnum.TryGetValue(key2, out var value2))
|
||||
{
|
||||
throw new JsonException();
|
||||
}
|
||||
aethernetShortcut.To = value2;
|
||||
return aethernetShortcut;
|
||||
}
|
||||
|
||||
public override void Write(Utf8JsonWriter writer, AethernetShortcut value, JsonSerializerOptions options)
|
||||
{
|
||||
writer.WriteStartArray();
|
||||
writer.WriteStringValue(EnumToString[value.From]);
|
||||
writer.WriteStringValue(EnumToString[value.To]);
|
||||
writer.WriteEndArray();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
using System.Collections.Generic;
|
||||
using Questionable.Model.Common.Converter;
|
||||
|
||||
namespace Questionable.Model.Questing.Converter;
|
||||
|
||||
public sealed class CombatItemUseConditionConverter : EnumConverter<ECombatItemUseCondition>
|
||||
{
|
||||
private static readonly Dictionary<ECombatItemUseCondition, string> Values = new Dictionary<ECombatItemUseCondition, string>
|
||||
{
|
||||
{
|
||||
ECombatItemUseCondition.Incapacitated,
|
||||
"Incapacitated"
|
||||
},
|
||||
{
|
||||
ECombatItemUseCondition.HealthPercent,
|
||||
"Health%"
|
||||
},
|
||||
{
|
||||
ECombatItemUseCondition.MissingStatus,
|
||||
"MissingStatus"
|
||||
}
|
||||
};
|
||||
|
||||
public CombatItemUseConditionConverter()
|
||||
: base((IReadOnlyDictionary<ECombatItemUseCondition, string>)Values)
|
||||
{
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
using System.Collections.Generic;
|
||||
using Questionable.Model.Common.Converter;
|
||||
|
||||
namespace Questionable.Model.Questing.Converter;
|
||||
|
||||
public sealed class DialogueChoiceTypeConverter : EnumConverter<EDialogChoiceType>
|
||||
{
|
||||
private static readonly Dictionary<EDialogChoiceType, string> Values = new Dictionary<EDialogChoiceType, string>
|
||||
{
|
||||
{
|
||||
EDialogChoiceType.YesNo,
|
||||
"YesNo"
|
||||
},
|
||||
{
|
||||
EDialogChoiceType.List,
|
||||
"List"
|
||||
}
|
||||
};
|
||||
|
||||
public DialogueChoiceTypeConverter()
|
||||
: base((IReadOnlyDictionary<EDialogChoiceType, string>)Values)
|
||||
{
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
using System;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Questionable.Model.Questing.Converter;
|
||||
|
||||
public sealed class ElementIdConverter : JsonConverter<ElementId>
|
||||
{
|
||||
public override ElementId Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
if (reader.TokenType == JsonTokenType.Number)
|
||||
{
|
||||
return new QuestId(reader.GetUInt16());
|
||||
}
|
||||
return ElementId.FromString(reader.GetString() ?? throw new JsonException());
|
||||
}
|
||||
|
||||
public override void Write(Utf8JsonWriter writer, ElementId value, JsonSerializerOptions options)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Questionable.Model.Questing.Converter;
|
||||
|
||||
public sealed class ElementIdListConverter : JsonConverter<List<ElementId>>
|
||||
{
|
||||
public override List<ElementId> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
if (reader.TokenType != JsonTokenType.StartArray)
|
||||
{
|
||||
throw new JsonException();
|
||||
}
|
||||
reader.Read();
|
||||
List<ElementId> list = new List<ElementId>();
|
||||
while (reader.TokenType != JsonTokenType.EndArray)
|
||||
{
|
||||
if (reader.TokenType == JsonTokenType.Number)
|
||||
{
|
||||
list.Add(new QuestId(reader.GetUInt16()));
|
||||
}
|
||||
else
|
||||
{
|
||||
list.Add(ElementId.FromString(reader.GetString() ?? throw new JsonException()));
|
||||
}
|
||||
reader.Read();
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
public override void Write(Utf8JsonWriter writer, List<ElementId> value, JsonSerializerOptions options)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,48 @@
|
|||
using System.Collections.Generic;
|
||||
using Questionable.Model.Common.Converter;
|
||||
|
||||
namespace Questionable.Model.Questing.Converter;
|
||||
|
||||
public sealed class EnemySpawnTypeConverter : EnumConverter<EEnemySpawnType>
|
||||
{
|
||||
private static readonly Dictionary<EEnemySpawnType, string> Values = new Dictionary<EEnemySpawnType, string>
|
||||
{
|
||||
{
|
||||
EEnemySpawnType.AfterInteraction,
|
||||
"AfterInteraction"
|
||||
},
|
||||
{
|
||||
EEnemySpawnType.AfterItemUse,
|
||||
"AfterItemUse"
|
||||
},
|
||||
{
|
||||
EEnemySpawnType.AfterAction,
|
||||
"AfterAction"
|
||||
},
|
||||
{
|
||||
EEnemySpawnType.AfterEmote,
|
||||
"AfterEmote"
|
||||
},
|
||||
{
|
||||
EEnemySpawnType.AutoOnEnterArea,
|
||||
"AutoOnEnterArea"
|
||||
},
|
||||
{
|
||||
EEnemySpawnType.OverworldEnemies,
|
||||
"OverworldEnemies"
|
||||
},
|
||||
{
|
||||
EEnemySpawnType.FateEnemies,
|
||||
"FateEnemies"
|
||||
},
|
||||
{
|
||||
EEnemySpawnType.FinishCombatIfAny,
|
||||
"FinishCombatIfAny"
|
||||
}
|
||||
};
|
||||
|
||||
public EnemySpawnTypeConverter()
|
||||
: base((IReadOnlyDictionary<EEnemySpawnType, string>)Values)
|
||||
{
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
using System;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Questionable.Model.Questing.Converter;
|
||||
|
||||
public sealed class ExcelRefConverter : JsonConverter<ExcelRef>
|
||||
{
|
||||
public override ExcelRef? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
return reader.TokenType switch
|
||||
{
|
||||
JsonTokenType.String => ExcelRef.FromKey(reader.GetString()),
|
||||
JsonTokenType.Number => ExcelRef.FromRowId(reader.GetUInt32()),
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
|
||||
public override void Write(Utf8JsonWriter writer, ExcelRef? value, JsonSerializerOptions options)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
writer.WriteNullValue();
|
||||
return;
|
||||
}
|
||||
if (value.Type == ExcelRef.EType.Key)
|
||||
{
|
||||
writer.WriteStringValue(value.AsKey());
|
||||
return;
|
||||
}
|
||||
if (value.Type == ExcelRef.EType.RowId)
|
||||
{
|
||||
writer.WriteNumberValue(value.AsRowId());
|
||||
return;
|
||||
}
|
||||
throw new JsonException();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,212 @@
|
|||
using System.Collections.Generic;
|
||||
using Questionable.Model.Common.Converter;
|
||||
|
||||
namespace Questionable.Model.Questing.Converter;
|
||||
|
||||
internal sealed class ExtendedClassJobConverter : EnumConverter<EExtendedClassJob>
|
||||
{
|
||||
private static readonly Dictionary<EExtendedClassJob, string> Values = new Dictionary<EExtendedClassJob, string>
|
||||
{
|
||||
{
|
||||
EExtendedClassJob.None,
|
||||
"None"
|
||||
},
|
||||
{
|
||||
EExtendedClassJob.Gladiator,
|
||||
"Gladiator"
|
||||
},
|
||||
{
|
||||
EExtendedClassJob.Pugilist,
|
||||
"Pugilist"
|
||||
},
|
||||
{
|
||||
EExtendedClassJob.Marauder,
|
||||
"Marauder"
|
||||
},
|
||||
{
|
||||
EExtendedClassJob.Lancer,
|
||||
"Lancer"
|
||||
},
|
||||
{
|
||||
EExtendedClassJob.Archer,
|
||||
"Archer"
|
||||
},
|
||||
{
|
||||
EExtendedClassJob.Conjurer,
|
||||
"Conjurer"
|
||||
},
|
||||
{
|
||||
EExtendedClassJob.Thaumaturge,
|
||||
"Thaumaturge"
|
||||
},
|
||||
{
|
||||
EExtendedClassJob.Carpenter,
|
||||
"Carpenter"
|
||||
},
|
||||
{
|
||||
EExtendedClassJob.Blacksmith,
|
||||
"Blacksmith"
|
||||
},
|
||||
{
|
||||
EExtendedClassJob.Armorer,
|
||||
"Armorer"
|
||||
},
|
||||
{
|
||||
EExtendedClassJob.Goldsmith,
|
||||
"Goldsmith"
|
||||
},
|
||||
{
|
||||
EExtendedClassJob.Leatherworker,
|
||||
"Leatherworker"
|
||||
},
|
||||
{
|
||||
EExtendedClassJob.Weaver,
|
||||
"Weaver"
|
||||
},
|
||||
{
|
||||
EExtendedClassJob.Alchemist,
|
||||
"Alchemist"
|
||||
},
|
||||
{
|
||||
EExtendedClassJob.Culinarian,
|
||||
"Culinarian"
|
||||
},
|
||||
{
|
||||
EExtendedClassJob.Miner,
|
||||
"Miner"
|
||||
},
|
||||
{
|
||||
EExtendedClassJob.Botanist,
|
||||
"Botanist"
|
||||
},
|
||||
{
|
||||
EExtendedClassJob.Fisher,
|
||||
"Fisher"
|
||||
},
|
||||
{
|
||||
EExtendedClassJob.Paladin,
|
||||
"Paladin"
|
||||
},
|
||||
{
|
||||
EExtendedClassJob.Monk,
|
||||
"Monk"
|
||||
},
|
||||
{
|
||||
EExtendedClassJob.Warrior,
|
||||
"Warrior"
|
||||
},
|
||||
{
|
||||
EExtendedClassJob.Dragoon,
|
||||
"Dragoon"
|
||||
},
|
||||
{
|
||||
EExtendedClassJob.Bard,
|
||||
"Bard"
|
||||
},
|
||||
{
|
||||
EExtendedClassJob.WhiteMage,
|
||||
"White Mage"
|
||||
},
|
||||
{
|
||||
EExtendedClassJob.BlackMage,
|
||||
"Black Mage"
|
||||
},
|
||||
{
|
||||
EExtendedClassJob.Arcanist,
|
||||
"Arcanist"
|
||||
},
|
||||
{
|
||||
EExtendedClassJob.Summoner,
|
||||
"Summoner"
|
||||
},
|
||||
{
|
||||
EExtendedClassJob.Scholar,
|
||||
"Scholar"
|
||||
},
|
||||
{
|
||||
EExtendedClassJob.Rogue,
|
||||
"Rogue"
|
||||
},
|
||||
{
|
||||
EExtendedClassJob.Ninja,
|
||||
"Ninja"
|
||||
},
|
||||
{
|
||||
EExtendedClassJob.Machinist,
|
||||
"Machinist"
|
||||
},
|
||||
{
|
||||
EExtendedClassJob.DarkKnight,
|
||||
"Dark Knight"
|
||||
},
|
||||
{
|
||||
EExtendedClassJob.Astrologian,
|
||||
"Astrologian"
|
||||
},
|
||||
{
|
||||
EExtendedClassJob.Samurai,
|
||||
"Samurai"
|
||||
},
|
||||
{
|
||||
EExtendedClassJob.RedMage,
|
||||
"Red Mage"
|
||||
},
|
||||
{
|
||||
EExtendedClassJob.BlueMage,
|
||||
"Blue Mage"
|
||||
},
|
||||
{
|
||||
EExtendedClassJob.Gunbreaker,
|
||||
"Gunbreaker"
|
||||
},
|
||||
{
|
||||
EExtendedClassJob.Dancer,
|
||||
"Dancer"
|
||||
},
|
||||
{
|
||||
EExtendedClassJob.Reaper,
|
||||
"Reaper"
|
||||
},
|
||||
{
|
||||
EExtendedClassJob.Sage,
|
||||
"Sage"
|
||||
},
|
||||
{
|
||||
EExtendedClassJob.Viper,
|
||||
"Viper"
|
||||
},
|
||||
{
|
||||
EExtendedClassJob.Pictomancer,
|
||||
"Pictomancer"
|
||||
},
|
||||
{
|
||||
EExtendedClassJob.DoW,
|
||||
"DoW"
|
||||
},
|
||||
{
|
||||
EExtendedClassJob.DoM,
|
||||
"DoM"
|
||||
},
|
||||
{
|
||||
EExtendedClassJob.DoH,
|
||||
"DoH"
|
||||
},
|
||||
{
|
||||
EExtendedClassJob.DoL,
|
||||
"DoL"
|
||||
},
|
||||
{
|
||||
EExtendedClassJob.ConfiguredCombatJob,
|
||||
"ConfiguredCombatJob"
|
||||
},
|
||||
{
|
||||
EExtendedClassJob.QuestStartJob,
|
||||
"QuestStartJob"
|
||||
}
|
||||
};
|
||||
|
||||
public ExtendedClassJobConverter()
|
||||
: base((IReadOnlyDictionary<EExtendedClassJob, string>)Values)
|
||||
{
|
||||
}
|
||||
}
|
|
@ -0,0 +1,136 @@
|
|||
using System.Collections.Generic;
|
||||
using Questionable.Model.Common.Converter;
|
||||
|
||||
namespace Questionable.Model.Questing.Converter;
|
||||
|
||||
public sealed class InteractionTypeConverter : EnumConverter<EInteractionType>
|
||||
{
|
||||
private static readonly Dictionary<EInteractionType, string> Values = new Dictionary<EInteractionType, string>
|
||||
{
|
||||
{
|
||||
EInteractionType.None,
|
||||
"None"
|
||||
},
|
||||
{
|
||||
EInteractionType.Interact,
|
||||
"Interact"
|
||||
},
|
||||
{
|
||||
EInteractionType.WalkTo,
|
||||
"WalkTo"
|
||||
},
|
||||
{
|
||||
EInteractionType.AttuneAethernetShard,
|
||||
"AttuneAethernetShard"
|
||||
},
|
||||
{
|
||||
EInteractionType.AttuneAetheryte,
|
||||
"AttuneAetheryte"
|
||||
},
|
||||
{
|
||||
EInteractionType.RegisterFreeOrFavoredAetheryte,
|
||||
"RegisterFreeOrFavoredAetheryte"
|
||||
},
|
||||
{
|
||||
EInteractionType.AttuneAetherCurrent,
|
||||
"AttuneAetherCurrent"
|
||||
},
|
||||
{
|
||||
EInteractionType.Combat,
|
||||
"Combat"
|
||||
},
|
||||
{
|
||||
EInteractionType.UseItem,
|
||||
"UseItem"
|
||||
},
|
||||
{
|
||||
EInteractionType.EquipItem,
|
||||
"EquipItem"
|
||||
},
|
||||
{
|
||||
EInteractionType.PurchaseItem,
|
||||
"PurchaseItem"
|
||||
},
|
||||
{
|
||||
EInteractionType.EquipRecommended,
|
||||
"EquipRecommended"
|
||||
},
|
||||
{
|
||||
EInteractionType.Say,
|
||||
"Say"
|
||||
},
|
||||
{
|
||||
EInteractionType.Emote,
|
||||
"Emote"
|
||||
},
|
||||
{
|
||||
EInteractionType.Action,
|
||||
"Action"
|
||||
},
|
||||
{
|
||||
EInteractionType.StatusOff,
|
||||
"StatusOff"
|
||||
},
|
||||
{
|
||||
EInteractionType.WaitForObjectAtPosition,
|
||||
"WaitForNpcAtPosition"
|
||||
},
|
||||
{
|
||||
EInteractionType.WaitForManualProgress,
|
||||
"WaitForManualProgress"
|
||||
},
|
||||
{
|
||||
EInteractionType.Duty,
|
||||
"Duty"
|
||||
},
|
||||
{
|
||||
EInteractionType.SinglePlayerDuty,
|
||||
"SinglePlayerDuty"
|
||||
},
|
||||
{
|
||||
EInteractionType.Jump,
|
||||
"Jump"
|
||||
},
|
||||
{
|
||||
EInteractionType.Dive,
|
||||
"Dive"
|
||||
},
|
||||
{
|
||||
EInteractionType.Craft,
|
||||
"Craft"
|
||||
},
|
||||
{
|
||||
EInteractionType.Gather,
|
||||
"Gather"
|
||||
},
|
||||
{
|
||||
EInteractionType.Snipe,
|
||||
"Snipe"
|
||||
},
|
||||
{
|
||||
EInteractionType.SwitchClass,
|
||||
"SwitchClass"
|
||||
},
|
||||
{
|
||||
EInteractionType.UnlockTaxiStand,
|
||||
"UnlockTaxiStand"
|
||||
},
|
||||
{
|
||||
EInteractionType.Instruction,
|
||||
"Instruction"
|
||||
},
|
||||
{
|
||||
EInteractionType.AcceptQuest,
|
||||
"AcceptQuest"
|
||||
},
|
||||
{
|
||||
EInteractionType.CompleteQuest,
|
||||
"CompleteQuest"
|
||||
}
|
||||
};
|
||||
|
||||
public InteractionTypeConverter()
|
||||
: base((IReadOnlyDictionary<EInteractionType, string>)Values)
|
||||
{
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
using System.Collections.Generic;
|
||||
using Questionable.Model.Common.Converter;
|
||||
|
||||
namespace Questionable.Model.Questing.Converter;
|
||||
|
||||
public sealed class JumpTypeConverter : EnumConverter<EJumpType>
|
||||
{
|
||||
private static readonly Dictionary<EJumpType, string> Values = new Dictionary<EJumpType, string>
|
||||
{
|
||||
{
|
||||
EJumpType.SingleJump,
|
||||
"SingleJump"
|
||||
},
|
||||
{
|
||||
EJumpType.RepeatedJumps,
|
||||
"RepeatedJumps"
|
||||
}
|
||||
};
|
||||
|
||||
public JumpTypeConverter()
|
||||
: base((IReadOnlyDictionary<EJumpType, string>)Values)
|
||||
{
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
using System.Collections.Generic;
|
||||
using Questionable.Model.Common.Converter;
|
||||
|
||||
namespace Questionable.Model.Questing.Converter;
|
||||
|
||||
public sealed class LockedSkipConditionConverter : EnumConverter<ELockedSkipCondition>
|
||||
{
|
||||
private static readonly Dictionary<ELockedSkipCondition, string> Values = new Dictionary<ELockedSkipCondition, string>
|
||||
{
|
||||
{
|
||||
ELockedSkipCondition.Locked,
|
||||
"Locked"
|
||||
},
|
||||
{
|
||||
ELockedSkipCondition.Unlocked,
|
||||
"Unlocked"
|
||||
}
|
||||
};
|
||||
|
||||
public LockedSkipConditionConverter()
|
||||
: base((IReadOnlyDictionary<ELockedSkipCondition, string>)Values)
|
||||
{
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
using System;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Questionable.Model.Questing.Converter;
|
||||
|
||||
public sealed class QuestWorkConfigConverter : JsonConverter<QuestWorkValue>
|
||||
{
|
||||
public override QuestWorkValue Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
if (reader.TokenType == JsonTokenType.Number)
|
||||
{
|
||||
return new QuestWorkValue(reader.GetByte());
|
||||
}
|
||||
if (reader.TokenType != JsonTokenType.StartObject)
|
||||
{
|
||||
throw new JsonException();
|
||||
}
|
||||
byte? high = null;
|
||||
byte? low = null;
|
||||
EQuestWorkMode mode = EQuestWorkMode.Bitwise;
|
||||
while (reader.Read())
|
||||
{
|
||||
switch (reader.TokenType)
|
||||
{
|
||||
case JsonTokenType.PropertyName:
|
||||
{
|
||||
string text = reader.GetString();
|
||||
if (text == null || !reader.Read())
|
||||
{
|
||||
throw new JsonException();
|
||||
}
|
||||
switch (text)
|
||||
{
|
||||
case "High":
|
||||
high = reader.GetByte();
|
||||
break;
|
||||
case "Low":
|
||||
low = reader.GetByte();
|
||||
break;
|
||||
case "Mode":
|
||||
mode = new QuestWorkModeConverter().Read(ref reader, typeof(EQuestWorkMode), options);
|
||||
break;
|
||||
default:
|
||||
throw new JsonException();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case JsonTokenType.EndObject:
|
||||
return new QuestWorkValue(high, low, mode);
|
||||
default:
|
||||
throw new JsonException();
|
||||
}
|
||||
}
|
||||
throw new JsonException();
|
||||
}
|
||||
|
||||
public override void Write(Utf8JsonWriter writer, QuestWorkValue value, JsonSerializerOptions options)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
using System.Collections.Generic;
|
||||
using Questionable.Model.Common.Converter;
|
||||
|
||||
namespace Questionable.Model.Questing.Converter;
|
||||
|
||||
public sealed class QuestWorkModeConverter : EnumConverter<EQuestWorkMode>
|
||||
{
|
||||
private static readonly Dictionary<EQuestWorkMode, string> Values = new Dictionary<EQuestWorkMode, string>
|
||||
{
|
||||
{
|
||||
EQuestWorkMode.Bitwise,
|
||||
"Bitwise"
|
||||
},
|
||||
{
|
||||
EQuestWorkMode.Exact,
|
||||
"Exact"
|
||||
}
|
||||
};
|
||||
|
||||
public QuestWorkModeConverter()
|
||||
: base((IReadOnlyDictionary<EQuestWorkMode, string>)Values)
|
||||
{
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
using System.Collections.Generic;
|
||||
using Questionable.Model.Common.Converter;
|
||||
|
||||
namespace Questionable.Model.Questing.Converter;
|
||||
|
||||
public sealed class SkipConditionConverter : EnumConverter<EExtraSkipCondition>
|
||||
{
|
||||
private static readonly Dictionary<EExtraSkipCondition, string> Values = new Dictionary<EExtraSkipCondition, string>
|
||||
{
|
||||
{
|
||||
EExtraSkipCondition.WakingSandsMainArea,
|
||||
"WakingSandsMainArea"
|
||||
},
|
||||
{
|
||||
EExtraSkipCondition.WakingSandsSolar,
|
||||
"WakingSandsSolar"
|
||||
},
|
||||
{
|
||||
EExtraSkipCondition.RisingStonesSolar,
|
||||
"RisingStonesSolar"
|
||||
},
|
||||
{
|
||||
EExtraSkipCondition.RoguesGuild,
|
||||
"RoguesGuild"
|
||||
},
|
||||
{
|
||||
EExtraSkipCondition.NotRoguesGuild,
|
||||
"NotRoguesGuild"
|
||||
},
|
||||
{
|
||||
EExtraSkipCondition.DockStorehouse,
|
||||
"DockStorehouse"
|
||||
}
|
||||
};
|
||||
|
||||
public SkipConditionConverter()
|
||||
: base((IReadOnlyDictionary<EExtraSkipCondition, string>)Values)
|
||||
{
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
using System.Collections.Generic;
|
||||
using Questionable.Model.Common.Converter;
|
||||
|
||||
namespace Questionable.Model.Questing.Converter;
|
||||
|
||||
public sealed class StatusConverter : EnumConverter<EStatus>
|
||||
{
|
||||
private static readonly Dictionary<EStatus, string> Values = new Dictionary<EStatus, string> {
|
||||
{
|
||||
EStatus.Hidden,
|
||||
"Hidden"
|
||||
} };
|
||||
|
||||
public StatusConverter()
|
||||
: base((IReadOnlyDictionary<EStatus, string>)Values)
|
||||
{
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
using System.Globalization;
|
||||
|
||||
namespace Questionable.Model.Questing;
|
||||
|
||||
public sealed class AetherCurrentId : ElementId
|
||||
{
|
||||
public AetherCurrentId(ushort value)
|
||||
: base(value)
|
||||
{
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "C" + base.Value.ToString(CultureInfo.InvariantCulture);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
using System.Globalization;
|
||||
|
||||
namespace Questionable.Model.Questing;
|
||||
|
||||
public sealed class AethernetId : ElementId
|
||||
{
|
||||
public AethernetId(ushort value)
|
||||
: base(value)
|
||||
{
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "N" + base.Value.ToString(CultureInfo.InvariantCulture);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
namespace Questionable.Model.Questing;
|
||||
|
||||
public sealed class AlliedSocietyDailyId(byte alliedSociety, byte rank = 0) : ElementId((ushort)(alliedSociety * 10 + rank))
|
||||
{
|
||||
public byte AlliedSociety { get; } = alliedSociety;
|
||||
|
||||
public byte Rank { get; } = rank;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "A" + AlliedSociety + "x" + Rank;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
namespace Questionable.Model.Questing;
|
||||
|
||||
public sealed class ChatMessage
|
||||
{
|
||||
public string? ExcelSheet { get; set; }
|
||||
|
||||
public string Key { get; set; }
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
using System.Text.Json.Serialization;
|
||||
using Questionable.Model.Questing.Converter;
|
||||
|
||||
namespace Questionable.Model.Questing;
|
||||
|
||||
public sealed class CombatItemUse
|
||||
{
|
||||
public uint ItemId { get; set; }
|
||||
|
||||
[JsonConverter(typeof(CombatItemUseConditionConverter))]
|
||||
public ECombatItemUseCondition Condition { get; set; }
|
||||
|
||||
public int Value { get; set; }
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace Questionable.Model.Questing;
|
||||
|
||||
public sealed class ComplexCombatData
|
||||
{
|
||||
public uint DataId { get; set; }
|
||||
|
||||
public uint? NameId { get; set; }
|
||||
|
||||
public uint? MinimumKillCount { get; set; }
|
||||
|
||||
public uint? RewardItemId { get; set; }
|
||||
|
||||
public int? RewardItemCount { get; set; }
|
||||
|
||||
public IList<QuestWorkValue?> CompletionQuestVariablesFlags { get; set; } = new List<QuestWorkValue>();
|
||||
|
||||
public bool IgnoreQuestMarker { get; set; }
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
using System.Text.Json.Serialization;
|
||||
using Questionable.Model.Questing.Converter;
|
||||
|
||||
namespace Questionable.Model.Questing;
|
||||
|
||||
public sealed class DialogueChoice
|
||||
{
|
||||
[JsonConverter(typeof(DialogueChoiceTypeConverter))]
|
||||
public EDialogChoiceType Type { get; set; }
|
||||
|
||||
public string? ExcelSheet { get; set; }
|
||||
|
||||
[JsonConverter(typeof(ExcelRefConverter))]
|
||||
public ExcelRef? Prompt { get; set; }
|
||||
|
||||
public bool Yes { get; set; } = true;
|
||||
|
||||
[JsonConverter(typeof(ExcelRefConverter))]
|
||||
public ExcelRef? Answer { get; set; }
|
||||
|
||||
public bool PromptIsRegularExpression { get; set; }
|
||||
|
||||
public bool AnswerIsRegularExpression { get; set; }
|
||||
|
||||
public uint? DataId { get; set; }
|
||||
|
||||
public string? SpecialCondition { get; set; }
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace Questionable.Model.Questing;
|
||||
|
||||
public class DutyOptions
|
||||
{
|
||||
public bool Enabled { get; set; }
|
||||
|
||||
public uint ContentFinderConditionId { get; set; }
|
||||
|
||||
public bool LowPriority { get; set; }
|
||||
|
||||
public List<string> Notes { get; set; } = new List<string>();
|
||||
}
|
80
Questionable.Model/Questionable.Model.Questing/EAction.cs
Normal file
80
Questionable.Model/Questionable.Model.Questing/EAction.cs
Normal file
|
@ -0,0 +1,80 @@
|
|||
using System.Text.Json.Serialization;
|
||||
using Questionable.Model.Questing.Converter;
|
||||
|
||||
namespace Questionable.Model.Questing;
|
||||
|
||||
[JsonConverter(typeof(ActionConverter))]
|
||||
public enum EAction
|
||||
{
|
||||
DutyAction1 = 65562,
|
||||
DutyAction2 = 65563,
|
||||
HeavySwing = 31,
|
||||
Bootshine = 53,
|
||||
TwinSnakes = 61,
|
||||
Demolish = 66,
|
||||
DragonKick = 74,
|
||||
HeavyShot = 97,
|
||||
Cure = 120,
|
||||
Cure2 = 135,
|
||||
Eukrasia = 24290,
|
||||
Diagnosis = 24284,
|
||||
EukrasianDiagnosis = 24291,
|
||||
Esuna = 7568,
|
||||
Physick = 190,
|
||||
AspectedBenefic = 3595,
|
||||
FormShift = 4262,
|
||||
FieryBreath = 1764,
|
||||
BuffetSanuwa = 4931,
|
||||
BuffetGriffin = 4583,
|
||||
Trample = 4585,
|
||||
Fumigate = 5872,
|
||||
Roar = 6293,
|
||||
Seed = 6294,
|
||||
MagitekPulse = 8624,
|
||||
MagitekThunder = 8625,
|
||||
Inhale = 10013,
|
||||
SiphonSnout = 18187,
|
||||
PeculiarLight = 20030,
|
||||
Cannonfire = 20121,
|
||||
RedGulal = 29382,
|
||||
YellowGulal = 29383,
|
||||
BlueGulal = 29384,
|
||||
ElectrixFlux = 29718,
|
||||
HopStep = 31116,
|
||||
Hide = 2245,
|
||||
Ten = 2259,
|
||||
Ninjutsu = 2260,
|
||||
Chi = 2261,
|
||||
Jin = 2263,
|
||||
FumaShuriken = 2265,
|
||||
Katon = 2266,
|
||||
Raiton = 2267,
|
||||
RabbitMedium = 2272,
|
||||
SlugShot = 2868,
|
||||
BosomBrook = 37173,
|
||||
Souleater = 3632,
|
||||
Fire3 = 152,
|
||||
Adloquium = 185,
|
||||
WaterCannon = 11385,
|
||||
Wasshoi = 11499,
|
||||
ShroudedLuminescence = 39505,
|
||||
BigSneeze = 1765,
|
||||
Prospect = 227,
|
||||
CollectMiner = 240,
|
||||
LuckOfTheMountaineer = 4081,
|
||||
ScourMiner = 22182,
|
||||
MeticulousMiner = 22184,
|
||||
ScrutinyMiner = 22185,
|
||||
Triangulate = 210,
|
||||
CollectBotanist = 815,
|
||||
LuckOfThePioneer = 4095,
|
||||
ScourBotanist = 22186,
|
||||
MeticulousBotanist = 22188,
|
||||
ScrutinyBotanist = 22189,
|
||||
SharpVision1 = 235,
|
||||
SharpVision2 = 237,
|
||||
SharpVision3 = 295,
|
||||
FieldMastery1 = 218,
|
||||
FieldMastery2 = 220,
|
||||
FieldMastery3 = 294
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
namespace Questionable.Model.Questing;
|
||||
|
||||
public static class EActionExtensions
|
||||
{
|
||||
public static bool RequiresMount(this EAction action)
|
||||
{
|
||||
switch (action)
|
||||
{
|
||||
case EAction.FieryBreath:
|
||||
case EAction.BigSneeze:
|
||||
case EAction.BuffetGriffin:
|
||||
case EAction.Trample:
|
||||
case EAction.BuffetSanuwa:
|
||||
case EAction.Fumigate:
|
||||
case EAction.Roar:
|
||||
case EAction.Seed:
|
||||
case EAction.Inhale:
|
||||
case EAction.Wasshoi:
|
||||
case EAction.SiphonSnout:
|
||||
case EAction.PeculiarLight:
|
||||
case EAction.Cannonfire:
|
||||
case EAction.RedGulal:
|
||||
case EAction.YellowGulal:
|
||||
case EAction.BlueGulal:
|
||||
case EAction.ElectrixFlux:
|
||||
case EAction.HopStep:
|
||||
case EAction.BosomBrook:
|
||||
case EAction.ShroudedLuminescence:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
namespace Questionable.Model.Questing;
|
||||
|
||||
public enum ECombatItemUseCondition
|
||||
{
|
||||
None,
|
||||
Incapacitated,
|
||||
HealthPercent,
|
||||
MissingStatus
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
namespace Questionable.Model.Questing;
|
||||
|
||||
public enum EDialogChoiceType
|
||||
{
|
||||
None,
|
||||
YesNo,
|
||||
List
|
||||
}
|
275
Questionable.Model/Questionable.Model.Questing/EEmote.cs
Normal file
275
Questionable.Model/Questionable.Model.Questing/EEmote.cs
Normal file
|
@ -0,0 +1,275 @@
|
|||
using System.Text.Json.Serialization;
|
||||
using Questionable.Model.Questing.Converter;
|
||||
|
||||
namespace Questionable.Model.Questing;
|
||||
|
||||
[JsonConverter(typeof(EmoteConverter))]
|
||||
public enum EEmote
|
||||
{
|
||||
None = 0,
|
||||
Surprised = 1,
|
||||
Angry = 2,
|
||||
Furious = 3,
|
||||
Blush = 4,
|
||||
Bow = 5,
|
||||
Cheer = 6,
|
||||
Clap = 7,
|
||||
Beckon = 8,
|
||||
Comfort = 9,
|
||||
Cry = 10,
|
||||
Dance = 11,
|
||||
Doubt = 12,
|
||||
Doze = 13,
|
||||
Fume = 14,
|
||||
Goodbye = 15,
|
||||
Wave = 16,
|
||||
Huh = 17,
|
||||
Joy = 18,
|
||||
Kneel = 19,
|
||||
Chuckle = 20,
|
||||
Laugh = 21,
|
||||
Lookout = 22,
|
||||
Me = 23,
|
||||
No = 24,
|
||||
Deny = 25,
|
||||
Panic = 26,
|
||||
Point = 27,
|
||||
Poke = 28,
|
||||
Congratulate = 29,
|
||||
Psych = 30,
|
||||
Salute = 31,
|
||||
Shocked = 32,
|
||||
Shrug = 33,
|
||||
Rally = 34,
|
||||
Soothe = 35,
|
||||
Stagger = 36,
|
||||
Stretch = 37,
|
||||
Sulk = 38,
|
||||
Think = 39,
|
||||
Upset = 40,
|
||||
Welcome = 41,
|
||||
Yes = 42,
|
||||
ThumbsUp = 43,
|
||||
ExamineSelf = 44,
|
||||
Pose = 45,
|
||||
BlowKiss = 46,
|
||||
Grovel = 47,
|
||||
Happy = 48,
|
||||
Disappointed = 49,
|
||||
Lounge = 50,
|
||||
GroundSit = 52,
|
||||
AirQuotes = 54,
|
||||
GcSalute = 55,
|
||||
Pray = 58,
|
||||
ImperialSalute = 59,
|
||||
Visor = 60,
|
||||
Megaflare = 62,
|
||||
CrimsonLotus = 63,
|
||||
Charmed = 64,
|
||||
CheerOn = 65,
|
||||
CheerWave = 66,
|
||||
CheerJump = 67,
|
||||
StraightFace = 68,
|
||||
Smile = 69,
|
||||
Grin = 70,
|
||||
Smirk = 71,
|
||||
Taunt = 72,
|
||||
ShutEyes = 73,
|
||||
Sad = 74,
|
||||
Scared = 75,
|
||||
Amazed = 76,
|
||||
Ouch = 77,
|
||||
Annoyed = 78,
|
||||
Alert = 79,
|
||||
Worried = 80,
|
||||
BigGrin = 81,
|
||||
Reflect = 82,
|
||||
Furrow = 83,
|
||||
Scoff = 84,
|
||||
Throw = 85,
|
||||
ChangePose = 90,
|
||||
StepDance = 101,
|
||||
HarvestDance = 102,
|
||||
BallDance = 103,
|
||||
MandervilleDance = 104,
|
||||
Pet = 105,
|
||||
HandOver = 106,
|
||||
BombDance = 109,
|
||||
Hurray = 110,
|
||||
Slap = 111,
|
||||
Hug = 112,
|
||||
Embrace = 113,
|
||||
Hildibrand = 114,
|
||||
FistBump = 115,
|
||||
ThavDance = 118,
|
||||
GoldDance = 119,
|
||||
SundropDance = 120,
|
||||
BattleStance = 121,
|
||||
VictoryPose = 122,
|
||||
Backflip = 123,
|
||||
EasternGreeting = 124,
|
||||
Eureka = 125,
|
||||
MogDance = 126,
|
||||
Haurchefant = 127,
|
||||
EasternStretch = 128,
|
||||
EasternDance = 129,
|
||||
RangerPose1R = 130,
|
||||
RangerPose2R = 131,
|
||||
RangerPose3R = 132,
|
||||
Wink = 133,
|
||||
RangerPose1L = 134,
|
||||
RangerPose2L = 135,
|
||||
RangerPose3L = 136,
|
||||
Facepalm = 137,
|
||||
Zantetsuken = 138,
|
||||
Flex = 139,
|
||||
Respect = 140,
|
||||
Sneer = 141,
|
||||
PrettyPlease = 142,
|
||||
PlayDead = 143,
|
||||
IceHeart = 144,
|
||||
MoonLift = 145,
|
||||
Dote = 146,
|
||||
Spectacles = 148,
|
||||
Songbird = 149,
|
||||
WaterFloat = 150,
|
||||
WaterFlip = 151,
|
||||
PuckerUp = 152,
|
||||
PowerUp = 153,
|
||||
EasternBow = 154,
|
||||
Squats = 155,
|
||||
PushUps = 156,
|
||||
SitUps = 157,
|
||||
BreathControl = 158,
|
||||
Converse = 159,
|
||||
Concentrate = 160,
|
||||
Disturbed = 161,
|
||||
Simper = 162,
|
||||
Beam = 163,
|
||||
Attention = 164,
|
||||
AtEase = 165,
|
||||
Box = 166,
|
||||
RitualPrayer = 167,
|
||||
Tremble = 169,
|
||||
Winded = 170,
|
||||
Aback = 171,
|
||||
Greeting = 172,
|
||||
BoxStep = 173,
|
||||
SideStep = 174,
|
||||
Ultima = 175,
|
||||
YolDance = 176,
|
||||
Splash = 178,
|
||||
Sweat = 180,
|
||||
Shiver = 181,
|
||||
Elucidate = 182,
|
||||
Ponder = 183,
|
||||
LeftWink = 184,
|
||||
GetFantasy = 185,
|
||||
PopotoStep = 186,
|
||||
Hum = 187,
|
||||
Confirm = 188,
|
||||
Scheme = 189,
|
||||
Endure = 190,
|
||||
Tomestone = 191,
|
||||
HeelToe = 192,
|
||||
GoobbueDouble = 193,
|
||||
Gratuity = 194,
|
||||
FistPump = 195,
|
||||
Reprimand = 196,
|
||||
Sabotender = 197,
|
||||
MandervilleMambo = 198,
|
||||
LaliHo = 199,
|
||||
SimulationM = 200,
|
||||
SimulationF = 201,
|
||||
Toast = 202,
|
||||
Lean = 203,
|
||||
Headache = 204,
|
||||
Snap = 205,
|
||||
BreakFast = 206,
|
||||
Read = 207,
|
||||
Insist = 208,
|
||||
Consider = 209,
|
||||
Wasshoi = 210,
|
||||
FlowerShower = 211,
|
||||
FlameDance = 212,
|
||||
HighFive = 213,
|
||||
Guard = 214,
|
||||
Malevolence = 215,
|
||||
BeesKnees = 216,
|
||||
LaliHop = 217,
|
||||
EatRiceBall = 220,
|
||||
EatApple = 221,
|
||||
WringHands = 222,
|
||||
Sweep = 223,
|
||||
PaintBlack = 224,
|
||||
PaintRed = 225,
|
||||
PaintYellow = 226,
|
||||
PaintBlue = 227,
|
||||
FakeSmile = 228,
|
||||
Pantomime = 229,
|
||||
Vexed = 230,
|
||||
Shush = 231,
|
||||
EatPizza = 232,
|
||||
ClutchHead = 233,
|
||||
EatChocolate = 234,
|
||||
EatEgg = 235,
|
||||
Content = 236,
|
||||
Sheathe = 237,
|
||||
Draw = 238,
|
||||
Tea = 239,
|
||||
Determined = 240,
|
||||
ShowRight = 241,
|
||||
ShowLeft = 242,
|
||||
Deride = 245,
|
||||
Wow = 246,
|
||||
EatPumpkinCookie = 247,
|
||||
Spirit = 248,
|
||||
MagicTrick = 249,
|
||||
LittleLadiesDance = 250,
|
||||
Linkpearl = 251,
|
||||
EarWiggle = 252,
|
||||
Frighten = 256,
|
||||
AdventOfLight = 257,
|
||||
JumpForJoy1 = 258,
|
||||
JumpForJoy2 = 259,
|
||||
JumpForJoy3 = 260,
|
||||
JumpForJoy4 = 261,
|
||||
JumpForJoy5 = 262,
|
||||
HandToHeart = 263,
|
||||
CheerOnBright = 264,
|
||||
CheerWaveViolet = 265,
|
||||
CheerJumpGreen = 266,
|
||||
AllSaintsCharm = 267,
|
||||
LopHop = 269,
|
||||
Reference = 270,
|
||||
EatChicken = 271,
|
||||
Sundering = 272,
|
||||
Slump = 273,
|
||||
LoveHeart = 274,
|
||||
HumbleTriumph = 275,
|
||||
VictoryReveal = 276,
|
||||
FryEgg = 277,
|
||||
Uchiwasshoi = 278,
|
||||
Attend = 279,
|
||||
Water = 280,
|
||||
ShakeDrink = 281,
|
||||
Unbound = 282,
|
||||
Bouquet = 283,
|
||||
BlowBubbles = 284,
|
||||
Ohokaliy = 285,
|
||||
Visage = 286,
|
||||
Photograph = 288,
|
||||
Overreact = 291,
|
||||
Twirl = 292,
|
||||
Dazed = 293,
|
||||
Rage = 294,
|
||||
TomeScroll = 295,
|
||||
Study = 296,
|
||||
GridanianSip = 298,
|
||||
UldahnSip = 299,
|
||||
LominsanSip = 300,
|
||||
GridanianGulp = 301,
|
||||
UldahnGulp = 302,
|
||||
LominsanGulp = 303,
|
||||
Pen = 307
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
using System.Text.Json.Serialization;
|
||||
using Questionable.Model.Questing.Converter;
|
||||
|
||||
namespace Questionable.Model.Questing;
|
||||
|
||||
[JsonConverter(typeof(EnemySpawnTypeConverter))]
|
||||
public enum EEnemySpawnType
|
||||
{
|
||||
None,
|
||||
AfterInteraction,
|
||||
AfterItemUse,
|
||||
AfterAction,
|
||||
AfterEmote,
|
||||
AutoOnEnterArea,
|
||||
OverworldEnemies,
|
||||
FateEnemies,
|
||||
FinishCombatIfAny,
|
||||
QuestInterruption
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
using System.Text.Json.Serialization;
|
||||
using Questionable.Model.Questing.Converter;
|
||||
|
||||
namespace Questionable.Model.Questing;
|
||||
|
||||
[JsonConverter(typeof(ExtendedClassJobConverter))]
|
||||
public enum EExtendedClassJob
|
||||
{
|
||||
None,
|
||||
Gladiator,
|
||||
Pugilist,
|
||||
Marauder,
|
||||
Lancer,
|
||||
Archer,
|
||||
Conjurer,
|
||||
Thaumaturge,
|
||||
Carpenter,
|
||||
Blacksmith,
|
||||
Armorer,
|
||||
Goldsmith,
|
||||
Leatherworker,
|
||||
Weaver,
|
||||
Alchemist,
|
||||
Culinarian,
|
||||
Miner,
|
||||
Botanist,
|
||||
Fisher,
|
||||
Paladin,
|
||||
Monk,
|
||||
Warrior,
|
||||
Dragoon,
|
||||
Bard,
|
||||
WhiteMage,
|
||||
BlackMage,
|
||||
Arcanist,
|
||||
Summoner,
|
||||
Scholar,
|
||||
Rogue,
|
||||
Ninja,
|
||||
Machinist,
|
||||
DarkKnight,
|
||||
Astrologian,
|
||||
Samurai,
|
||||
RedMage,
|
||||
BlueMage,
|
||||
Gunbreaker,
|
||||
Dancer,
|
||||
Reaper,
|
||||
Sage,
|
||||
Viper,
|
||||
Pictomancer,
|
||||
DoW,
|
||||
DoM,
|
||||
DoH,
|
||||
DoL,
|
||||
ConfiguredCombatJob,
|
||||
QuestStartJob
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
using System.Text.Json.Serialization;
|
||||
using Questionable.Model.Questing.Converter;
|
||||
|
||||
namespace Questionable.Model.Questing;
|
||||
|
||||
[JsonConverter(typeof(SkipConditionConverter))]
|
||||
public enum EExtraSkipCondition
|
||||
{
|
||||
None,
|
||||
WakingSandsMainArea,
|
||||
WakingSandsSolar,
|
||||
RisingStonesSolar,
|
||||
RoguesGuild,
|
||||
NotRoguesGuild,
|
||||
DockStorehouse
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
using System.Text.Json.Serialization;
|
||||
using Questionable.Model.Questing.Converter;
|
||||
|
||||
namespace Questionable.Model.Questing;
|
||||
|
||||
[JsonConverter(typeof(InteractionTypeConverter))]
|
||||
public enum EInteractionType
|
||||
{
|
||||
None,
|
||||
Interact,
|
||||
WalkTo,
|
||||
AttuneAethernetShard,
|
||||
AttuneAetheryte,
|
||||
RegisterFreeOrFavoredAetheryte,
|
||||
AttuneAetherCurrent,
|
||||
Combat,
|
||||
UseItem,
|
||||
EquipItem,
|
||||
PurchaseItem,
|
||||
EquipRecommended,
|
||||
Say,
|
||||
Emote,
|
||||
Action,
|
||||
StatusOff,
|
||||
WaitForObjectAtPosition,
|
||||
WaitForManualProgress,
|
||||
Duty,
|
||||
SinglePlayerDuty,
|
||||
Jump,
|
||||
Dive,
|
||||
Craft,
|
||||
Gather,
|
||||
Snipe,
|
||||
SwitchClass,
|
||||
UnlockTaxiStand,
|
||||
Instruction,
|
||||
AcceptQuest,
|
||||
CompleteQuest
|
||||
}
|
11
Questionable.Model/Questionable.Model.Questing/EJumpType.cs
Normal file
11
Questionable.Model/Questionable.Model.Questing/EJumpType.cs
Normal file
|
@ -0,0 +1,11 @@
|
|||
using System.Text.Json.Serialization;
|
||||
using Questionable.Model.Questing.Converter;
|
||||
|
||||
namespace Questionable.Model.Questing;
|
||||
|
||||
[JsonConverter(typeof(JumpTypeConverter))]
|
||||
public enum EJumpType
|
||||
{
|
||||
SingleJump,
|
||||
RepeatedJumps
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
using System.Text.Json.Serialization;
|
||||
using Questionable.Model.Questing.Converter;
|
||||
|
||||
namespace Questionable.Model.Questing;
|
||||
|
||||
[JsonConverter(typeof(LockedSkipConditionConverter))]
|
||||
public enum ELockedSkipCondition
|
||||
{
|
||||
Locked,
|
||||
Unlocked
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
using System.Text.Json.Serialization;
|
||||
using Questionable.Model.Questing.Converter;
|
||||
|
||||
namespace Questionable.Model.Questing;
|
||||
|
||||
[JsonConverter(typeof(QuestWorkModeConverter))]
|
||||
public enum EQuestWorkMode
|
||||
{
|
||||
Bitwise,
|
||||
Exact
|
||||
}
|
15
Questionable.Model/Questionable.Model.Questing/EStatus.cs
Normal file
15
Questionable.Model/Questionable.Model.Questing/EStatus.cs
Normal file
|
@ -0,0 +1,15 @@
|
|||
using System.Text.Json.Serialization;
|
||||
using Questionable.Model.Questing.Converter;
|
||||
|
||||
namespace Questionable.Model.Questing;
|
||||
|
||||
[JsonConverter(typeof(StatusConverter))]
|
||||
public enum EStatus : uint
|
||||
{
|
||||
Triangulate = 217u,
|
||||
GatheringRateUp = 218u,
|
||||
Prospect = 225u,
|
||||
Hidden = 614u,
|
||||
Eukrasia = 2606u,
|
||||
Jog = 4209u
|
||||
}
|
123
Questionable.Model/Questionable.Model.Questing/ElementId.cs
Normal file
123
Questionable.Model/Questionable.Model.Questing/ElementId.cs
Normal file
|
@ -0,0 +1,123 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
|
||||
namespace Questionable.Model.Questing;
|
||||
|
||||
public abstract class ElementId : IComparable<ElementId>, IEquatable<ElementId>
|
||||
{
|
||||
public ushort Value { get; }
|
||||
|
||||
protected ElementId(ushort value)
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public int CompareTo(ElementId? other)
|
||||
{
|
||||
if ((object)this == other)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if ((object)other == null)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
return Value.CompareTo(other.Value);
|
||||
}
|
||||
|
||||
public bool Equals(ElementId? other)
|
||||
{
|
||||
if ((object)other == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if ((object)this == other)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (other.GetType() != GetType())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return Value == other.Value;
|
||||
}
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
if (obj == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (this == obj)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (obj.GetType() != GetType())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return Equals((ElementId)obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Value.GetHashCode();
|
||||
}
|
||||
|
||||
public static bool operator ==(ElementId? left, ElementId? right)
|
||||
{
|
||||
return object.Equals(left, right);
|
||||
}
|
||||
|
||||
public static bool operator !=(ElementId? left, ElementId? right)
|
||||
{
|
||||
return !object.Equals(left, right);
|
||||
}
|
||||
|
||||
public static ElementId FromString(string value)
|
||||
{
|
||||
if (value.StartsWith("S"))
|
||||
{
|
||||
return new SatisfactionSupplyNpcId(ushort.Parse(value.Substring(1), CultureInfo.InvariantCulture));
|
||||
}
|
||||
if (value.StartsWith("U"))
|
||||
{
|
||||
return new UnlockLinkId(ushort.Parse(value.Substring(1), CultureInfo.InvariantCulture));
|
||||
}
|
||||
if (value.StartsWith("N"))
|
||||
{
|
||||
return new AethernetId(ushort.Parse(value.Substring(1), CultureInfo.InvariantCulture));
|
||||
}
|
||||
if (value.StartsWith("C"))
|
||||
{
|
||||
return new AetherCurrentId(ushort.Parse(value.Substring(1), CultureInfo.InvariantCulture));
|
||||
}
|
||||
if (value.StartsWith("A"))
|
||||
{
|
||||
value = value.Substring(1);
|
||||
string[] array = value.Split(new char[1] { 'x' });
|
||||
if (array.Length == 2)
|
||||
{
|
||||
return new AlliedSocietyDailyId(byte.Parse(array[0], CultureInfo.InvariantCulture), byte.Parse(array[1], CultureInfo.InvariantCulture));
|
||||
}
|
||||
return new AlliedSocietyDailyId(byte.Parse(value, CultureInfo.InvariantCulture), 0);
|
||||
}
|
||||
return new QuestId(ushort.Parse(value, CultureInfo.InvariantCulture));
|
||||
}
|
||||
|
||||
public static bool TryFromString(string value, out ElementId? elementId)
|
||||
{
|
||||
try
|
||||
{
|
||||
elementId = FromString(value);
|
||||
return true;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
elementId = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract override string ToString();
|
||||
}
|
83
Questionable.Model/Questionable.Model.Questing/ExcelRef.cs
Normal file
83
Questionable.Model/Questionable.Model.Questing/ExcelRef.cs
Normal file
|
@ -0,0 +1,83 @@
|
|||
using System;
|
||||
|
||||
namespace Questionable.Model.Questing;
|
||||
|
||||
public class ExcelRef
|
||||
{
|
||||
public enum EType
|
||||
{
|
||||
None,
|
||||
Key,
|
||||
RowId,
|
||||
RawString
|
||||
}
|
||||
|
||||
private readonly string? _stringValue;
|
||||
|
||||
private readonly uint? _rowIdValue;
|
||||
|
||||
public EType Type { get; }
|
||||
|
||||
public ExcelRef(string value)
|
||||
{
|
||||
_stringValue = value;
|
||||
_rowIdValue = null;
|
||||
Type = EType.Key;
|
||||
}
|
||||
|
||||
public ExcelRef(uint value)
|
||||
{
|
||||
_stringValue = null;
|
||||
_rowIdValue = value;
|
||||
Type = EType.RowId;
|
||||
}
|
||||
|
||||
private ExcelRef(string? stringValue, uint? rowIdValue, EType type)
|
||||
{
|
||||
_stringValue = stringValue;
|
||||
_rowIdValue = rowIdValue;
|
||||
Type = type;
|
||||
}
|
||||
|
||||
public static ExcelRef FromKey(string value)
|
||||
{
|
||||
return new ExcelRef(value, null, EType.Key);
|
||||
}
|
||||
|
||||
public static ExcelRef FromRowId(uint rowId)
|
||||
{
|
||||
return new ExcelRef(null, rowId, EType.RowId);
|
||||
}
|
||||
|
||||
public static ExcelRef FromSheetValue(string value)
|
||||
{
|
||||
return new ExcelRef(value, null, EType.RawString);
|
||||
}
|
||||
|
||||
public string AsKey()
|
||||
{
|
||||
if (Type != EType.Key)
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
return _stringValue;
|
||||
}
|
||||
|
||||
public uint AsRowId()
|
||||
{
|
||||
if (Type != EType.RowId)
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
return _rowIdValue.Value;
|
||||
}
|
||||
|
||||
public string AsRawString()
|
||||
{
|
||||
if (Type != EType.RawString)
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
return _stringValue;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
namespace Questionable.Model.Questing;
|
||||
|
||||
public sealed class GatheredItem
|
||||
{
|
||||
public uint ItemId { get; set; }
|
||||
|
||||
public uint AlternativeItemId { get; set; }
|
||||
|
||||
public int ItemCount { get; set; }
|
||||
|
||||
public ushort Collectability { get; set; }
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
using System.Numerics;
|
||||
using System.Text.Json.Serialization;
|
||||
using Questionable.Model.Common.Converter;
|
||||
|
||||
namespace Questionable.Model.Questing;
|
||||
|
||||
public sealed class JumpDestination
|
||||
{
|
||||
[JsonConverter(typeof(VectorConverter))]
|
||||
public Vector3 Position { get; set; }
|
||||
|
||||
public float? StopDistance { get; set; }
|
||||
|
||||
public float? DelaySeconds { get; set; }
|
||||
|
||||
public EJumpType Type { get; set; }
|
||||
|
||||
public float CalculateStopDistance()
|
||||
{
|
||||
return StopDistance ?? 1f;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
using System.Numerics;
|
||||
using System.Text.Json.Serialization;
|
||||
using Questionable.Model.Common.Converter;
|
||||
|
||||
namespace Questionable.Model.Questing;
|
||||
|
||||
public sealed class NearPositionCondition
|
||||
{
|
||||
[JsonConverter(typeof(VectorConverter))]
|
||||
public Vector3 Position { get; set; }
|
||||
|
||||
public float MaximumDistance { get; set; }
|
||||
|
||||
public ushort TerritoryId { get; set; }
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
using System.Text.Json.Serialization;
|
||||
using Questionable.Model.Questing.Converter;
|
||||
|
||||
namespace Questionable.Model.Questing;
|
||||
|
||||
public sealed class PurchaseMenu
|
||||
{
|
||||
public string? ExcelSheet { get; set; }
|
||||
|
||||
[JsonConverter(typeof(ExcelRefConverter))]
|
||||
public ExcelRef? Key { get; set; }
|
||||
}
|
21
Questionable.Model/Questionable.Model.Questing/QuestId.cs
Normal file
21
Questionable.Model/Questionable.Model.Questing/QuestId.cs
Normal file
|
@ -0,0 +1,21 @@
|
|||
using System.Globalization;
|
||||
|
||||
namespace Questionable.Model.Questing;
|
||||
|
||||
public sealed class QuestId : ElementId
|
||||
{
|
||||
public QuestId(ushort value)
|
||||
: base(value)
|
||||
{
|
||||
}
|
||||
|
||||
public static QuestId FromRowId(uint rowId)
|
||||
{
|
||||
return new QuestId((ushort)(rowId & 0xFFFF));
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return base.Value.ToString(CultureInfo.InvariantCulture);
|
||||
}
|
||||
}
|
19
Questionable.Model/Questionable.Model.Questing/QuestRoot.cs
Normal file
19
Questionable.Model/Questionable.Model.Questing/QuestRoot.cs
Normal file
|
@ -0,0 +1,19 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Text.Json.Serialization;
|
||||
using Questionable.Model.Common.Converter;
|
||||
|
||||
namespace Questionable.Model.Questing;
|
||||
|
||||
public sealed class QuestRoot
|
||||
{
|
||||
[JsonConverter(typeof(StringListOrValueConverter))]
|
||||
public List<string> Author { get; set; } = new List<string>();
|
||||
|
||||
public bool Disabled { get; set; }
|
||||
|
||||
public bool Interruptible { get; set; } = true;
|
||||
|
||||
public string? Comment { get; set; }
|
||||
|
||||
public List<QuestSequence> QuestSequence { get; set; } = new List<QuestSequence>();
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Questionable.Model.Questing;
|
||||
|
||||
public sealed class QuestSequence
|
||||
{
|
||||
public byte Sequence { get; set; }
|
||||
|
||||
public string? Comment { get; set; }
|
||||
|
||||
public List<QuestStep> Steps { get; set; } = new List<QuestStep>();
|
||||
|
||||
public QuestStep? FindStep(int step)
|
||||
{
|
||||
if (step < 0 || step >= Steps.Count)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return Steps[step];
|
||||
}
|
||||
|
||||
public QuestStep? LastStep()
|
||||
{
|
||||
return Steps.LastOrDefault();
|
||||
}
|
||||
}
|
172
Questionable.Model/Questionable.Model.Questing/QuestStep.cs
Normal file
172
Questionable.Model/Questionable.Model.Questing/QuestStep.cs
Normal file
|
@ -0,0 +1,172 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using System.Text.Json.Serialization;
|
||||
using Questionable.Model.Common;
|
||||
using Questionable.Model.Common.Converter;
|
||||
using Questionable.Model.Questing.Converter;
|
||||
|
||||
namespace Questionable.Model.Questing;
|
||||
|
||||
public sealed class QuestStep
|
||||
{
|
||||
public const float DefaultStopDistance = 3f;
|
||||
|
||||
public const int VesperBayAetheryteTicket = 30362;
|
||||
|
||||
public uint? DataId { get; set; }
|
||||
|
||||
[JsonConverter(typeof(VectorConverter))]
|
||||
public Vector3? Position { get; set; }
|
||||
|
||||
public float? StopDistance { get; set; }
|
||||
|
||||
public ushort TerritoryId { get; set; }
|
||||
|
||||
[JsonIgnore(/*Could not decode attribute arguments.*/)]
|
||||
public EInteractionType InteractionType { get; set; }
|
||||
|
||||
public float? NpcWaitDistance { get; set; }
|
||||
|
||||
public ushort? TargetTerritoryId { get; set; }
|
||||
|
||||
public float? DelaySecondsAtStart { get; set; }
|
||||
|
||||
public uint? PickUpItemId { get; set; }
|
||||
|
||||
public bool Disabled { get; set; }
|
||||
|
||||
public bool DisableNavmesh { get; set; }
|
||||
|
||||
public bool? Mount { get; set; }
|
||||
|
||||
public bool? Fly { get; set; }
|
||||
|
||||
public bool? Land { get; set; }
|
||||
|
||||
public bool? Sprint { get; set; }
|
||||
|
||||
public bool? IgnoreDistanceToObject { get; set; }
|
||||
|
||||
public bool? RestartNavigationIfCancelled { get; set; }
|
||||
|
||||
public string? Comment { get; set; }
|
||||
|
||||
public EAetheryteLocation? Aetheryte { get; set; }
|
||||
|
||||
[JsonConverter(typeof(AethernetShardConverter))]
|
||||
public EAetheryteLocation? AethernetShard { get; set; }
|
||||
|
||||
public EAetheryteLocation? AetheryteShortcut { get; set; }
|
||||
|
||||
public AethernetShortcut? AethernetShortcut { get; set; }
|
||||
|
||||
public uint? AetherCurrentId { get; set; }
|
||||
|
||||
public uint? ItemId { get; set; }
|
||||
|
||||
public bool? GroundTarget { get; set; }
|
||||
|
||||
public int? ItemCount { get; set; }
|
||||
|
||||
public EEmote? Emote { get; set; }
|
||||
|
||||
public ChatMessage? ChatMessage { get; set; }
|
||||
|
||||
public EAction? Action { get; set; }
|
||||
|
||||
public EStatus? Status { get; set; }
|
||||
|
||||
public EExtendedClassJob TargetClass { get; set; }
|
||||
|
||||
public byte? TaxiStandId { get; set; }
|
||||
|
||||
public EEnemySpawnType? EnemySpawnType { get; set; }
|
||||
|
||||
public List<uint> KillEnemyDataIds { get; set; } = new List<uint>();
|
||||
|
||||
public List<ComplexCombatData> ComplexCombatData { get; set; } = new List<ComplexCombatData>();
|
||||
|
||||
public CombatItemUse? CombatItemUse { get; set; }
|
||||
|
||||
public float? CombatDelaySecondsAtStart { get; set; }
|
||||
|
||||
public JumpDestination? JumpDestination { get; set; }
|
||||
|
||||
public DutyOptions? DutyOptions { get; set; }
|
||||
|
||||
public SinglePlayerDutyOptions? SinglePlayerDutyOptions { get; set; }
|
||||
|
||||
public byte SinglePlayerDutyIndex => SinglePlayerDutyOptions?.Index ?? 0;
|
||||
|
||||
public SkipConditions? SkipConditions { get; set; }
|
||||
|
||||
public List<List<QuestWorkValue>?> RequiredQuestVariables { get; set; } = new List<List<QuestWorkValue>>();
|
||||
|
||||
public List<EExtendedClassJob> RequiredCurrentJob { get; set; } = new List<EExtendedClassJob>();
|
||||
|
||||
public List<EExtendedClassJob> RequiredQuestAcceptedJob { get; set; } = new List<EExtendedClassJob>();
|
||||
|
||||
public List<GatheredItem> ItemsToGather { get; set; } = new List<GatheredItem>();
|
||||
|
||||
public List<QuestWorkValue?> CompletionQuestVariablesFlags { get; set; } = new List<QuestWorkValue>();
|
||||
|
||||
public List<DialogueChoice> DialogueChoices { get; set; } = new List<DialogueChoice>();
|
||||
|
||||
public List<uint> PointMenuChoices { get; set; } = new List<uint>();
|
||||
|
||||
public PurchaseMenu? PurchaseMenu { get; set; }
|
||||
|
||||
[JsonConverter(typeof(ElementIdConverter))]
|
||||
public ElementId? PickUpQuestId { get; set; }
|
||||
|
||||
[JsonConverter(typeof(ElementIdConverter))]
|
||||
public ElementId? TurnInQuestId { get; set; }
|
||||
|
||||
[JsonConverter(typeof(ElementIdConverter))]
|
||||
public ElementId? NextQuestId { get; set; }
|
||||
|
||||
[JsonConstructor]
|
||||
public QuestStep()
|
||||
{
|
||||
}
|
||||
|
||||
public QuestStep(EInteractionType interactionType, uint? dataId, Vector3? position, ushort territoryId)
|
||||
{
|
||||
InteractionType = interactionType;
|
||||
DataId = dataId;
|
||||
Position = position;
|
||||
TerritoryId = territoryId;
|
||||
}
|
||||
|
||||
public float CalculateActualStopDistance()
|
||||
{
|
||||
float? stopDistance = StopDistance;
|
||||
if (stopDistance.HasValue)
|
||||
{
|
||||
return stopDistance.GetValueOrDefault();
|
||||
}
|
||||
switch (InteractionType)
|
||||
{
|
||||
case EInteractionType.WalkTo:
|
||||
return 0.25f;
|
||||
case EInteractionType.AttuneAetheryte:
|
||||
case EInteractionType.RegisterFreeOrFavoredAetheryte:
|
||||
return 10f;
|
||||
default:
|
||||
return 3f;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsTeleportableForPriorityQuests()
|
||||
{
|
||||
if (AetheryteShortcut.HasValue)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (InteractionType == EInteractionType.UseItem && ItemId == 30362)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
using System.Text.Json.Serialization;
|
||||
using Questionable.Model.Questing.Converter;
|
||||
|
||||
namespace Questionable.Model.Questing;
|
||||
|
||||
[JsonConverter(typeof(QuestWorkConfigConverter))]
|
||||
public sealed class QuestWorkValue(byte? high, byte? low, EQuestWorkMode mode)
|
||||
{
|
||||
public byte? High { get; set; } = high;
|
||||
|
||||
public byte? Low { get; set; } = low;
|
||||
|
||||
public EQuestWorkMode Mode { get; set; } = mode;
|
||||
|
||||
public QuestWorkValue(byte value)
|
||||
: this((byte)(value >> 4), (byte)(value & 0xF), EQuestWorkMode.Bitwise)
|
||||
{
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
if (High.HasValue && Low.HasValue)
|
||||
{
|
||||
return ((byte)(High << 4).Value + Low).ToString();
|
||||
}
|
||||
if (High.HasValue)
|
||||
{
|
||||
return High + "H";
|
||||
}
|
||||
if (Low.HasValue)
|
||||
{
|
||||
return Low + "L";
|
||||
}
|
||||
return "-";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
using System.Globalization;
|
||||
|
||||
namespace Questionable.Model.Questing;
|
||||
|
||||
public sealed class SatisfactionSupplyNpcId : ElementId
|
||||
{
|
||||
public SatisfactionSupplyNpcId(ushort value)
|
||||
: base(value)
|
||||
{
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "S" + base.Value.ToString(CultureInfo.InvariantCulture);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace Questionable.Model.Questing;
|
||||
|
||||
public sealed class SinglePlayerDutyOptions
|
||||
{
|
||||
public bool Enabled { get; set; }
|
||||
|
||||
public List<string> Notes { get; set; } = new List<string>();
|
||||
|
||||
public byte Index { get; set; }
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Text.Json.Serialization;
|
||||
using Questionable.Model.Common;
|
||||
using Questionable.Model.Questing.Converter;
|
||||
|
||||
namespace Questionable.Model.Questing;
|
||||
|
||||
public sealed class SkipAetheryteCondition
|
||||
{
|
||||
public bool Never { get; set; }
|
||||
|
||||
public bool InSameTerritory { get; set; }
|
||||
|
||||
public List<ushort> InTerritory { get; set; } = new List<ushort>();
|
||||
|
||||
[JsonConverter(typeof(ElementIdListConverter))]
|
||||
public List<ElementId> QuestsAccepted { get; set; } = new List<ElementId>();
|
||||
|
||||
[JsonConverter(typeof(ElementIdListConverter))]
|
||||
public List<ElementId> QuestsCompleted { get; set; } = new List<ElementId>();
|
||||
|
||||
public EAetheryteLocation? AetheryteLocked { get; set; }
|
||||
|
||||
public EAetheryteLocation? AetheryteUnlocked { get; set; }
|
||||
|
||||
public bool RequiredQuestVariablesNotMet { get; set; }
|
||||
|
||||
public NearPositionCondition? NearPosition { get; set; }
|
||||
|
||||
public NearPositionCondition? NotNearPosition { get; set; }
|
||||
|
||||
public EExtraSkipCondition? ExtraCondition { get; set; }
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
namespace Questionable.Model.Questing;
|
||||
|
||||
public sealed class SkipConditions
|
||||
{
|
||||
public SkipStepConditions? StepIf { get; set; }
|
||||
|
||||
public SkipAetheryteCondition? AetheryteShortcutIf { get; set; }
|
||||
|
||||
public SkipAetheryteCondition? AethernetShortcutIf { get; set; }
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue