punish v6.8.18.0
This commit is contained in:
commit
cfb4dea47e
316 changed files with 554088 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