Difference between revisions of "WorldForge-ArtImport"

From Stormhalter
Jump to: navigation, search
(Finished initial documentation, uploaded images.)
Line 40: Line 40:
The following components types, and their syntax, exist as of March 2022 and are unlikely to change:
The following components types, and their syntax, exist as of March 2022 and are unlikely to change:


codeblock
    <staticcomponent>
</staticcomponent>


== Detailed Example ==
== Detailed Example ==
@Tenser#2262 on Discord has graciously provided us with a sample sprite sheet as an example for this process.
@Tenser#2262 on Discord has graciously provided us with a sample sprite sheet as an example for this process.
[[File:AnimatedCampFire.gif|thumb]]
This will result in the animated campfire to the right.


The sprites are contained in the second image to the right. This image uses the 100x100 size, with frames progressing along the x(left\right)-axis.
This will result in this animated campfire:<br>
[[File:Fire-Sheet.png|thumb]]
[[File:AnimatedCampFire.gif|inline]]
 
The sprites are contained in this sprite sheet. This image uses the 100x100 size, with frames progressing along the x(left\right)-axis.<br>
[[File:Fire-Sheet.png|inline]]
 
To generate our Terrain-External XML, we need to create a Terrain with a sprite consisting of multiple frames. We also need a unique ID and to decide on the cost and order.
To generate our Terrain-External XML, we need to create a Terrain with a sprite consisting of multiple frames. We also need a unique ID and to decide on the cost and order.


Line 57: Line 60:
The following XML snippet defines our Terrain:
The following XML snippet defines our Terrain:


<terrain id="2000" cost="1">
    <nowiki><terrain id="2000" cost="1">
      <!-- animated campfire from Tenser -->
      <sprite order="0" underpile=true>
       <texture>Korazail\Demo\fire-sheet</texture>
       <source>(0,0,500,100)</source> <!-- Note that the source here is the WHOLE thing. The frames are called out below. If you had only a single sprite, you would use something more like (0,0,100,100) for a 100x100px sprite. -->
        <frames step="100" keys="0-4"> <!--100ms between frames-->
         <frame>(0,0,100,100)</frame>
         <frame>(100,0,100,100)</frame>
         <frame>(200,0,100,100)</frame>
         <frame>(300,0,100,100)</frame>
         <frame>(400,0,100,100)</frame>
       </frames>
      </sprite>
      <average>(252,120,0,255)</average>
    </terrain></nowiki>


    <!-- animated campfire from Tenser -->
Now that we have our Terrain, lets add it to the bottom of the current Data\Terrain-External.xml and load up WorldForge.<br>


    <sprite order="0" underpile=true>
[[File:WFAI-Error1.png|inline]]<br>
Uh-oh. We had an issue in our XML. WorldForge exposes XML parsing errors as a popup during start. Depending on where an error occurs, either the whole custom art Terrain-External.xml is skipped or the specific Terrain is skipped. In the latter case, any components referencing that terrain will still load, but the art will be missing. Let me fix my typo by putting quotes around the true for the underpile attribute...


      <texture>Korazail\Demo\fire-sheet</texture>
[[File:WFAI-Error2.png|inline]]<br>
WorldForge starts with a different complaint. I forgot to place the sprite sheet in the folder. Let me go fix that...<br>
[[File:WFAI-NewTextures.png|inline]]


      <source>(0,0,500,100)</source> <!-- Note that the source here is the WHOLE thing. The frames are called out below. If you had only a single sprite, you would use something more like (0,0,100,100) for a 100x100px sprite. -->
