mirror of
https://github.com/Outer-Wilds-New-Horizons/new-horizons.git
synced 2025-12-11 20:15:44 +01:00
Added XML Schemas To Docs (#57)
* Add Bootstrap Extension * Rename main.yml * Artifact Upload * Fix Bootstrap Reference Error * BootstrapTreeProcessor * getiterator removed * keys function * Style Images * Update docs_build.yml * Added Meta Files * Template Get * Fix Page Ref * Update BASE_URL * Sort Schemas * Add Sitemaps * Add favicons, open-graph, and setup guide * Update Setup.md * Update .gitignore * Update Setup.md * Use _blank on external links * Restructured Docs * Fix Links * Added XML Schemas * Name XML Schemas
This commit is contained in:
parent
a78bde530e
commit
2552d1b8a4
2
.github/workflows/docs_build.yml
vendored
2
.github/workflows/docs_build.yml
vendored
@ -42,6 +42,8 @@ jobs:
|
||||
cp NewHorizons/schema.json content/schemas/
|
||||
cp NewHorizons/star_system_schema.json content/schemas/
|
||||
cp NewHorizons/translation_schema.json content/schemas/
|
||||
cp NewHorizons/shiplog_schema.xsd content/schemas/
|
||||
cp NewHorizons/dialogue_schema.xsd content/schemas/
|
||||
|
||||
- name: Create Output Dir
|
||||
run: |
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Dialogue Tree -->
|
||||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
||||
<!-- Dialogue Tree Info -->
|
||||
<xs:element name="DialogueTree">
|
||||
|
||||
@ -62,7 +62,8 @@
|
||||
"$ref": "#/$defs/colorPart"
|
||||
},
|
||||
"A": {
|
||||
"$ref": "#/$defs/colorPart"
|
||||
"$ref": "#/$defs/colorPart",
|
||||
"default": 255
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -112,8 +113,7 @@
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "Unique name of your planet",
|
||||
"default": "New planet"
|
||||
"description": "Unique name of your planet"
|
||||
},
|
||||
"starSystem": {
|
||||
"type": "string",
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Astro Object Entry -->
|
||||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
||||
<!-- Astro Object Entry Info -->
|
||||
<xs:element name="AstroObjectEntry">
|
||||
|
||||
@ -8,6 +8,7 @@ jinja2 = "*"
|
||||
json-schema-for-humans = "*"
|
||||
markdown = "*"
|
||||
htmlmin = "*"
|
||||
xmlschema = "*"
|
||||
|
||||
[dev-packages]
|
||||
|
||||
|
||||
22
docs/Pipfile.lock
generated
22
docs/Pipfile.lock
generated
@ -1,7 +1,7 @@
|
||||
{
|
||||
"_meta": {
|
||||
"hash": {
|
||||
"sha256": "32e1cfede01ba2b312ef0b550d6d9b15ce2a2a89db4c1a961143f2a543c7cd0f"
|
||||
"sha256": "c708dfd89a9b02892b9323b222b2a077a8aee1852c02a2324d85dd29a06d45ba"
|
||||
},
|
||||
"pipfile-spec": 6,
|
||||
"requires": {
|
||||
@ -55,6 +55,14 @@
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==0.5.6"
|
||||
},
|
||||
"elementpath": {
|
||||
"hashes": [
|
||||
"sha256:2a432775e37a19e4362443078130a7dbfc457d7d093cd421c03958d9034cc08b",
|
||||
"sha256:3a27aaf3399929fccda013899cb76d3ff111734abf4281e5f9d3721ba0b9ffa3"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==2.5.0"
|
||||
},
|
||||
"htmlmin": {
|
||||
"hashes": [
|
||||
"sha256:50c1ef4630374a5d723900096a961cff426dff46b48f34d194a81bbe14eca178"
|
||||
@ -99,7 +107,7 @@
|
||||
"sha256:8f4ac8d9a124ab408c67361090ed512deda746c04362c36c2ec16190c720c2b0",
|
||||
"sha256:91113caf23aa662570fe21984f08fe74f814695c0a0ea8e863a8b4c4f63f9f6e"
|
||||
],
|
||||
"markers": "python_version >= '3.5' and python_full_version < '4.0.0'",
|
||||
"markers": "python_version >= '3.5' and python_version < '4'",
|
||||
"version": "==2.4.2"
|
||||
},
|
||||
"markupsafe": {
|
||||
@ -253,8 +261,16 @@
|
||||
"sha256:000ca7f471a233c2251c6c7023ee85305721bfdf18621ebff4fd17a8653427ed",
|
||||
"sha256:0e7c33d9a63e7ddfcb86780aac87befc2fbddf46c58dbb487e0855f7ceec283c"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_full_version < '4.0.0'",
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'",
|
||||
"version": "==1.26.8"
|
||||
},
|
||||
"xmlschema": {
|
||||
"hashes": [
|
||||
"sha256:3ce6fe408a8c0a0ca5917cbe6181a933dfb5cfade9714eeb07b6335f9aff7b10",
|
||||
"sha256:a7ba52b774a87b59c6428cd9e3601210cbb226552208015bd40800698a6500ad"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.9.2"
|
||||
}
|
||||
},
|
||||
"develop": {}
|
||||
|
||||
@ -1,17 +1,9 @@
|
||||
{% if dumb %}
|
||||
{% from 'macros.jinja2' import external_link, is_active_page, nav_item with context %}
|
||||
{% else %}
|
||||
{% from 'base/macros.jinja2' import external_link, is_active_page, nav_item with context %}
|
||||
{% endif %}
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
{% if dumb %}
|
||||
{% include "meta.jinja2" %}
|
||||
{% else %}
|
||||
{% include "base/meta.jinja2" %}
|
||||
{% endif %}
|
||||
<link href="https://bootswatch.com/5/darkly/bootstrap.min.css"
|
||||
crossorigin="anonymous" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.8.1/font/bootstrap-icons.css">
|
||||
@ -21,7 +13,7 @@
|
||||
<script crossorigin="anonymous" src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
|
||||
{% block resources %} {% endblock %}
|
||||
</head>
|
||||
<body>
|
||||
<body onload="{% block onLoad %}{% endblock %}">
|
||||
<header>
|
||||
<nav class="navbar navbar-expand-xl navbar-dark bg-dark mb-2">
|
||||
<div class="container-fluid">
|
||||
|
||||
@ -1,3 +0,0 @@
|
||||
{%- macro restriction(inner_text, css_class_name, html_id) -%}
|
||||
<p><span class="badge bg-dark restriction {{ css_class_name }}-restriction" id="{{ html_id }}">{{ inner_text }}</span></p>
|
||||
{%- endmacro -%}
|
||||
@ -3,3 +3,5 @@
|
||||
{% macro is_active_page(title) %}{% if title == page.title %}active{% endif %}{% endmacro %}
|
||||
|
||||
{% macro nav_item(title, href) %}<li class="nav-item me-1"><a href="{{ href }}" class="nav-link {% if title|lower == page.title|lower %}active{% endif %}" {% if title|lower == page.title|lower %}aria-current="page"{% endif %}>{{ title }}</a></li>{% endmacro %}
|
||||
|
||||
{% macro badge(type, content, classes) %}<span class="badge rounded-pill bg-{{ type }} {{ classes }}">{{ content|safe }}</span>{% endmacro %}
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
{{ content|markdown }}
|
||||
{{ rendered|safe }}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
{% from "macro_restriction.jinja2" import restriction with context %}
|
||||
{% from "base/schema/json/macro_restriction.jinja2" import restriction with context %}
|
||||
{% from "base/macros.jinja2" import badge %}
|
||||
|
||||
{% macro tabbed_section(operator, current_node) %}
|
||||
{% include "tabbed_section.jinja2" %}
|
||||
{% include "base/schema/json/tabbed_section.jinja2" %}
|
||||
{% endmacro %}
|
||||
|
||||
{% macro content(schema, skip_headers=False) %}
|
||||
@ -12,14 +13,14 @@
|
||||
|
||||
{% if not skip_headers %}
|
||||
{% if config.show_breadcrumbs %}
|
||||
{% include "breadcrumbs.jinja2" %}
|
||||
{% include "base/schema/json/breadcrumbs.jinja2" %}
|
||||
{% endif %}
|
||||
|
||||
{# Display type #}
|
||||
{% if not schema is combining %}
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="badge bg-secondary">Type: {{ type_name }}</span>
|
||||
{{ badge("secondary", "Type: " + type_name) }}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
@ -29,14 +30,14 @@
|
||||
{% if default_value %}
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
{{ " " }}<span class="badge bg-secondary">Default: {{ default_value }}</span>
|
||||
{{ badge("secondary", "Default: " + default_value) }}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
<br/>
|
||||
|
||||
{% set description = (schema | get_description) %}
|
||||
{% include "section_description.jinja2" %}
|
||||
{% include "base/schema/json/section_description.jinja2" %}
|
||||
{% endif %}
|
||||
|
||||
|
||||
@ -52,7 +53,7 @@
|
||||
{{ content(schema.kw_any_of.array_items[0]) }}
|
||||
{% else %}
|
||||
{% if schema.explicit_no_additional_properties %}
|
||||
{{ " " }}<span class="badge bg-info no-additional">No Additional Properties</span>
|
||||
{{ badge("info", "No Additional Properties") }}
|
||||
{% endif %}
|
||||
|
||||
{# Combining: allOf, anyOf, oneOf, not #}
|
||||
@ -66,7 +67,7 @@
|
||||
<div class="one-of-value" id="{{ schema.kw_one_of.html_id }}">{{ tabbed_section("oneOf", schema.kw_one_of) }}</div>
|
||||
{% endif %}
|
||||
{% if schema.kw_not %}
|
||||
{% include "section_not.jinja2" %}
|
||||
{% include "base/schema/json/section_not.jinja2" %}
|
||||
{% endif %}
|
||||
|
||||
{# Enum and const #}
|
||||
@ -91,29 +92,29 @@
|
||||
|
||||
{# Conditional subschema, or if-then-else section #}
|
||||
{% if schema.has_conditional %}
|
||||
{% include "section_conditional_subschema.jinja2" %}
|
||||
{% include "base/schema/json/section_conditional_subschema.jinja2" %}
|
||||
{% endif %}
|
||||
|
||||
{# Required properties that are not defined under "properties". They will only be listed #}
|
||||
{% include "section_undocumented_required_properties.jinja2" %}
|
||||
{% include "base/schema/json/section_undocumented_required_properties.jinja2" %}
|
||||
|
||||
{# Show the requested type(s) #}
|
||||
{% include "badge_type.jinja2" %}
|
||||
{% include "base/schema/json/badge_type.jinja2" %}
|
||||
|
||||
{# Show array restrictions #}
|
||||
{% if type_name.startswith("array") %}
|
||||
{% include "section_array.jinja2" %}
|
||||
{% include "base/schema/json/section_array.jinja2" %}
|
||||
{% endif %}
|
||||
|
||||
{# Display examples #}
|
||||
{% set examples = schema.examples %}
|
||||
{% if examples %}
|
||||
{% include "section_examples.jinja2" %}
|
||||
{% include "base/schema/json/section_examples.jinja2" %}
|
||||
{% endif %}
|
||||
|
||||
{# Properties, pattern properties, additional properties #}
|
||||
{% for sub_property in schema.iterate_properties %}
|
||||
{% include "section_properties.jinja2" %}
|
||||
{% include "base/schema/json/section_properties.jinja2" %}
|
||||
{% endfor %}
|
||||
|
||||
{% endif %}
|
||||
5
docs/content/base/schema/json/macro_restriction.jinja2
Normal file
5
docs/content/base/schema/json/macro_restriction.jinja2
Normal file
@ -0,0 +1,5 @@
|
||||
{% from "base/macros.jinja2" import badge %}
|
||||
|
||||
{% macro restriction(inner_text, css_class_name, html_id) %}
|
||||
<p>{{ badge("dark", inner_text|safe, 'p-1') }}</p>
|
||||
{% endmacro %}
|
||||
@ -1,10 +1,12 @@
|
||||
{% extends "base.jinja2" %}
|
||||
{% from 'content.jinja2' import content with context %}
|
||||
{% extends "base/base.jinja2" %}
|
||||
{% from 'base/schema/json/content.jinja2' import content with context %}
|
||||
|
||||
{% block resources %}
|
||||
<script src="/schemas/schema_doc.min.js"></script>
|
||||
{% include "base/schema/schema_includes.jinja2" %}
|
||||
{% endblock %}
|
||||
|
||||
{% block onLoad %}anchorOnLoad();{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col text-center">
|
||||
@ -1,3 +1,4 @@
|
||||
{% from "base/macros.jinja2" import badge %}
|
||||
{% set html_id = sub_property.html_id %}
|
||||
<div class="row my-2">
|
||||
<div class="col">
|
||||
@ -17,14 +18,15 @@
|
||||
{% if sub_property.is_additional_properties %}
|
||||
</em>
|
||||
{% endif %}
|
||||
{% macro subprop_badge(type, content) %}{{ badge(type, content|title, "required-property, ms-2") }}{% endmacro %}
|
||||
{% if sub_property.is_required_property %}
|
||||
{{ " " }}<span class="badge bg-warning required-property ms-2">Required</span>
|
||||
{{ " " }}{{ subprop_badge('warning', "Required") }}
|
||||
{% endif %}
|
||||
{% if sub_property is deprecated %}
|
||||
{{ " " }}<span class="badge bg-danger deprecated-property ms-2">Deprecated</span>
|
||||
{{ " " }}{{ subprop_badge('danger', "Deprecated") }}
|
||||
{% endif %}
|
||||
{% if sub_property.is_pattern_property %}
|
||||
{{ " " }}<span class="badge bg-info pattern-property ms-2">Pattern Property</span>
|
||||
{{ " " }}{{ subprop_badge('info', "Pattern Property") }}
|
||||
{% endif %}
|
||||
</button>
|
||||
</h2>
|
||||
2
docs/content/base/schema/schema_includes.jinja2
Normal file
2
docs/content/base/schema/schema_includes.jinja2
Normal file
@ -0,0 +1,2 @@
|
||||
<script src="{{ 'scripts/schema.min.js'|static }}"></script>
|
||||
<link rel="stylesheet" href="{{ 'styles/schema.css'|static }}"/>
|
||||
85
docs/content/base/schema/xml/schema_base.jinja2
Normal file
85
docs/content/base/schema/xml/schema_base.jinja2
Normal file
@ -0,0 +1,85 @@
|
||||
{% extends "base/base.jinja2" %}
|
||||
|
||||
{% from "base/macros.jinja2" import badge %}
|
||||
|
||||
{% macro body(element, trail) %}
|
||||
<div class="breadcrumbs">
|
||||
{% for node in trail|split("-") %}
|
||||
{% if loop.first %}
|
||||
Root
|
||||
{% else %}
|
||||
<a href="#{{ node }}"
|
||||
onclick="anchorLink('{{ node }}')">{{ node }}</a>
|
||||
{% endif %}
|
||||
{% if not loop.last %}
|
||||
<i class="bi-arrow-right-short"></i>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% if element.occurs[0] != 1 or element.occurs[1] != 1 %}
|
||||
{{ badge("secondary", element.occurs|occurs_text) }}
|
||||
{% endif %}
|
||||
{{ badge("secondary", "Type: " + element.type|name, "ms-2") }}
|
||||
{% if element.type.has_complex_content() %}
|
||||
{% for child in element.type.content|children %}
|
||||
{{ render(child, trail) }}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
|
||||
{% macro render(element, trail) %}
|
||||
<div class="row my-2">
|
||||
<div class="col">
|
||||
{% set trail=trail+"-"+element.name %}
|
||||
{% set html_id=trail %}
|
||||
<div class="accordion" id="accordion_{{ html_id }}">
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header" id="heading_{{ html_id }}">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse"
|
||||
data-bs-target="#{{ html_id }}" aria-expanded="false"
|
||||
aria-controls="{{ html_id }}" onclick="setAnchor('#{{ html_id }}')">
|
||||
<span class="property-name align-middle pb-1">{{ element.name | escape }}</span>
|
||||
{% if element.occurs[0] == 1 and element.occurs[1] == 1 %}
|
||||
{{ " " }}{{ badge("warning", "Required", "ms-2") }}
|
||||
{% endif %}
|
||||
</button>
|
||||
</h2>
|
||||
<div id="{{ trail }}" class="accordion-collapse collapse"
|
||||
aria-labelledby="heading_{{ html_id }}"
|
||||
data-parent="#accordion_{{ html_id }}">
|
||||
<div class="accordion-body">
|
||||
{% if element.parent != None and element.type.name == element.parent.parent.name %}
|
||||
{{ badge("info", "<em>Recursive Reference</em>") }}
|
||||
{% else %}
|
||||
{{ body(element, trail) }}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{% block resources %}
|
||||
{% include "base/schema/schema_includes.jinja2" %}
|
||||
{% endblock %}
|
||||
|
||||
{% block onLoad %}anchorOnLoad();{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col text-center">
|
||||
<h1>{{ page.title|title }}</h1>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
{% for element in schema.elements.values() %}
|
||||
{% for child in element.type.content %}
|
||||
{{ render(child, element.name) }}
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@ -3,20 +3,20 @@ Sort-Priority: 90
|
||||
|
||||
## Details/Scatterer
|
||||
|
||||
For physical objects there are currently two ways of setting them up: specify an asset bundle and path to load a custom asset you created, or specify the path to the item you want to copy from the game in the scene hierarchy. Use the [Unity Explorer](https://outerwildsmods.com/mods/unityexplorer) mod to find an object you want to copy onto your new body. Some objects work better than others for this. Good luck. Some pointers:
|
||||
For physical objects there are currently two ways of setting them up: specify an asset bundle and path to load a custom asset you created, or specify the path to the item you want to copy from the game in the scene hierarchy. Use the [Unity Explorer](https://outerwildsmods.com/mods/unityexplorer){ target="_blank" } mod to find an object you want to copy onto your new body. Some objects work better than others for this. Good luck. Some pointers:
|
||||
- Use "Object Explorer" to search
|
||||
- Do not use the search functionality on Scene Explorer, it is really really slow. Use the "Object Search" tab instead.
|
||||
- Do not use the search functionality on Scene Explorer, it is really, really slow. Use the "Object Search" tab instead.
|
||||
- Generally you can find planets by writing their name with no spaces/punctuation followed by "_Body".
|
||||
|
||||
## Asset Bundles
|
||||
|
||||
Here is a template project: [Outer Wilds Unity Template](https://github.com/xen-42/outer-wilds-unity-template)
|
||||
Here is a template project: [Outer Wilds Unity Template](https://github.com/xen-42/outer-wilds-unity-template){ target="_blank" }
|
||||
|
||||
The template project contains ripped versions of all the game scripts, meaning you can put things like DirectionalForceVolumes in your Unity project to have artificial gravity volumes loaded right into the game.
|
||||
|
||||
If for whatever reason you want to set up a Unity project manually instead of using the template, follow these instructions:
|
||||
|
||||
1. Start up a Unity 2017 project (I use Unity 2017.4.40f1 (64-bit), so if you use something else I can't guarantee it will work). The DLC updated Outer Wilds to 2019.4.27 so that probably works but I personally haven't tried it.
|
||||
1. Start up a Unity 2017 project (I use Unity 2017.4.40f1 (64-bit), so if you use something else I can't guarantee it will work). The DLC updated Outer Wilds to 2019.4.27 so that probably works, but I personally haven't tried it.
|
||||
2. In the "Assets" folder in Unity, create a new folder called "Editor". In it create a file called "CreateAssetBundle.cs" with the following code in it:
|
||||
|
||||
```cs
|
||||
@ -40,8 +40,8 @@ public class CreateAssetBundles
|
||||
```
|
||||
|
||||
3. Create your object in the Unity scene and save it as a prefab.
|
||||
4. Add all files used (models, prefabs, textures, materials, etc) to an asset bundle by selecting them and using the drop down in the bottom right. Here I am adding a rover model to my "rss" asset bundle for the Real Solar System add-on.
|
||||
4. Add all files used (models, prefabs, textures, materials, etc.) to an asset bundle by selecting them and using the dropdown in the bottom right. Here I am adding a rover model to my "rss" asset bundle for the Real Solar System add-on.
|
||||

|
||||
|
||||
5. In the top left click the "Assets" drop-down and select "Build AssetBundles". This should create your asset bundle in a folder in the root directory called "StreamingAssets".
|
||||
6. Copy the asset bundle and asset bundle .manifest files from StreamingAssets into your mod's "planets" folder. If you did everything properly they should work in game. To double check everything is included, open the .manifest file in a text editor to see the files included and their paths.
|
||||
6. Copy the asset bundle and asset bundle .manifest files from StreamingAssets into your mod's "planets" folder. If you did everything properly they should work in game. To double-check everything is included, open the .manifest file in a text editor to see the files included and their paths.
|
||||
|
||||
@ -6,35 +6,41 @@ Sort-Priority: 200
|
||||
|
||||
# Welcome!
|
||||
|
||||
This is the official documentation for [New Horizons](https://github.com/xen-42/outer-wilds-new-horizons)
|
||||
This is the official documentation for [New Horizons](https://github.com/xen-42/outer-wilds-new-horizons){ target="_blank" }
|
||||
|
||||
## Getting Started with Planet Creation
|
||||
|
||||
There is a template [here](https://github.com/xen-42/ow-new-horizons-config-template) if you want to release your own planet mod using configs. You can learn how the configs work by picking apart the [Real Solar System](https://github.com/xen-42/outer-wilds-real-solar-system) mod or the [New Horizons Examples](https://github.com/xen-42/ow-new-horizons-examples) mod.
|
||||
There is a template [here](https://github.com/xen-42/ow-new-horizons-config-template){ target="_blank" } if you want to release your own
|
||||
planet mod using configs. You can learn how the configs work by picking apart
|
||||
the [Real Solar System](https://github.com/xen-42/outer-wilds-real-solar-system){ target="_blank" } mod or
|
||||
the [New Horizons Examples](https://github.com/xen-42/ow-new-horizons-examples){ target="_blank" } mod.
|
||||
|
||||
Planets are created using a JSON file format structure, and placed in a folder called planets (or in any subdirectory of it) in the location where New Horizons is installed (by default this folder doesn't exist, you have to create it within the xen.NewHorizons directory).
|
||||
Planets are created using a JSON file format structure, and placed in a folder called planets (or in any subdirectory of
|
||||
it) in the location where New Horizons is installed (by default this folder doesn't exist, you have to create it within
|
||||
the xen.NewHorizons directory).
|
||||
|
||||
To locate this directory, click the "⋮" symbol next to "New Horizons" in the Outer Wilds Mod Manager and then click "show in explorer" in the pop-up.
|
||||
To locate this directory, click the "⋮" symbol next to "New Horizons" in the Outer Wilds Mod Manager and then click "
|
||||
show in explorer" in the pop-up.
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
Now that you have created your planets folder, this is where you will put your planet config files. A config file will look something like this:
|
||||
Now that you have created your planets folder, this is where you will put your planet config files. A config file will
|
||||
look something like this:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "Wetrock",
|
||||
"$schema": "https://raw.githubusercontent.com/xen-42/outer-wilds-new-horizons/master/NewHorizons/schema.json",
|
||||
"starSystem": "SolarSystem",
|
||||
"Base" :
|
||||
{
|
||||
"Base": {
|
||||
"groundSize": 100,
|
||||
"surfaceSize": 101,
|
||||
"surfaceGravity": 12,
|
||||
"hasMapMarker": true
|
||||
},
|
||||
"Orbit" :
|
||||
{
|
||||
"Orbit": {
|
||||
"semiMajorAxis": 1300,
|
||||
"inclination": 0,
|
||||
"primaryBody": "TIMBER_HEARTH",
|
||||
@ -44,11 +50,9 @@ Now that you have created your planets folder, this is where you will put your p
|
||||
"eccentricity": 0,
|
||||
"argumentOfPeriapsis": 0
|
||||
},
|
||||
"Atmosphere" :
|
||||
{
|
||||
"Atmosphere": {
|
||||
"size": 150,
|
||||
"fogTint" :
|
||||
{
|
||||
"fogTint": {
|
||||
"r": 200,
|
||||
"g": 255,
|
||||
"b": 255,
|
||||
@ -58,24 +62,34 @@ Now that you have created your planets folder, this is where you will put your p
|
||||
"fogDensity": 0.2,
|
||||
"hasRain": true
|
||||
},
|
||||
"Props" :
|
||||
{
|
||||
"Props": {
|
||||
"scatter": [
|
||||
{"path" : "DreamWorld_Body/Sector_DreamWorld/Sector_DreamZone_1/Props_DreamZone_1/OtherComponentsGroup/Trees_Z1/DreamHouseIsland/Tree_DW_M_Var", "count" : 12}
|
||||
{
|
||||
"path": "DreamWorld_Body/Sector_DreamWorld/Sector_DreamZone_1/Props_DreamZone_1/OtherComponentsGroup/Trees_Z1/DreamHouseIsland/Tree_DW_M_Var",
|
||||
"count": 12
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The first field you should have in any config file is the `name`. This should be unique in the solar system. If it isn't, the mod will instead try to modify the planet that already has that name.
|
||||
The first field you should have in any config file is the `name`. This should be unique in the solar system. If it
|
||||
isn't, the mod will instead try to modify the planet that already has that name.
|
||||
|
||||
After `name` is `starSystem`. You can use this to place the planet in a different system accessible using a black-hole (see the [Singularity](#singularity) module). To ensure compatibility with other mods this name should be unique. After setting a value for this, the changes in the config will only affect that body in that star system. By default, it is "SolarSystem", which is the scene from the stock game.
|
||||
After `name` is `starSystem`. You can use this to place the planet in a different system accessible using a black-hole (
|
||||
see the [Singularity]({{ 'body'|route }}#Singularity) module). To ensure compatibility with other mods this name should be unique. After
|
||||
setting a value for this, the changes in the config will only affect that body in that star system. By default, it is "
|
||||
SolarSystem", which is the scene from the stock game.
|
||||
|
||||
Including the "$schema" line is optional, but will allow your text editor to highlight errors and auto-suggest words in your config. I recommend using VSCode as a text editor, but anything that supports Json files will work. Something as basic as notepad will work but will not highlight any of your errors.
|
||||
Including the "$schema" line is optional, but will allow your text editor to highlight errors and auto-suggest words in
|
||||
your config. I recommend using VSCode as a text editor, but anything that supports Json files will work. Something as
|
||||
basic as notepad will work but will not highlight any of your errors.
|
||||
|
||||
The config file is then split into modules, each one with its own fields that define how that part of the planet will be generated. In the example above I've used the `Base`, `Orbit`, `Atmosphere`, and `Props` modules. A config file must have a `Base` and `Orbit` module, the rest are optional.
|
||||
The config file is then split into modules, each one with its own fields that define how that part of the planet will be
|
||||
generated. In the example above I've used the `Base`, `Orbit`, `Atmosphere`, and `Props` modules. A config file must
|
||||
have a `Base` and `Orbit` module, the rest are optional.
|
||||
|
||||
Each { must match up with a closing } to denote its section. If you don't know how JSONs work then check Wikipedia.
|
||||
Each `{` must match up with a closing `}` to denote its section. If you don't know how JSONs work then check Wikipedia.
|
||||
|
||||
Modules look like this:
|
||||
|
||||
@ -93,14 +107,20 @@ Modules look like this:
|
||||
}
|
||||
```
|
||||
|
||||
In this example the `Star` module has a `size` field and a `tint` field. Since the colour is a complex object it needs another set of { and } around it, and then it has its own fields inside it : `r`, `g`, `b`, and `a`. Don't forget to put commas after each field.
|
||||
In this example the `Star` module has a `size` field and a `tint` field. Since the colour is a complex object it needs
|
||||
another set of `{` and `}` around it, and then it has its own fields inside it : `r`, `g`, `b`, and `a`. Don't forget to put
|
||||
commas after each field.
|
||||
|
||||
Most fields are either true/false, a decimal number, and integer number, or a string (word with quotation marks around it).
|
||||
Most fields are either true/false, a decimal number, and integer number, or a string (word with quotation marks around
|
||||
it).
|
||||
|
||||
Check out the rest of the site for how to format planet, star system, dialogue, ship log, and translation files!
|
||||
|
||||
## Helpful resources
|
||||
|
||||
The texturemap/heightmap feature was inspired by the KSP mod Kopernicus. A lot of the same techniques that apply to planet creation there apply to New Horizons. You can check out a planetary texturing guide repository [here](https://forum.kerbalspaceprogram.com/index.php?/topic/165285-planetary-texturing-guide-repository/).
|
||||
The texturemap/heightmap feature was inspired by the KSP mod Kopernicus. A lot of the same techniques that apply to
|
||||
planet creation there apply to New Horizons. You can check out a planetary texturing guide
|
||||
repository [here](https://forum.kerbalspaceprogram.com/index.php?/topic/165285-planetary-texturing-guide-repository/){ target="_blank" }.
|
||||
|
||||
[Photopea](https://www.photopea.com/) is a free browser-based photo editor which has useful features like rectangular-to-polar coordinate transformation, useful for fixing abnormalities at the poles of your planets.
|
||||
[Photopea](https://www.photopea.com/){ target="_blank" } is a free browser-based photo editor which has useful features like
|
||||
rectangular-to-polar coordinate transformation, useful for fixing abnormalities at the poles of your planets.
|
||||
@ -11,19 +11,19 @@ Welcome! this page outlines how to create a custom ship log.
|
||||
|
||||
These mods are useful when developing your ship log
|
||||
|
||||
- [Unity Explorer](https://outerwildsmods.com/mods/unityexplorer)
|
||||
- [Collider Visualizer](https://outerwildsmods.com/mods/collidervisualizer)
|
||||
- [Save Editor](https://outerwildsmods.com/mods/saveeditor)
|
||||
- [Unity Explorer](https://outerwildsmods.com/mods/unityexplorer){ target="_blank" }
|
||||
- [Collider Visualizer](https://outerwildsmods.com/mods/collidervisualizer){ target="_blank" }
|
||||
- [Save Editor](https://outerwildsmods.com/mods/saveeditor){ target="_blank" }
|
||||
|
||||
## Helpful Tools
|
||||
|
||||
These tools/references are highly recommended
|
||||
|
||||
- [VSCode](https://code.visualstudio.com/) { target="_blank" }
|
||||
- [VSCode XML Addon](https://marketplace.visualstudio.com/items?itemName=redhat.vscode-xml)
|
||||
- [XML Basics Tutorial](https://www.w3schools.com/xml/xml_whatis.asp)
|
||||
- [The XML Schema](https://github.com/xen-42/outer-wilds-new-horizons/blob/master/NewHorizons/shiplog_schema.xsd)
|
||||
- [The Examples Mod](https://github.com/xen-42/ow-new-horizons-examples)
|
||||
- [VSCode XML Addon](https://marketplace.visualstudio.com/items?itemName=redhat.vscode-xml){ target="_blank" }
|
||||
- [XML Basics Tutorial](https://www.w3schools.com/xml/xml_whatis.asp){ target="_blank" }
|
||||
- [The XML Schema](https://github.com/xen-42/outer-wilds-new-horizons/blob/master/NewHorizons/shiplog_schema.xsd){ target="_blank" }
|
||||
- [The Examples Mod](https://github.com/xen-42/ow-new-horizons-examples){ target="_blank" }
|
||||
|
||||
# Understanding Ship Logs
|
||||
|
||||
@ -161,7 +161,7 @@ navigate to ShipLogManager.
|
||||
In the example XML, you may notice something like `xsi:noNamespaceSchemaLocation` at the top, this tells whatever editor
|
||||
you're using that the file at that link is the schema. The game simply ignores this though, so it won't be able to catch
|
||||
errors at runtime.
|
||||
Some editors may require you to [Trust](https://code.visualstudio.com/docs/editor/workspace-trust) the workspace to use
|
||||
Some editors may require you to [Trust](https://code.visualstudio.com/docs/editor/workspace-trust){ target="_blank" } the workspace to use
|
||||
the schema file. Doing this varies per-editor, and you may also have to right-click the link and click download.
|
||||
|
||||
## Loading The File
|
||||
|
||||
33
docs/content/static/styles/schema.css
Normal file
33
docs/content/static/styles/schema.css
Normal file
@ -0,0 +1,33 @@
|
||||
.jsfh-animated-property {
|
||||
animation: eclair;
|
||||
animation-iteration-count: 1;
|
||||
animation-fill-mode: forwards;
|
||||
animation-duration: .75s;
|
||||
}
|
||||
|
||||
@keyframes eclair {
|
||||
0%, 100% {
|
||||
transform: scale(1);
|
||||
}
|
||||
50% {
|
||||
transform: scale(1.03);
|
||||
}
|
||||
}
|
||||
|
||||
.accordion-button:not(.collapsed) {
|
||||
color: #effff7;
|
||||
background-color: #2b2b2b;
|
||||
}
|
||||
|
||||
.accordion-button:not(.collapsed)::after {
|
||||
color: #effff7;
|
||||
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e");
|
||||
}
|
||||
|
||||
.accordion-item {
|
||||
border: 1px solid rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
|
||||
.bg-warning {
|
||||
color: #131313;
|
||||
}
|
||||
130
docs/generate.py
130
docs/generate.py
@ -2,102 +2,76 @@ import os
|
||||
from pathlib import Path
|
||||
from shutil import copytree, rmtree
|
||||
|
||||
from htmlmin import minify
|
||||
from jinja2 import Environment, select_autoescape, FileSystemLoader
|
||||
from markupsafe import Markup
|
||||
from json_schema_for_humans.generate import GenerationConfiguration
|
||||
from markdown import Markdown
|
||||
|
||||
from lib.BootstrapExtension import BootstrapExtension
|
||||
from lib.Schema import Schema
|
||||
from lib.Page import Page
|
||||
from lib.Content.HTMLPage import HTMLPage
|
||||
from lib.Content.JSONSchema import JSONSchema
|
||||
from lib.Content.MDPage import MDPage
|
||||
from lib.Content.MetaItem import MetaItem, MinifiedMetaItem
|
||||
from lib.Content.XMLSchema import XMLSchema
|
||||
|
||||
print("Setup")
|
||||
|
||||
OUT_DIR = os.getenv("OUT_DIR", "/")
|
||||
BASE_URL = os.getenv("BASE_URL", "")
|
||||
|
||||
env = Environment(
|
||||
loader=FileSystemLoader("content/"),
|
||||
autoescape=select_autoescape(['jinja2'])
|
||||
)
|
||||
|
||||
scanners = (MDPage, HTMLPage)
|
||||
schema_scanners = (JSONSchema, XMLSchema)
|
||||
meta_files = (MinifiedMetaItem(Path('browserconfig.jinja2'), '.xml'), MinifiedMetaItem(Path('sitemap.jinja2'), '.xml'), MetaItem(Path('robots.jinja2'), '.txt'))
|
||||
|
||||
router = {}
|
||||
|
||||
env.filters.update({
|
||||
'upper_first': lambda x: x[0].upper() + x[1:],
|
||||
'static': lambda path: str(Path(OUT_DIR, path).as_posix()),
|
||||
'route': lambda title: router.get(title.lower(), "#"),
|
||||
'full_url': lambda relative: BASE_URL + (relative[1:] if relative[0] == "/" else relative)
|
||||
})
|
||||
|
||||
MetaItem.initialize(env)
|
||||
MinifiedMetaItem.initialize(env)
|
||||
|
||||
pages = []
|
||||
schemas = []
|
||||
|
||||
print("Clearing Old Output")
|
||||
|
||||
if Path("out/").exists():
|
||||
rmtree("out/", ignore_errors=True)
|
||||
|
||||
copytree("content/static", "out")
|
||||
os.makedirs("out/schemas", exist_ok=True)
|
||||
|
||||
env = Environment(
|
||||
loader=FileSystemLoader("content"),
|
||||
autoescape=select_autoescape(['jinja2'])
|
||||
)
|
||||
print("Scanning For Files")
|
||||
|
||||
markdown_settings = {
|
||||
'extensions': ['extra', 'toc', 'meta', BootstrapExtension()]
|
||||
}
|
||||
for scanner in scanners:
|
||||
new_pages = scanner.initialize(env)
|
||||
for page in new_pages:
|
||||
page.add_route(router, OUT_DIR)
|
||||
pages += new_pages
|
||||
|
||||
schema_settings = GenerationConfiguration(custom_template_path="content/base/schema_base.jinja2")
|
||||
schema_settings.link_to_reused_ref = False
|
||||
schema_settings.minify = True
|
||||
|
||||
minify_settings = {
|
||||
'remove_empty_space': True,
|
||||
'keep_pre': True,
|
||||
'remove_optional_attribute_quotes': False
|
||||
}
|
||||
|
||||
env.minify_settings = minify_settings
|
||||
|
||||
md = Markdown(**markdown_settings)
|
||||
|
||||
env.filters["upper_first"] = lambda x: x[0].upper() + x[1:]
|
||||
env.filters["markdown"] = lambda text: Markup(md.convert(text))
|
||||
env.filters["static"] = lambda path: str(Path(OUT_DIR, path).as_posix())
|
||||
|
||||
pages_paths = Path("content/pages").glob("**/*.md")
|
||||
schemas_paths = Path("content/schemas").glob("**/*.json")
|
||||
|
||||
router = {}
|
||||
|
||||
env.filters['route'] = lambda title: router.get(title.lower(), "#")
|
||||
env.filters['full_url'] = lambda relative: BASE_URL + (relative[1:] if relative[0] == "/" else relative)
|
||||
|
||||
pages = []
|
||||
schemas = []
|
||||
|
||||
for page_path in pages_paths:
|
||||
new_page = Page(page_path, env, markdown_settings)
|
||||
router[new_page.title.lower()] = OUT_DIR + str(new_page.out_path.relative_to('out/'))
|
||||
pages.append(new_page)
|
||||
|
||||
for schema_path in schemas_paths:
|
||||
new_schema = Schema(schema_path, env, schema_settings)
|
||||
router[new_schema.title.lower()] = OUT_DIR + "schemas/" + str(new_schema.out_path.relative_to("out/schemas/"))
|
||||
schemas.append(new_schema)
|
||||
for scanner in schema_scanners:
|
||||
new_schemas = scanner.initialize(env)
|
||||
for schema in new_schemas:
|
||||
schema.add_route(router, OUT_DIR)
|
||||
schemas += new_schemas
|
||||
|
||||
content = pages + schemas
|
||||
|
||||
if OUT_DIR != "":
|
||||
router['home'] = OUT_DIR
|
||||
print("Generating Pages")
|
||||
|
||||
pages.sort(key=lambda p: p.sort_priority, reverse=True)
|
||||
schemas.sort(key=lambda s: s.title)
|
||||
|
||||
|
||||
def log_build(in_path, out_path):
|
||||
print("Building:", str(in_path), "->", str(out_path))
|
||||
|
||||
|
||||
def build_meta(in_path, out_path, do_minify=False):
|
||||
log_build(in_path, out_path)
|
||||
meta_template = env.get_template(str(in_path.relative_to("content/")))
|
||||
with Path("out/", out_path).open(mode="w+", encoding="utf-8") as file:
|
||||
out = meta_template.render(content=content)
|
||||
if do_minify:
|
||||
out = minify(out, **minify_settings)
|
||||
file.write(out)
|
||||
|
||||
|
||||
print("Building Meta Files")
|
||||
build_meta(Path("content/sitemap.jinja2"), Path("sitemap.xml"), do_minify=True)
|
||||
build_meta(Path("content/robots.jinja2"), Path("robots.txt"))
|
||||
build_meta(Path("content/browserconfig.jinja2"), Path("fav/browserconfig.xml"), do_minify=True)
|
||||
|
||||
print("Building Pages")
|
||||
for item in content:
|
||||
log_build(item.in_path, item.out_path)
|
||||
item.render(page=item, pages=pages, schemas=schemas)
|
||||
item.generate(pages=pages, schemas=schemas)
|
||||
|
||||
for meta_file in meta_files:
|
||||
meta_file.env = env
|
||||
meta_file.generate(content=content)
|
||||
|
||||
print("Done")
|
||||
|
||||
58
docs/lib/Content/AbstractContentItem.py
Normal file
58
docs/lib/Content/AbstractContentItem.py
Normal file
@ -0,0 +1,58 @@
|
||||
from abc import ABC
|
||||
from pathlib import Path
|
||||
|
||||
from htmlmin import minify
|
||||
from jinja2 import Environment
|
||||
|
||||
|
||||
class AbstractContentItem(ABC):
|
||||
|
||||
output_ext: str = '.html'
|
||||
env: Environment
|
||||
in_path: Path
|
||||
out_path: Path
|
||||
root_dir: Path
|
||||
|
||||
def __init__(self, in_path: Path, ext: str = None):
|
||||
self.env = None
|
||||
if ext is not None:
|
||||
self.output_ext = ext
|
||||
self.in_path = in_path
|
||||
self.out_path = Path('out/', in_path.name).with_suffix(self.output_ext)
|
||||
|
||||
@classmethod
|
||||
def initialize(cls, env: Environment) -> list:
|
||||
raise NotImplementedError()
|
||||
|
||||
def render(self, **context) -> str:
|
||||
raise NotImplementedError()
|
||||
|
||||
def _save(self, rendered: str):
|
||||
self.out_path.parent.mkdir(mode=511, parents=True, exist_ok=True)
|
||||
with self.out_path.open(mode='w+', encoding='utf-8') as file:
|
||||
file.write(rendered)
|
||||
|
||||
def generate(self, **context) -> None:
|
||||
print("Building:", self.in_path, "->", self.out_path)
|
||||
self._save(self.render(**context))
|
||||
|
||||
def add_route(self, routes, out_dir):
|
||||
pass
|
||||
|
||||
|
||||
class MinifyMixin(AbstractContentItem, ABC):
|
||||
|
||||
MINIFY_SETTINGS = {
|
||||
'remove_empty_space': True,
|
||||
'keep_pre': True,
|
||||
'remove_optional_attribute_quotes': False
|
||||
}
|
||||
|
||||
def _save(self, rendered: str):
|
||||
rendered = minify(rendered, **self.MINIFY_SETTINGS)
|
||||
super(MinifyMixin, self)._save(rendered)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
24
docs/lib/Content/AbstractSchemaItem.py
Normal file
24
docs/lib/Content/AbstractSchemaItem.py
Normal file
@ -0,0 +1,24 @@
|
||||
from abc import ABC
|
||||
from pathlib import Path
|
||||
|
||||
from jinja2 import Template, Environment
|
||||
|
||||
from lib.Content.AbstractTemplatedItem import AbstractTemplatedItem
|
||||
|
||||
|
||||
class AbstractSchemaItem(AbstractTemplatedItem, ABC):
|
||||
root_dir = Path('schemas/')
|
||||
|
||||
def __init__(self, in_path: Path):
|
||||
super().__init__(in_path)
|
||||
self.out_path = Path('out/schemas/', self.out_path.relative_to('out'))
|
||||
|
||||
@classmethod
|
||||
def initialize(cls, env: Environment):
|
||||
new_pages = super(AbstractSchemaItem, cls).initialize(env)
|
||||
for page in new_pages:
|
||||
page.out_path = page.out_path.with_stem(page.title.replace(" ", "_").lower())
|
||||
return new_pages
|
||||
|
||||
def render(self, template: Template, **context):
|
||||
raise NotImplementedError()
|
||||
48
docs/lib/Content/AbstractTemplatedItem.py
Normal file
48
docs/lib/Content/AbstractTemplatedItem.py
Normal file
@ -0,0 +1,48 @@
|
||||
from abc import ABC
|
||||
from itertools import chain
|
||||
from pathlib import Path
|
||||
|
||||
from jinja2 import Template, Environment
|
||||
|
||||
from lib.Content.AbstractContentItem import AbstractContentItem, MinifyMixin
|
||||
|
||||
|
||||
class AbstractTemplatedItem(MinifyMixin, AbstractContentItem, ABC):
|
||||
extensions: tuple[str]
|
||||
title: str = None
|
||||
description: str | None = None
|
||||
sort_priority: int = None
|
||||
|
||||
def load_metadata(self):
|
||||
if self.title is None:
|
||||
self.title = self.in_path.stem
|
||||
if self.description is None:
|
||||
self.description = None
|
||||
if self.sort_priority is None:
|
||||
self.sort_priority = 10
|
||||
|
||||
@classmethod
|
||||
def initialize(cls, env: Environment):
|
||||
pages = []
|
||||
file_paths = list(chain(*[Path('content/', cls.root_dir).glob(f'**/*.{ext}') for ext in cls.extensions]))
|
||||
for path in file_paths:
|
||||
new_page = cls(path)
|
||||
new_page.env = env
|
||||
new_page.load_metadata()
|
||||
pages.append(new_page)
|
||||
return pages
|
||||
|
||||
def inner_render(self, template: Template, **context):
|
||||
return template.render(**context)
|
||||
|
||||
def render(self, **context):
|
||||
container = self.env.get_template('base/page_template.jinja2')
|
||||
template = self.env.get_template(str(self.in_path.relative_to(Path('content/')).as_posix()))
|
||||
context.update({
|
||||
'page': self,
|
||||
'rendered': self.inner_render(template, **context)
|
||||
})
|
||||
return container.render(**context)
|
||||
|
||||
def add_route(self, router, out_dir):
|
||||
router[self.title.lower()] = out_dir + str(self.out_path.relative_to('out/'))
|
||||
8
docs/lib/Content/HTMLPage.py
Normal file
8
docs/lib/Content/HTMLPage.py
Normal file
@ -0,0 +1,8 @@
|
||||
from pathlib import Path
|
||||
|
||||
from lib.Content.AbstractTemplatedItem import AbstractTemplatedItem
|
||||
|
||||
|
||||
class HTMLPage(AbstractTemplatedItem):
|
||||
root_dir = Path("pages/")
|
||||
extensions = ('html', 'jinja2', 'jinja')
|
||||
58
docs/lib/Content/JSONSchema.py
Normal file
58
docs/lib/Content/JSONSchema.py
Normal file
@ -0,0 +1,58 @@
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
from json_schema_for_humans.generate import generate_schemas_doc
|
||||
from json_schema_for_humans.generation_configuration import GenerationConfiguration
|
||||
from json_schema_for_humans.schema.schema_to_render import SchemaToRender
|
||||
from json_schema_for_humans.template_renderer import TemplateRenderer
|
||||
|
||||
from lib.Content.AbstractSchemaItem import AbstractSchemaItem
|
||||
|
||||
SCHEMA_SETTINGS = GenerationConfiguration()
|
||||
SCHEMA_SETTINGS.link_to_reused_ref = False
|
||||
SCHEMA_SETTINGS.minify = False
|
||||
|
||||
|
||||
class NoPrint:
|
||||
def __enter__(self):
|
||||
self._original_stdout = sys.stdout
|
||||
sys.stdout = open(os.devnull, 'w')
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
sys.stdout.close()
|
||||
sys.stdout = self._original_stdout
|
||||
|
||||
|
||||
class JSONSchema(AbstractSchemaItem):
|
||||
extensions = ('json',)
|
||||
|
||||
def load_metadata(self):
|
||||
self.sort_priority = 10
|
||||
with self.in_path.open(mode='r', encoding='utf-8') as file:
|
||||
self.title = json.load(file).get('title', self.in_path.stem)
|
||||
self.description = "Schema for a " + self.title + " in New Horizons"
|
||||
super(JSONSchema, self).load_metadata()
|
||||
|
||||
def render(self, **context):
|
||||
context.update({
|
||||
'page': self
|
||||
})
|
||||
dumb_renderer = TemplateRenderer(SCHEMA_SETTINGS)
|
||||
self.env.filters.update(dumb_renderer.template.environment.filters)
|
||||
self.env.tests.update(dumb_renderer.template.environment.tests)
|
||||
self.env.globals.update(dumb_renderer.template.environment.globals)
|
||||
schemas = [SchemaToRender(self.in_path, None, None)]
|
||||
schema_template = self.env.get_template(str(Path('base/schema/json/schema_base.jinja2').as_posix()))
|
||||
template_renderer = TemplateRenderer(SCHEMA_SETTINGS, schema_template)
|
||||
template_renderer.render = lambda inter: self.template_override(template_renderer, inter, **context)
|
||||
with NoPrint():
|
||||
rendered = generate_schemas_doc(schemas, template_renderer)
|
||||
return rendered[str(self.in_path.name)]
|
||||
|
||||
def template_override(self, template: TemplateRenderer, intermediate_schema, **context):
|
||||
template.template.environment.loader = self.env.loader
|
||||
rendered = template.template.render(schema=intermediate_schema, config=SCHEMA_SETTINGS,
|
||||
title=self.title + " Schema", **context)
|
||||
return rendered
|
||||
39
docs/lib/Content/MDPage.py
Normal file
39
docs/lib/Content/MDPage.py
Normal file
@ -0,0 +1,39 @@
|
||||
from pathlib import Path
|
||||
|
||||
from markdown import Markdown
|
||||
|
||||
from lib.Content.AbstractTemplatedItem import AbstractTemplatedItem
|
||||
from lib.BootstrapExtension import BootstrapExtension
|
||||
|
||||
|
||||
class MDPage(AbstractTemplatedItem):
|
||||
root_dir = Path("pages/")
|
||||
extensions = ('md', 'markdown')
|
||||
|
||||
MARKDOWN_SETTINGS = {
|
||||
'extensions': ['extra', 'toc', 'meta', BootstrapExtension()]
|
||||
}
|
||||
|
||||
def load_metadata(self):
|
||||
md = self.__get_md()
|
||||
with self.in_path.open(mode='r', encoding='utf-8') as file:
|
||||
md.convert(file.read())
|
||||
|
||||
self.title = md.Meta.get('title')[0]
|
||||
self.description = md.Meta.get('description', [None])[0]
|
||||
raw_priority = md.Meta.get('sort-priority')
|
||||
if raw_priority is not None:
|
||||
self.sort_priority = int(raw_priority[0])
|
||||
out_name = md.Meta.get('out-file', None)
|
||||
if out_name is not None:
|
||||
self.out_path = self.out_path.with_stem(out_name[0])
|
||||
super(MDPage, self).load_metadata()
|
||||
|
||||
def __get_md(self):
|
||||
return Markdown(**self.MARKDOWN_SETTINGS)
|
||||
|
||||
def inner_render(self, template, **context):
|
||||
rendered_markdown = super(MDPage, self).inner_render(template, **context)
|
||||
md = self.__get_md()
|
||||
return md.convert(rendered_markdown)
|
||||
|
||||
18
docs/lib/Content/MetaItem.py
Normal file
18
docs/lib/Content/MetaItem.py
Normal file
@ -0,0 +1,18 @@
|
||||
from jinja2 import Environment
|
||||
|
||||
from lib.Content.AbstractContentItem import AbstractContentItem, MinifyMixin
|
||||
|
||||
|
||||
class MetaItem(AbstractContentItem):
|
||||
|
||||
@classmethod
|
||||
def initialize(cls, env: Environment) -> list:
|
||||
cls.env = env
|
||||
|
||||
def render(self, **context) -> str:
|
||||
template = self.env.get_template(str(self.in_path.as_posix()))
|
||||
return template.render(**context)
|
||||
|
||||
|
||||
class MinifiedMetaItem(MinifyMixin, MetaItem):
|
||||
pass
|
||||
85
docs/lib/Content/XMLSchema.py
Normal file
85
docs/lib/Content/XMLSchema.py
Normal file
@ -0,0 +1,85 @@
|
||||
import os
|
||||
|
||||
import xmlschema
|
||||
from xmlschema.extras.codegen import AbstractGenerator, filter_method
|
||||
|
||||
__all__ = ('XMLSchema',)
|
||||
|
||||
from lib.Content.AbstractSchemaItem import AbstractSchemaItem
|
||||
|
||||
|
||||
def children(group):
|
||||
child = [child for child in group if child.__class__.__name__ == "XsdElement"]
|
||||
for child_list in [children(inner_group) for inner_group in group if inner_group.__class__.__name__ == "XsdGroup"]:
|
||||
child += child_list
|
||||
return child
|
||||
|
||||
|
||||
def ancestry(element):
|
||||
if element.parent is None:
|
||||
print(element.name)
|
||||
return [element.name]
|
||||
else:
|
||||
if element.name is None:
|
||||
return ancestry(element.parent)
|
||||
else:
|
||||
return [element.name] + ancestry(element.parent)
|
||||
|
||||
|
||||
class HTMLConverter(AbstractGenerator):
|
||||
formal_language = "html"
|
||||
searchpaths = [os.getcwd() + "/content/"]
|
||||
|
||||
@staticmethod
|
||||
@filter_method
|
||||
def children(group):
|
||||
return children(group)
|
||||
|
||||
@staticmethod
|
||||
@filter_method
|
||||
def id_path(element):
|
||||
return '-'.join(reversed(ancestry(element)))
|
||||
|
||||
@staticmethod
|
||||
@filter_method
|
||||
def split(string, delim):
|
||||
return string.split(delim)
|
||||
|
||||
@staticmethod
|
||||
@filter_method
|
||||
def occurs_text(occurs):
|
||||
words = {
|
||||
0: "Zero",
|
||||
1: "One",
|
||||
None: "Many"
|
||||
}
|
||||
return "Appears " + words[occurs[0]] + " To " + words[occurs[1]] + " " + ("Time" if occurs[1] == 1 else "Times")
|
||||
|
||||
def update_filters(self, filters):
|
||||
self._env.filters.update(filters)
|
||||
|
||||
|
||||
class XMLSchema(AbstractSchemaItem):
|
||||
extensions = ('xsd', 'xml')
|
||||
|
||||
def load_metadata(self):
|
||||
with self.in_path.open(mode='r', encoding='utf-8') as file:
|
||||
file.readline()
|
||||
line = file.readline()
|
||||
if len(line.strip()) != 0 and '<!--' in line and '-->' in line:
|
||||
self.title = line.replace('<!--', '').replace('-->', '').strip()
|
||||
super(XMLSchema, self).load_metadata()
|
||||
|
||||
def render(self, **context):
|
||||
context.update({
|
||||
'page': self
|
||||
})
|
||||
with self.in_path.open(mode='r', encoding='utf-8') as file:
|
||||
schema = xmlschema.XMLSchema(file)
|
||||
converter = HTMLConverter(schema)
|
||||
converter.update_filters(self.env.filters)
|
||||
return converter.render('base/schema/xml/schema_base.jinja2', global_vars=context)[0]
|
||||
|
||||
|
||||
|
||||
|
||||
@ -1,43 +0,0 @@
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
|
||||
from htmlmin import minify
|
||||
from jinja2 import Environment
|
||||
from markdown import Markdown
|
||||
|
||||
|
||||
@dataclass
|
||||
class Page:
|
||||
sort_priority: int
|
||||
in_path: Path
|
||||
out_path: Path
|
||||
title: str
|
||||
description: str | None
|
||||
env: object
|
||||
|
||||
def __init__(self, path, environment, options):
|
||||
self.in_path = path
|
||||
self.env = environment
|
||||
md = Markdown(**options)
|
||||
with path.open('r', encoding='utf-8') as file:
|
||||
md.convert(file.read())
|
||||
self.sort_priority = int(md.Meta.get('sort-priority', '20')[0])
|
||||
self.title = md.Meta.get('title', (path.stem,))[0]
|
||||
self.description = md.Meta.get('description', [None])[0]
|
||||
outfile: Path
|
||||
try:
|
||||
outfile = Path("out/", path.relative_to(Path("content/pages/")).parent,
|
||||
md.Meta['out-file'][0] + '.html')
|
||||
except KeyError:
|
||||
outfile = Path("out/", path.relative_to(Path("content/pages/"))).with_suffix('.html')
|
||||
self.out_path = outfile
|
||||
|
||||
def render(self, **options):
|
||||
template = self.env.get_template(str(self.in_path.relative_to(Path("content/")).as_posix()))
|
||||
page_template = self.env.get_template("base/page_template.jinja2")
|
||||
rendered_string = page_template.render(content=template.render(**options), **options)
|
||||
|
||||
self.out_path.parent.mkdir(mode=511, parents=True, exist_ok=True)
|
||||
|
||||
with self.out_path.open(mode='w+', encoding='utf-8') as file:
|
||||
file.write(minify(rendered_string, **self.env.minify_settings))
|
||||
@ -1,44 +0,0 @@
|
||||
import json
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
|
||||
from htmlmin import minify
|
||||
from json_schema_for_humans.generate import generate_schemas_doc, copy_additional_files_to_target
|
||||
from json_schema_for_humans.schema.schema_importer import get_schemas_to_render
|
||||
from json_schema_for_humans.template_renderer import TemplateRenderer, _minify
|
||||
from json_schema_for_humans.generation_configuration import GenerationConfiguration
|
||||
|
||||
# noinspection PyUnresolvedReferences
|
||||
from lib.Page import Page
|
||||
|
||||
|
||||
@dataclass
|
||||
class Schema(Page):
|
||||
|
||||
config: GenerationConfiguration
|
||||
|
||||
def __init__(self, path, env, options):
|
||||
self.sort_priority = 10
|
||||
self.in_path = path
|
||||
self.config = options
|
||||
self.env = env
|
||||
with path.open() as file:
|
||||
self.title = json.load(file).get('title', path.stem)
|
||||
self.description = "Schema for a " + self.title + " in New Horizons"
|
||||
self.out_path = Path('out/schemas/', self.in_path.relative_to(Path("content/schemas/")).with_name(self.title.replace(" ", "_").lower()).with_suffix(".html"))
|
||||
|
||||
def render(self, **options):
|
||||
schemas = get_schemas_to_render(self.in_path, self.out_path, ".html")
|
||||
template_renderer = TemplateRenderer(self.config)
|
||||
template_renderer.render = lambda inter: self.template_override(template_renderer, inter, **options)
|
||||
generate_schemas_doc(schemas, template_renderer)
|
||||
copy_additional_files_to_target(schemas, self.config)
|
||||
|
||||
def template_override(self, template, intermediate_schema, **options):
|
||||
template.template.environment.filters.update(self.env.filters)
|
||||
rendered = template.template.render(schema=intermediate_schema, dumb=True, config=self.config, title=self.title + " Schema", **options)
|
||||
|
||||
if template.config.minify:
|
||||
rendered = minify(rendered, **self.env.minify_settings)
|
||||
|
||||
return rendered
|
||||
19
docs/lib/utils.py
Normal file
19
docs/lib/utils.py
Normal file
@ -0,0 +1,19 @@
|
||||
import re
|
||||
|
||||
|
||||
def camel_to_pretty(raw):
|
||||
return ' '.join(re.findall(r'[A-Z](?:[a-z]+|[A-Z]*(?=[A-Z]|$))', raw))
|
||||
|
||||
|
||||
def pretty_title(raw: str) -> str:
|
||||
if '_' in raw:
|
||||
return ' '.join(x[0].upper() + x[1:] for x in raw.split('_'))
|
||||
elif any(x.isupper() for x in raw):
|
||||
if raw[0].islower():
|
||||
new_raw = raw[0].upper() + raw[1:]
|
||||
else:
|
||||
new_raw = raw
|
||||
return camel_to_pretty(new_raw)
|
||||
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user