Переход на Newtonsoft.Json
This commit is contained in:
@@ -11,4 +11,10 @@
|
|||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Update="test.json">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -1,3 +1,17 @@
|
|||||||
// See https://aka.ms/new-console-template for more information
|
using Newtonsoft.Json.Linq;
|
||||||
|
using Pf2eDataScrapper.SchemaBuilder;
|
||||||
|
|
||||||
Console.WriteLine("Hello, World!");
|
Console.WriteLine("Hello, World!");
|
||||||
|
|
||||||
|
JObject data;
|
||||||
|
|
||||||
|
using (var reader = new StreamReader("test.json"))
|
||||||
|
{
|
||||||
|
data = JObject.Parse(reader.ReadToEnd());
|
||||||
|
}
|
||||||
|
|
||||||
|
AbstractSchemaType schema = new ObjectSchemaType();
|
||||||
|
schema = schema.Adapt(data);
|
||||||
|
schema.AddValue(data);
|
||||||
|
|
||||||
|
Console.WriteLine(schema);
|
||||||
|
|||||||
@@ -1,10 +1,4 @@
|
|||||||
using System;
|
using Newtonsoft.Json.Linq;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Pf2eDataScrapper.SchemaBuilder
|
namespace Pf2eDataScrapper.SchemaBuilder
|
||||||
{
|
{
|
||||||
@@ -30,7 +24,7 @@ namespace Pf2eDataScrapper.SchemaBuilder
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="value">Value to be checked</param>
|
/// <param name="value">Value to be checked</param>
|
||||||
/// <returns>True if value is compatible with current type</returns>
|
/// <returns>True if value is compatible with current type</returns>
|
||||||
public abstract bool IsCompatibleValue(object value);
|
public abstract bool IsCompatibleValue(JToken? value);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adapt to new value.
|
/// Adapt to new value.
|
||||||
@@ -39,21 +33,16 @@ namespace Pf2eDataScrapper.SchemaBuilder
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="value">Value to be checked</param>
|
/// <param name="value">Value to be checked</param>
|
||||||
/// <returns>Compatible type</returns>
|
/// <returns>Compatible type</returns>
|
||||||
public virtual AbstractSchemaType Adapt(object value)
|
public virtual AbstractSchemaType Adapt(JToken? value)
|
||||||
{
|
{
|
||||||
if (IsCompatibleValue(value))
|
return IsCompatibleValue(value) ? this : new MixedSchemaType(this);
|
||||||
{
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Add new possible value.
|
/// Add new possible value.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="value">New value</param>
|
/// <param name="value">New value</param>
|
||||||
public abstract void AddValue(object value);
|
public abstract void AddValue(JToken? value);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// String representation of type.
|
/// String representation of type.
|
||||||
@@ -61,14 +50,13 @@ namespace Pf2eDataScrapper.SchemaBuilder
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public abstract string GetTypeName();
|
public abstract string GetTypeName();
|
||||||
|
|
||||||
public override abstract string ToString();
|
public abstract override string ToString();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Tabulation characters.
|
/// Tabulation characters.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const string TAB = " ";
|
public const string Tab = " ";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Limit of elements in list or dictionary to be displayed.
|
/// Limit of elements in list or dictionary to be displayed.
|
||||||
@@ -82,7 +70,7 @@ namespace Pf2eDataScrapper.SchemaBuilder
|
|||||||
/// <returns>Indented text</returns>
|
/// <returns>Indented text</returns>
|
||||||
public static string Indent(string text)
|
public static string Indent(string text)
|
||||||
{
|
{
|
||||||
return text.Replace("\n", "\n" + TAB);
|
return text.Replace("\n", "\n" + Tab);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -90,37 +78,14 @@ namespace Pf2eDataScrapper.SchemaBuilder
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="value">Value to be checked</param>
|
/// <param name="value">Value to be checked</param>
|
||||||
/// <returns>String representation of value type</returns>
|
/// <returns>String representation of value type</returns>
|
||||||
public static string ResolveType(object value)
|
public static string ResolveType(JToken? value)
|
||||||
{
|
{
|
||||||
if (value == null)
|
if (value is null || value.Type is JTokenType.Null or JTokenType.Undefined or JTokenType.None)
|
||||||
{
|
{
|
||||||
return "null";
|
return "null";
|
||||||
}
|
}
|
||||||
if (value is string)
|
|
||||||
{
|
return value.Type.ToString().ToLower();
|
||||||
return "string";
|
|
||||||
}
|
|
||||||
if (value is bool)
|
|
||||||
{
|
|
||||||
return "bool";
|
|
||||||
}
|
|
||||||
if (value is int)
|
|
||||||
{
|
|
||||||
return "int";
|
|
||||||
}
|
|
||||||
if (value is double)
|
|
||||||
{
|
|
||||||
return "double";
|
|
||||||
}
|
|
||||||
if (value is List<object>)
|
|
||||||
{
|
|
||||||
return "List<object>";
|
|
||||||
}
|
|
||||||
if (value is Dictionary<string, object>)
|
|
||||||
{
|
|
||||||
return "Dictionary<string, object>";
|
|
||||||
}
|
|
||||||
return "object";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -128,15 +93,17 @@ namespace Pf2eDataScrapper.SchemaBuilder
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="value">Value to be checked</param>
|
/// <param name="value">Value to be checked</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static AbstractSchemaType CreateFromValue(object value)
|
public static AbstractSchemaType CreateFromValue(JToken? value)
|
||||||
{
|
{
|
||||||
|
var type = ResolveType(value);
|
||||||
|
|
||||||
switch (ResolveType(value))
|
return type switch
|
||||||
{
|
{
|
||||||
//case "null": return new NullSchemaType();
|
"null" => new EmptySchemaType(),
|
||||||
|
"array" => new ArraySchemaType(),
|
||||||
default: throw new NotImplementedException();
|
"object" => new ObjectSchemaType(),
|
||||||
}
|
_ => new StaticSchemaType(type),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
namespace Pf2eDataScrapper.SchemaBuilder
|
namespace Pf2eDataScrapper.SchemaBuilder
|
||||||
{
|
{
|
||||||
@@ -14,10 +15,10 @@ namespace Pf2eDataScrapper.SchemaBuilder
|
|||||||
|
|
||||||
protected AbstractSchemaType ElementType = new EmptySchemaType();
|
protected AbstractSchemaType ElementType = new EmptySchemaType();
|
||||||
|
|
||||||
protected List<object> Values = [];
|
protected List<JToken?> Values = [];
|
||||||
|
|
||||||
|
|
||||||
public override void AddValue(object value)
|
public override void AddValue(JToken? value)
|
||||||
{
|
{
|
||||||
MinCount = Math.Min(MinCount, Values.Count);
|
MinCount = Math.Min(MinCount, Values.Count);
|
||||||
MaxCount = Math.Max(MaxCount, Values.Count);
|
MaxCount = Math.Max(MaxCount, Values.Count);
|
||||||
@@ -30,14 +31,14 @@ namespace Pf2eDataScrapper.SchemaBuilder
|
|||||||
|
|
||||||
public override string GetTypeName()
|
public override string GetTypeName()
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
return $"array<{ElementType.GetTypeName()}>";
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool IsCompatibleValue(object value)
|
public override bool IsCompatibleValue(JToken? value)
|
||||||
{
|
{
|
||||||
var type = ResolveType(value);
|
var type = ResolveType(value);
|
||||||
|
|
||||||
if (type == "none")
|
if (type == "null")
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -58,14 +59,14 @@ namespace Pf2eDataScrapper.SchemaBuilder
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override AbstractSchemaType Adapt(object value)
|
public override AbstractSchemaType Adapt(JToken? value)
|
||||||
{
|
{
|
||||||
if (value is not Array)
|
if (value is null || value.Type != JTokenType.Array)
|
||||||
{
|
{
|
||||||
throw new Exception("Value is not an array");
|
throw new Exception("Value is not an array");
|
||||||
}
|
}
|
||||||
|
|
||||||
var array = (object[])value;
|
var array = (JArray)value;
|
||||||
|
|
||||||
if ((ResolveType(value) == "none") && (ElementType is EmptySchemaType))
|
if ((ResolveType(value) == "none") && (ElementType is EmptySchemaType))
|
||||||
{
|
{
|
||||||
@@ -92,7 +93,7 @@ namespace Pf2eDataScrapper.SchemaBuilder
|
|||||||
rows.Add($"Length: min={MinCount} max={MaxCount}");
|
rows.Add($"Length: min={MinCount} max={MaxCount}");
|
||||||
rows.Add("Value type:" + Indent(ElementType.GetTypeName()));
|
rows.Add("Value type:" + Indent(ElementType.GetTypeName()));
|
||||||
|
|
||||||
return string.Join("\n" + TAB, rows);
|
return string.Join("\n" + Tab, rows);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System;
|
using Newtonsoft.Json.Linq;
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
@@ -8,7 +9,7 @@ namespace Pf2eDataScrapper.SchemaBuilder
|
|||||||
{
|
{
|
||||||
internal class EmptySchemaType : AbstractSchemaType
|
internal class EmptySchemaType : AbstractSchemaType
|
||||||
{
|
{
|
||||||
public override AbstractSchemaType Adapt(object value)
|
public override AbstractSchemaType Adapt(JToken? value)
|
||||||
{
|
{
|
||||||
if (IsCompatibleValue(value))
|
if (IsCompatibleValue(value))
|
||||||
{
|
{
|
||||||
@@ -18,7 +19,7 @@ namespace Pf2eDataScrapper.SchemaBuilder
|
|||||||
return CreateFromValue(value).MarkNullable();
|
return CreateFromValue(value).MarkNullable();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void AddValue(object value)
|
public override void AddValue(JToken? value)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -27,7 +28,7 @@ namespace Pf2eDataScrapper.SchemaBuilder
|
|||||||
return "empty[] -> null";
|
return "empty[] -> null";
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool IsCompatibleValue(object value)
|
public override bool IsCompatibleValue(JToken? value)
|
||||||
{
|
{
|
||||||
return ResolveType(value) == "null";
|
return ResolveType(value) == "null";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System;
|
using Newtonsoft.Json.Linq;
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
@@ -15,7 +16,7 @@ namespace Pf2eDataScrapper.SchemaBuilder
|
|||||||
Types.Add(baseType);
|
Types.Add(baseType);
|
||||||
}
|
}
|
||||||
|
|
||||||
public AbstractSchemaType AddType(object value)
|
public AbstractSchemaType AddType(JToken value)
|
||||||
{
|
{
|
||||||
var newType = CreateFromValue(value);
|
var newType = CreateFromValue(value);
|
||||||
|
|
||||||
@@ -24,7 +25,7 @@ namespace Pf2eDataScrapper.SchemaBuilder
|
|||||||
return newType;
|
return newType;
|
||||||
}
|
}
|
||||||
|
|
||||||
public AbstractSchemaType? GetCompatibleType(object value)
|
public AbstractSchemaType? GetCompatibleType(JToken value)
|
||||||
{
|
{
|
||||||
foreach (var type in Types)
|
foreach (var type in Types)
|
||||||
{
|
{
|
||||||
@@ -36,7 +37,7 @@ namespace Pf2eDataScrapper.SchemaBuilder
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override AbstractSchemaType Adapt(object value)
|
public override AbstractSchemaType Adapt(JToken? value)
|
||||||
{
|
{
|
||||||
var type = GetCompatibleType(value);
|
var type = GetCompatibleType(value);
|
||||||
|
|
||||||
@@ -48,7 +49,7 @@ namespace Pf2eDataScrapper.SchemaBuilder
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void AddValue(object value)
|
public override void AddValue(JToken? value)
|
||||||
{
|
{
|
||||||
GetCompatibleType(value)?.AddValue(value);
|
GetCompatibleType(value)?.AddValue(value);
|
||||||
}
|
}
|
||||||
@@ -60,7 +61,7 @@ namespace Pf2eDataScrapper.SchemaBuilder
|
|||||||
return string.Join("|", typeNames);
|
return string.Join("|", typeNames);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool IsCompatibleValue(object value) => true;
|
public override bool IsCompatibleValue(JToken? value) => true;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// String representation of type.
|
/// String representation of type.
|
||||||
@@ -75,7 +76,7 @@ namespace Pf2eDataScrapper.SchemaBuilder
|
|||||||
result.Add(Indent(type?.ToString() ?? ""));
|
result.Add(Indent(type?.ToString() ?? ""));
|
||||||
}
|
}
|
||||||
|
|
||||||
return string.Join("\n" + TAB, result);
|
return string.Join("\n" + Tab, result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System;
|
using Newtonsoft.Json.Linq;
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
@@ -12,17 +13,17 @@ namespace Pf2eDataScrapper.SchemaBuilder
|
|||||||
|
|
||||||
public Dictionary<string, AbstractSchemaType> Fields = [];
|
public Dictionary<string, AbstractSchemaType> Fields = [];
|
||||||
|
|
||||||
public override void AddValue(object value)
|
public override void AddValue(JToken? value)
|
||||||
{
|
{
|
||||||
if (value == null)
|
if (value is null || ResolveType(value) == "null")
|
||||||
{
|
{
|
||||||
MarkNullable();
|
MarkNullable();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var dict = (Dictionary<string, object>)value;
|
var obj = (JObject)value;
|
||||||
|
|
||||||
if (dict.Keys.Count == 0)
|
if (obj.Count == 0)
|
||||||
{
|
{
|
||||||
MarkNullable();
|
MarkNullable();
|
||||||
return;
|
return;
|
||||||
@@ -30,13 +31,13 @@ namespace Pf2eDataScrapper.SchemaBuilder
|
|||||||
|
|
||||||
foreach (var field in Fields)
|
foreach (var field in Fields)
|
||||||
{
|
{
|
||||||
if (!dict.ContainsKey(field.Key))
|
if (!obj.ContainsKey(field.Key))
|
||||||
{
|
{
|
||||||
field.Value.MarkNullable();
|
field.Value.MarkNullable();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var field in dict)
|
foreach (var field in obj)
|
||||||
{
|
{
|
||||||
Fields[field.Key].AddValue(field.Value);
|
Fields[field.Key].AddValue(field.Value);
|
||||||
}
|
}
|
||||||
@@ -44,32 +45,38 @@ namespace Pf2eDataScrapper.SchemaBuilder
|
|||||||
|
|
||||||
public override string GetTypeName()
|
public override string GetTypeName()
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
return "object";
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool IsCompatibleValue(object value)
|
public override bool IsCompatibleValue(JToken? value)
|
||||||
{
|
{
|
||||||
return (ResolveType(value) == "object") || (ResolveType(value) == "null");
|
return (ResolveType(value) == "object") || (ResolveType(value) == "null");
|
||||||
}
|
}
|
||||||
|
|
||||||
public override AbstractSchemaType Adapt(object value)
|
public override AbstractSchemaType Adapt(JToken? value)
|
||||||
{
|
{
|
||||||
if (!IsCompatibleValue(value))
|
if (!IsCompatibleValue(value))
|
||||||
{
|
{
|
||||||
return new MixedSchemaType(this);
|
return new MixedSchemaType(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
Dictionary<string, object> dict = (Dictionary<string, object>)value;
|
if (value is null)
|
||||||
|
{
|
||||||
|
return MarkNullable();
|
||||||
|
}
|
||||||
|
|
||||||
|
var obj = (JObject)value;
|
||||||
|
|
||||||
foreach (var field in Fields)
|
foreach (var field in Fields)
|
||||||
{
|
{
|
||||||
if (!dict.ContainsKey(field.Key))
|
if (!obj.ContainsKey(field.Key))
|
||||||
{
|
{
|
||||||
field.Value.MarkNullable();
|
field.Value.MarkNullable();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var field in dict) {
|
foreach (var field in obj)
|
||||||
|
{
|
||||||
if (!Fields.ContainsKey(field.Key))
|
if (!Fields.ContainsKey(field.Key))
|
||||||
{
|
{
|
||||||
Fields[field.Key] = CreateFromValue(field.Value);
|
Fields[field.Key] = CreateFromValue(field.Value);
|
||||||
@@ -79,6 +86,8 @@ namespace Pf2eDataScrapper.SchemaBuilder
|
|||||||
Fields[field.Key].MarkNullable();
|
Fields[field.Key].MarkNullable();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Fields[field.Key].Adapt(field.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
IsFresh = false;
|
IsFresh = false;
|
||||||
|
|||||||
@@ -1,8 +1,4 @@
|
|||||||
using System;
|
using Newtonsoft.Json.Linq;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Pf2eDataScrapper.SchemaBuilder
|
namespace Pf2eDataScrapper.SchemaBuilder
|
||||||
{
|
{
|
||||||
@@ -10,9 +6,9 @@ namespace Pf2eDataScrapper.SchemaBuilder
|
|||||||
{
|
{
|
||||||
protected string Type = type;
|
protected string Type = type;
|
||||||
|
|
||||||
protected Dictionary<object, int> Values = [];
|
protected Dictionary<JToken?, int> Values = [];
|
||||||
|
|
||||||
public override void AddValue(object value)
|
public override void AddValue(JToken? value)
|
||||||
{
|
{
|
||||||
Values.TryAdd(value, 0);
|
Values.TryAdd(value, 0);
|
||||||
|
|
||||||
@@ -24,26 +20,26 @@ namespace Pf2eDataScrapper.SchemaBuilder
|
|||||||
return Type;
|
return Type;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool IsCompatibleValue(object value)
|
public override bool IsCompatibleValue(JToken? value)
|
||||||
{
|
{
|
||||||
return ResolveType(value) == Type;
|
return ResolveType(value) == Type;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
string result = Type;
|
var result = Type;
|
||||||
int count = Values.Count;
|
var count = Values.Count;
|
||||||
|
|
||||||
result += $" (different values: {count})";
|
result += $" (different values: {count})";
|
||||||
|
|
||||||
if ((count <= ElementLimit) && (count > 0))
|
if (count is > ElementLimit or <= 0)
|
||||||
{
|
{
|
||||||
|
return result.Trim();
|
||||||
|
}
|
||||||
|
|
||||||
result += "\n";
|
result += "\n";
|
||||||
foreach (var value in Values)
|
|
||||||
{
|
result = Values.Aggregate(result, (current, value) => current + $"{Tab}{value.Key} (x{value.Value})\n");
|
||||||
result += $"{TAB}{value.Key} (x{value.Value})\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result.Trim();
|
return result.Trim();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,239 @@
|
|||||||
|
{
|
||||||
|
"_id": "XwfcJuskrhI9GIjX",
|
||||||
|
"img": "systems/pf2e/icons/classes/alchemist.webp",
|
||||||
|
"name": "Alchemist",
|
||||||
|
"system": {
|
||||||
|
"ancestryFeatLevels": {
|
||||||
|
"value": [
|
||||||
|
1,
|
||||||
|
5,
|
||||||
|
9,
|
||||||
|
13,
|
||||||
|
17
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"attacks": {
|
||||||
|
"advanced": 0,
|
||||||
|
"martial": 0,
|
||||||
|
"other": {
|
||||||
|
"name": "Alchemical Bombs",
|
||||||
|
"rank": 1
|
||||||
|
},
|
||||||
|
"simple": 1,
|
||||||
|
"unarmed": 1
|
||||||
|
},
|
||||||
|
"classFeatLevels": {
|
||||||
|
"value": [
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
4,
|
||||||
|
6,
|
||||||
|
8,
|
||||||
|
10,
|
||||||
|
12,
|
||||||
|
14,
|
||||||
|
16,
|
||||||
|
18,
|
||||||
|
20
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"defenses": {
|
||||||
|
"heavy": 0,
|
||||||
|
"light": 1,
|
||||||
|
"medium": 1,
|
||||||
|
"unarmored": 1
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"value": "<p><em>There's no sight more beautiful to you than a strange brew bubbling in a beaker, and you consume your ingenious elixirs with abandon. You're fascinated by uncovering the secrets of science and the natural world, and you're constantly experimenting in your lab or on the go with inventive concoctions for every eventuality. You are fearless in the face of risk, hurling explosive or toxic creations at your foes. Your unique path toward greatness is lined with alchemical brews that push your mind and body to their limits.</em></p>\n<p>@UUID[Compendium.pf2e.journals.JournalEntry.kzxu2dI7tFxv6Ix6.JournalEntryPage.Om4e4tsgNWrComie]{Alchemist}</p>"
|
||||||
|
},
|
||||||
|
"generalFeatLevels": {
|
||||||
|
"value": [
|
||||||
|
3,
|
||||||
|
7,
|
||||||
|
11,
|
||||||
|
15,
|
||||||
|
19
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"hp": 8,
|
||||||
|
"items": {
|
||||||
|
"4bdrn": {
|
||||||
|
"img": "icons/tools/laboratory/mortar-liquid-pink.webp",
|
||||||
|
"level": 1,
|
||||||
|
"name": "Alchemy",
|
||||||
|
"uuid": "Compendium.pf2e.classfeatures.Item.Alchemy"
|
||||||
|
},
|
||||||
|
"CJ528": {
|
||||||
|
"img": "icons/magic/symbols/question-stone-yellow.webp",
|
||||||
|
"level": 11,
|
||||||
|
"name": "Chemical Hardiness",
|
||||||
|
"uuid": "Compendium.pf2e.classfeatures.Item.Chemical Hardiness"
|
||||||
|
},
|
||||||
|
"JK2gV": {
|
||||||
|
"img": "icons/consumables/potions/potion-round-stopped-yellow-green.webp",
|
||||||
|
"level": 15,
|
||||||
|
"name": "Alchemical Weapon Mastery",
|
||||||
|
"uuid": "Compendium.pf2e.classfeatures.Item.Alchemical Weapon Mastery"
|
||||||
|
},
|
||||||
|
"NY2Su": {
|
||||||
|
"img": "icons/creatures/eyes/human-single-blue.webp",
|
||||||
|
"level": 9,
|
||||||
|
"name": "Perception Expertise",
|
||||||
|
"uuid": "Compendium.pf2e.classfeatures.Item.Perception Expertise"
|
||||||
|
},
|
||||||
|
"Q1cDZ": {
|
||||||
|
"img": "icons/magic/symbols/question-stone-yellow.webp",
|
||||||
|
"level": 15,
|
||||||
|
"name": "Explosion Dodger",
|
||||||
|
"uuid": "Compendium.pf2e.classfeatures.Item.Explosion Dodger"
|
||||||
|
},
|
||||||
|
"SWNkG": {
|
||||||
|
"img": "icons/equipment/chest/breastplate-collared-steel.webp",
|
||||||
|
"level": 19,
|
||||||
|
"name": "Medium Armor Mastery",
|
||||||
|
"uuid": "Compendium.pf2e.classfeatures.Item.Medium Armor Mastery"
|
||||||
|
},
|
||||||
|
"aay1e": {
|
||||||
|
"img": "systems/pf2e/icons/features/classes/walls-of-will.webp",
|
||||||
|
"level": 7,
|
||||||
|
"name": "Will Expertise",
|
||||||
|
"uuid": "Compendium.pf2e.classfeatures.Item.Will Expertise"
|
||||||
|
},
|
||||||
|
"b3ohx": {
|
||||||
|
"img": "icons/magic/symbols/question-stone-yellow.webp",
|
||||||
|
"level": 1,
|
||||||
|
"name": "Research Field",
|
||||||
|
"uuid": "Compendium.pf2e.classfeatures.Item.Research Field"
|
||||||
|
},
|
||||||
|
"cNnDR": {
|
||||||
|
"img": "icons/equipment/chest/breastplate-quilted-brown.webp",
|
||||||
|
"level": 13,
|
||||||
|
"name": "Medium Armor Expertise",
|
||||||
|
"uuid": "Compendium.pf2e.classfeatures.Item.Medium Armor Expertise"
|
||||||
|
},
|
||||||
|
"enfdl": {
|
||||||
|
"img": "systems/pf2e/icons/features/classes/weapon-specialization.webp",
|
||||||
|
"level": 13,
|
||||||
|
"name": "Weapon Specialization",
|
||||||
|
"uuid": "Compendium.pf2e.classfeatures.Item.Weapon Specialization"
|
||||||
|
},
|
||||||
|
"f4a0p": {
|
||||||
|
"img": "icons/magic/light/orbs-hand-gray.webp",
|
||||||
|
"level": 9,
|
||||||
|
"name": "Double Brew",
|
||||||
|
"uuid": "Compendium.pf2e.classfeatures.Item.Double Brew"
|
||||||
|
},
|
||||||
|
"j5xnl": {
|
||||||
|
"img": "systems/pf2e/icons/features/classes/alchemical-weapon-expertise.webp",
|
||||||
|
"level": 7,
|
||||||
|
"name": "Alchemical Weapon Expertise",
|
||||||
|
"uuid": "Compendium.pf2e.classfeatures.Item.Alchemical Weapon Expertise"
|
||||||
|
},
|
||||||
|
"l170j": {
|
||||||
|
"img": "icons/magic/symbols/question-stone-yellow.webp",
|
||||||
|
"level": 13,
|
||||||
|
"name": "(Choice) Greater Field Discovery",
|
||||||
|
"uuid": "Compendium.pf2e.classfeatures.Item.Greater Field Discovery"
|
||||||
|
},
|
||||||
|
"plldb": {
|
||||||
|
"img": "icons/consumables/potions/potion-round-corked-glowing-green.webp",
|
||||||
|
"level": 9,
|
||||||
|
"name": "Alchemical Expertise",
|
||||||
|
"uuid": "Compendium.pf2e.classfeatures.Item.Alchemical Expertise"
|
||||||
|
},
|
||||||
|
"rpCw8": {
|
||||||
|
"img": "icons/magic/symbols/question-stone-yellow.webp",
|
||||||
|
"level": 17,
|
||||||
|
"name": "Abundant Vials",
|
||||||
|
"uuid": "Compendium.pf2e.classfeatures.Item.Abundant Vials"
|
||||||
|
},
|
||||||
|
"vOE57": {
|
||||||
|
"img": "icons/magic/symbols/question-stone-yellow.webp",
|
||||||
|
"level": 11,
|
||||||
|
"name": "Advanced Vials",
|
||||||
|
"uuid": "Compendium.pf2e.classfeatures.Item.Advanced Vials"
|
||||||
|
},
|
||||||
|
"xzcdr": {
|
||||||
|
"img": "icons/magic/symbols/question-stone-yellow.webp",
|
||||||
|
"level": 5,
|
||||||
|
"name": "Field Discovery",
|
||||||
|
"uuid": "Compendium.pf2e.classfeatures.Item.Field Discovery"
|
||||||
|
},
|
||||||
|
"yc9ak": {
|
||||||
|
"img": "icons/commodities/materials/bowl-powder-teal.webp",
|
||||||
|
"level": 5,
|
||||||
|
"name": "Powerful Alchemy",
|
||||||
|
"uuid": "Compendium.pf2e.classfeatures.Item.Powerful Alchemy"
|
||||||
|
},
|
||||||
|
"yy1uz": {
|
||||||
|
"img": "icons/tools/cooking/mortar-stone-yellow.webp",
|
||||||
|
"level": 17,
|
||||||
|
"name": "Alchemical Mastery",
|
||||||
|
"uuid": "Compendium.pf2e.classfeatures.Item.Alchemical Mastery"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"keyAbility": {
|
||||||
|
"value": [
|
||||||
|
"int"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"perception": 1,
|
||||||
|
"publication": {
|
||||||
|
"license": "ORC",
|
||||||
|
"remaster": true,
|
||||||
|
"title": "Pathfinder Player Core 2"
|
||||||
|
},
|
||||||
|
"rules": [
|
||||||
|
{
|
||||||
|
"key": "ActiveEffectLike",
|
||||||
|
"mode": "upgrade",
|
||||||
|
"path": "system.proficiencies.attacks.weapon-base-alchemical-bomb.rank",
|
||||||
|
"value": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"savingThrows": {
|
||||||
|
"fortitude": 2,
|
||||||
|
"reflex": 2,
|
||||||
|
"will": 1
|
||||||
|
},
|
||||||
|
"skillFeatLevels": {
|
||||||
|
"value": [
|
||||||
|
2,
|
||||||
|
4,
|
||||||
|
6,
|
||||||
|
8,
|
||||||
|
10,
|
||||||
|
12,
|
||||||
|
14,
|
||||||
|
16,
|
||||||
|
18,
|
||||||
|
20
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"skillIncreaseLevels": {
|
||||||
|
"value": [
|
||||||
|
3,
|
||||||
|
5,
|
||||||
|
7,
|
||||||
|
9,
|
||||||
|
11,
|
||||||
|
13,
|
||||||
|
15,
|
||||||
|
17,
|
||||||
|
19
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"spellcasting": 0,
|
||||||
|
"trainedSkills": {
|
||||||
|
"additional": 3,
|
||||||
|
"value": [
|
||||||
|
"crafting"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"traits": {
|
||||||
|
"rarity": "common",
|
||||||
|
"value": []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"type": "class"
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user