WorldForge now starts without complaint - though it does alert us that it made a change to a special file. This file is used when Stormhalter is compiled. It pre-processes all the art assets into the various .BIN files that are distributed to the client. We need to ensure that our new art is sent to clients, or they won't see it. If we decide to rename the sprite sheet file, there is a similar message that reminds us to remove the other filename from this file.<br>
[[File:WFAI-Texture.png|inline]]


      <frames step="100" keys="0-4"> <!--100ms between frames-->
        <frame>(0,0,100,100)</frame>
        <frame>(100,0,100,100)</frame>
        <frame>(200,0,100,100)</frame>
        <frame>(300,0,100,100)</frame>
        <frame>(400,0,100,100)</frame>
      </frames>
    </sprite>
    <average>(252,120,0,255)</average>
  </terrain>
Now that we have our Terrain, lets add it to the bottom of the current Data\Terrain-External.xml and load up WorldForge.
[[File:WFAI-Error1.png|thumb]]
Uh-oh. We had an issue in our XML. WorldForge exposes XML parsing errors as a popup during start. Depending on where an error occurs, either the whole custom art Terrain-External.xml is skipped or the specific Terrain is skipped. In the latter case, any components referencing that terrain will still load, but the art will be missing.
[[File:WFAI-Error2.png|thumb]]
After fixing our typo by putting quotes around the true, WorldForge starts with a different complaint. I forgot to place the sprite sheet in the folder. Let me go fix that...
[[File:WFAI-NewTextures.png|thumb]]
WorldForge now starts without complaint - though it does alert us that it made a change to a special file. This file is used when Stormhalter is compiled. It pre-processes all the art assets into the various .BIN files that are distributed to the client. We need to ensure that our new art is sent to clients, or they won't see it. If we decide to rename the sprite sheet file, there is a similar message that reminds us to remove the other filename from this file.
[[File:WFAI-Texture.png|thumb]]
We can see our new campfire! WorldForge does not currently animate sprites, so we only see the first frame.
We can see our new campfire! WorldForge does not currently animate sprites, so we only see the first frame.


Next, let's define it as a Component. This is a simple one. This component doesn't do anything, can be interacted with and is thus a StaticComponent. Let's put it in the Decorations category, right at the top, before the Boxing Ring:
Next, let's define it as a Component. This is a simple one. This component doesn't do anything, cannot be interacted with and is thus a StaticComponent. Let's put it in the Decorations category, right at the top, before the Boxing Ring:
 
    <nowiki>...
...
    <category name="Decorations">
 
    <StaticComponent name="Animated Campfire">
  <category name="Decorations">
      <static>2000</static>
 
     </StaticComponent>
    <StaticComponent name="Animated Campfire">
      <StaticComponent name="Boxing Ring">
 
    ...</nowiki>
      <static>2000</static>
 
    </StaticComponent>


    <StaticComponent name="Boxing Ring">
If we had made any typos in the Components.xml, those would have been shown to us at load time as well, but we didn't, so WorldForge loads without issue and we can see our new campfire is a Decoration.<br>
[[File:WFAI-Component.png|inline]]


...
Let's take a look at our GitHub Desktop changes tab to see what we did.<br>
[[File:WFAI-Component.png|thumb]]
[[File:WFAI-GitHub.png|inline]]
If we had made any typos in the Components.xml, those would have been shown to us at load time as well, but we didn't, so WorldForge loads without issue and we can see our new campfire is a Decoration.
[[File:WFAI-GitHub.png|thumb]]
Let's take a look at our GitHub Desktop changes tab to see what we did.


A new file, and a few new lines to the Terrain-External and Components xml files and a modification to that Content.Stormhalter-External.mgcb to include our new art in the .BIN files. We're good to commit this and then submit a Pull Request. Since multiple artists might be working at the same time, and there are only three files that define the contributed art, there is potential for merge conflicts, though they should be straightforward to resolve.
A new file, and a few new lines to the Terrain-External and Components xml files and a modification to that [Content.Stormhalter-External.mgcb] to include our new art in the .BIN files. We're good to commit this and then submit a Pull Request. Since multiple artists might be working at the same time, and there are only three files that define the contributed art, there is potential for merge conflicts, though they should be straightforward to resolve.

Revision as of 21:21, 19 March 2022

WorldForge Art import workflow and tutorial.

When WorldForge starts for the first time, it creates a subfolder in the current directory called ".storage". It caches some files there so that it can be used if the server is offline, but it also creates a configuration file to enable GitHub integration so artists can develop new terrain sprites and test them before creating pull requests.

Before beginning this process, ensure you have cloned the Stormhalter repo using a git tool like GitHub Desktop. If you need help with this process, consult the Stormhalter Discord.

To configure WorldForge, open the [.storage\CustomArt.cfg] file and modify the first line to point to the Content folder of your local repo. If you wish to disable GitHub integration, simply delete this file and it will be regenerated with the default path.

Once configured, WorldForge will now prefer the \Data\Terrain-External.xml and \WorldForge\Components.xml files when loading, over the ones in the .Bin files shipped with Stormhalter. You can make edits to these files and they will be reflected in the editor.

At this time, the only way to reload these files is to restart WorldForge entirely. A "hot-reload" feature should be forthcoming.

Overview

The process for importing new art has three major steps:

  1. Create sprite sheets or individual sprites as .png images
  2. Create Terrain definitions in Data/Terrain-External.xml
  3. Create Component definitions in WorldForge\Components.xml

Creating Sprite Sheets

Actually creating the sprites is not in scope for this document. The relevant details though, are that the sprites need to be either 55x55px (flat floor tiles) or 100x100px(any tile with depth that overlaps another tile). Sprites need to be saved as PNG files, and the engine supports the alpha channel for transparency. Multiple sprites can be in a single image, the Terrain XML definitions will define the region of the sprite sheet to be used.

Updating Terrain-External.xml

Review the comment at the top of Terrain-External.xml. Be aware of the details for cost, order, and underpile, as these have effects on gameplay.

The texture element is a relative path from the Content folder to the sprite sheet, but does not contain the file extension: [Korazail\Terrain\floors] would cause WorldForge to look for a sprite sheet at [Content\Korazail\Terrain\floors.png].

The source element is in the format (left, top, width, height). If you juxtapose the left and top coordinates, as long as your sprite sheet is not square, WorldForge will alert at startup that the source is out of bounds.

The average element is an approximate color of the tile if you squint at it hard from a long way away. It is used for making minimaps, though none are exposed to players at this time.

You will need to come up with unique Terrain IDs for your tiles. You can see the existing used IDs by looking at the Static dropdown in the WorldForge Components picker. At the time of this writing, Tenser is using 622-784 and is a regular contributor. There are thousands of available IDs, so give other contributors some padding when choosing your ranges. If you happen to collide with an existing Terrain ID, Worldforge will alert at startup.

Your terrain XML elements can go at the end of the file, or in order with existing Terrain IDs.

Updating Components.xml

Unlike the Terrain-External.xml file, the Components XML has structure and a more complicated syntax. Under the Data XML Node, there are several Category nodes. These are used for grouping in WorldForge and the names are free-form. A component could exist in multiple categories, though they would have to be manually kept in sync in the case of changes. We want to avoid category bloat, but if your components don't fit into an existing category, you can create a new one. New categories should be based on either a theme or a function, not a contributor; a world builder will be looking for either a specific functional component, like a door, or a palette to build with, like 'Nomad tents.'

Individual components are a way to put a name to a Terrain element, along with associated details. For example, a Door is really three distinct terrains: open door, closed door, destroyed door. the DoorComponent links the three Terrains into one object a world builder can place in the game.

The following components types, and their syntax, exist as of March 2022 and are unlikely to change:

   <staticcomponent>

</staticcomponent>

Detailed Example

@Tenser#2262 on Discord has graciously provided us with a sample sprite sheet as an example for this process.

This will result in this animated campfire:
inline

The sprites are contained in this sprite sheet. This image uses the 100x100 size, with frames progressing along the x(left\right)-axis.
inline

To generate our Terrain-External XML, we need to create a Terrain with a sprite consisting of multiple frames. We also need a unique ID and to decide on the cost and order.

For Cost, this is a normal floor tile. Despite being a fire, it won't hurt entities who step on it, as tiles are considered to be several feet wide. Plenty of room to walk around the fire. Lets leave this at a cost of 1, though 2 could be acceptable if we consider navigating around the hazard to be slower than walking through a normal floor.

For Order, we want this tile to be rendered over floors, bur under any doors that happen to open onto a campfire tile. This puts us at an Order of 0. When working with Order 0 sprites, we have to consider underpile, which decides whether the treasure sprite is rendered before or after our new sprite. In this case, treasure should probably be rendered on TOP of the campfire. so it is obvious. This defies physics somewhat, but makes gameplay sense. For this terrain, we will want an Underpile of true. Perhaps we could split this fire into two sets of sprites, and have a smoke sprite with an order of 6, so rendered over the pile and crits, but contained all in a single terrain. The possibilities!

The following XML snippet defines our Terrain:

   <terrain id="2000" cost="1">
      <!-- animated campfire from Tenser -->
      <sprite order="0" underpile=true>
        <texture>Korazail\Demo\fire-sheet</texture>
        <source>(0,0,500,100)</source> <!-- Note that the source here is the WHOLE thing. The frames are called out below. If you had only a single sprite, you would use something more like (0,0,100,100) for a 100x100px sprite. -->
        <frames step="100" keys="0-4"> <!--100ms between frames-->
          <frame>(0,0,100,100)</frame>
          <frame>(100,0,100,100)</frame>
          <frame>(200,0,100,100)</frame>
          <frame>(300,0,100,100)</frame>
          <frame>(400,0,100,100)</frame>
        </frames>
      </sprite>
      <average>(252,120,0,255)</average>
    </terrain>

Now that we have our Terrain, lets add it to the bottom of the current Data\Terrain-External.xml and load up WorldForge.

inline
Uh-oh. We had an issue in our XML. WorldForge exposes XML parsing errors as a popup during start. Depending on where an error occurs, either the whole custom art Terrain-External.xml is skipped or the specific Terrain is skipped. In the latter case, any components referencing that terrain will still load, but the art will be missing. Let me fix my typo by putting quotes around the true for the underpile attribute...

inline
WorldForge starts with a different complaint. I forgot to place the sprite sheet in the folder. Let me go fix that...
inline

WorldForge now starts without complaint - though it does alert us that it made a change to a special file. This file is used when Stormhalter is compiled. It pre-processes all the art assets into the various .BIN files that are distributed to the client. We need to ensure that our new art is sent to clients, or they won't see it. If we decide to rename the sprite sheet file, there is a similar message that reminds us to remove the other filename from this file.
inline

We can see our new campfire! WorldForge does not currently animate sprites, so we only see the first frame.

Next, let's define it as a Component. This is a simple one. This component doesn't do anything, cannot be interacted with and is thus a StaticComponent. Let's put it in the Decorations category, right at the top, before the Boxing Ring:

   ...
    <category name="Decorations">
      <StaticComponent name="Animated Campfire">
        <static>2000</static>
      </StaticComponent>
      <StaticComponent name="Boxing Ring">
    ...

If we had made any typos in the Components.xml, those would have been shown to us at load time as well, but we didn't, so WorldForge loads without issue and we can see our new campfire is a Decoration.
inline

Let's take a look at our GitHub Desktop changes tab to see what we did.
inline

A new file, and a few new lines to the Terrain-External and Components xml files and a modification to that [Content.Stormhalter-External.mgcb] to include our new art in the .BIN files. We're good to commit this and then submit a Pull Request. Since multiple artists might be working at the same time, and there are only three files that define the contributed art, there is potential for merge conflicts, though they should be straightforward to resolve.