Mod: 基础

此页面的内容需要被翻译。

你可以帮助我们来翻译此页面。

模组页面

This page aims to **briefly** go through all the basic concepts of Noita modding: ECS, the directory structure, Lua hooks, etc.

## Mod installation locations

The two common mod locations are:

* `Noita/mods/` - Use this for manually downloaded mods or mod development.
* `steamapps/workshop/content/881100/` - Mods that you've downloaded through Steam Workshop can be found here.

## Mod directory structure

This is an example mod, that shows the typical file structure you should follow.

```
myAwesomeMod/
├── data/
│   └── enemies_gfx/
│       └── player.png
├── files/
│   └── my_custom_script.lua
├── init.lua
└── mod.xml

```

Lets break it down a bit:

* `myAwesomeMod/`
	+ This is the root folder, the base path of your mod. Choose this carefully, as you will have to reference it all over the code later on. Note that this is **not** the visible "name" of the mod.
* `data/`
	+ This folder follows exactly the same structure as the Noita's data files that we extracted earlier. Use this **ONLY** when you want to override base assets.
* `enemies_gfx/`
	+ A folder that gathers together all enemy & friendly spritesheets
* `player.png`
	+ The player animation spritesheet (minä, the purple witch). Inside the data/ folder this is directly replacing the base game spritesheet when this mod is activated.
* `files/`
	+ This folder is for any and all custom assets/scripts/data you want to include in your mod, should be used when you don't want to directly replace existing assets (ie. almost always)
* `my_custom_script.lua`
	+ A custom script file, which could be doing literally anything or be called from anywhere. Try to name yours a bit better than this example.
* `init.lua`
	+ The starting point of your mod, includes hooks for post-/pre- world init, player spawn, etc. This is where you should probably start with a "Hello world"
* `mod.xml`
	+ The mod metadata definition file, includes the name, description and bunch of other settings.

### Referencing files in different paths

As noted above, the `data/` and `files/` folders behave very differently from eachother. Due to this, you reference files in them slightly differently aswell. The `data/` can essentially be considered as its own virtual filesystem inside Noita which can be referenced directly, but everything else needs to have your full mod path in it. For example including files in Lua, with the above structure:

```
-- Partial path for data files (also when overwritten and even if it's not included in \*your\* own mod)
dofile\_once("data/scripts/lib/utilities.lua")

-- Full path for own scripts
dofile\_once("mods/myAwesomeMod/files/scripts/my\_custom\_script.lua")

```
The same exact thing applies in XML aswell when defining paths (eg. spritesheets, projectiles, effects, anything)

## The Entity Component System basics

Entities are based on a couple of simple rules:

* Can contain any number of components, even same ones
* Can have child entities, defining their own components
* Can inherit from other entities, copying over their functionality
* Can have only one name
* Can have multiple tags

### Example

Lets break down an example entity, from the base game file: `data/entities/props/banner.xml`

```
<Entity tags="prop">

  <VelocityComponent />

  <SimplePhysicsComponent />

  <SpriteComponent
    z\_index="1"
    image\_file="data/props\_gfx/banner.xml"
    offset\_x="0"
    offset\_y="0"
  ></SpriteComponent>
</Entity>

```
This is one of the simplest entities you can find, composed of only three components inside it. If spawned in-game, you should see a simple animated banner flag dropping to the ground, without any special functionality.

* `VelocityComponent` is generally required for anything dealing with physics. It can define a number of things (eg. gravity for individual entities), but here we simply initiate it with default values. You can find all of the values in `component_documentation.txt`.
* `SimplePhysicsComponent` gives the entity very simple physical properties. It'll always fall down, stop at any walls and it generally cannot collide with proper physics objects, nor can it be kicked. For proper physics, you'd want a PhysicsBodyComponent alongside with PhysicsShapeComponent, you can find many different examples of this in the data files.
* `SpriteComponent` simply attaches an image file to the entity, with any given offsets and such. Here an XML file is defined as the source for the image, which is required when you want to add animated sprites to an entity. If your sprite is a still image, you can reference it directly here aswell.

Lets take a look at the image file `data/props_gfx/banner.xml` next:

```
<Sprite
  filename="data/props\_gfx/banner.png"
  offset\_x="12"
  offset\_y="30"
  default\_animation="stand">

  <!-- stand -->
  <RectAnimation
    name="stand"
    pos\_x="0"
    pos\_y="0"
    frame\_count="7"
    frame\_width="32"
    frame\_height="32"
    frame\_wait="0.11"
    frames\_per\_row="7"
    loop="1"
  ></RectAnimation>
</Sprite>

```
These separate metadata files are essentially outside of the Entity Component System, and simply define the sprite metadata in the same XML format as everything else. Thus no documentation can be found for the XML elements in the component\_documentation.txt, because these are not components nor entities. But lets break this down aswell:

* Here we finally reference the actual image file we want to draw

* The main `<Sprite>` element has only one `<RectAnimation>` child element, it could have multiple of these, one for each animation (normal for any characters)
* The animation name is "stand", and it's referenced as the default one aswell. The names can be anything, but it has to match with whatever's using them.
* The `frame_*` attributes are quite self-explanatory, but basically the most essential part when defining animations. These have to match with the spritesheet in your image file.

You can go take a look at the file `data/props_gfx/banner.png`, and this all should become fairly obvious.

## Creating and deleting entities

There are a multitude of ways in which you can spawn/kill entities. The simplest way is directly with Lua:

* Spawn with `EntityLoad("path/to/entity.xml" , x, y)`
* Delete with `EntityKill(entity_id)`

Just as an example, other possible spawn/kill places include:

* `ProjectileComponent::spawn_entity` can be used to spawn any entity upon projectile collision
* `CameraBoundComponent` can be used to limit frequently used entities' spawn rates / distances (eg. enemies, lanterns, ...)
* `LifetimeComponent` literally gives an entity a certain time to live
* `DamageModelComponenet` enables hitpoints and taking damage for the entity. Entity is killed upon reaching 0 health.
* ...and many more!

## Tags

Entities and components *both* can have any number of tags attached to them. Tags are a very simple way to categorize one type of "thing", and can be easily created on the fly without any extra definitions. For example, fetching a list of all enemies around the player is this easy: `EntityGetWithTag("enemy")`.

Most tags don't have anything else to them, but there is a group of special tags which have in-engine hard-coded functionality attached to them.

**Notes:**

* Entity tags are defined in the `tags` attribute, while Component tags are defined in the `_tags` attribute.
* The engine currently supports only a very limited amount of **custom tags**, which is global across all mods. This is important to keep in mind for inter-mod compatibilty.
	+ Via community testing, the upper limits seem to be currently be about `tags`=224 and `_tags`=210
* The special tags are **integral** to whole Entity-Component system of Noita, so make sure to read through that page

## Entity inheritance

* Implemented via the `Base` component
	+ Base entity filepath in the `file` attribute
* Override base entity components by defining new ones **inside** the Base element tags
	+ When overriding, the Base entity **must** have the components defined that you are trying to override. Otherwise an error will occur.
* Your own additions go normally outside the Base element

* You can inherit from as many Base entities as you like
* All entity tags will be bunched together

## init.lua

The first piece of code that is run for any mod. Includes pre-determined function names you can use for hooking into certain events and provides a place to gather together all your mod's file overrides.

The following bits are taken almost straight from `mods/example/init.lua`. All of the code is optional, you can include only the parts that you need.

### Available function hooks

```
-- Called in order upon loading a new(?) game:
function OnModPreInit() end
function OnModInit() end
function OnModPostInit() end

-- Called when player entity has been created. Ensures chunks around the player have been loaded & created.
function OnPlayerSpawned(player\_entity) end

-- Called when the player dies
function OnPlayerDied( player\_entity ) end

-- Called once the game world is initialized. Doesn't ensure any chunks around the player.
function OnWorldInitialized() end

-- Called \*every\* time the game is about to start updating the world
function OnWorldPreUpdate() end

-- Called \*every\* time the game has finished updating the world
function OnWorldPostUpdate() end

-- Called when the biome config is loaded.
function OnBiomeConfigLoaded() end

-- The last point where the Mod API is available. After this materials.xml will be loaded.
function OnMagicNumbersAndWorldSeedInitialized() end

-- Called when the game is paused or unpaused.
function OnPausedChanged( is\_paused, is\_inventory\_pause ) end

-- Will be called when the game is unpaused, if player changed any mod settings while the game was paused
function OnModSettingsChanged() end

-- Will be called when the game is paused, either by the pause menu or some inventory menus. Please be careful with this, as not everything will behave well when called while the game is paused.
function OnPausePreUpdate() end

```

### Overriding and extending systems

These lines are usually added to the end/beginning of `init.lua`. This code runs when all mods' filesystems are registered.

```
-- Basically dofile("mods/mymod/files/actions.lua") will appear at the end of gun\_actions.lua
ModLuaFileAppend("data/scripts/gun/gun\_actions.lua", "mods/mymod/files/actions.lua")

-- Same, but for potions
ModLuaFileAppend("data/scripts/items/potion.lua", "mods/mymod/files/potion\_appends.lua" )

-- Will override some magic numbers using the specified file
ModMagicNumbersFileAdd("mods/mymod/files/magic\_numbers.xml")

-- Append your own materials to the game's material list
ModMaterialsFileAdd("mods/mymod/files/custom\_materials.xml")

-- Use this to register custom fmod events. Event mapping files can be generated via File -> Export GUIDs in FMOD Studio.
ModRegisterAudioEventMappings("mods/mymod/files/audio\_events.txt")

```

## Important and interesting files

### Good to go through

`magic_numbers.xml`

* A lot of variables to control the behaviour of many aspects of the game and gameplay. From virtual resolution (for controlling the amount of world you see) to the amount of blood animals spill, good to go through.

`data/scripts/lib/utilities.lua`

* A lot of utility functions ready-made by Nolla. Includes helpers for a lot of basic 2D platformer math, the ECS and more. Good read through to get an idea how things work and not re-implement basic things many times.

### Probably not too useful starting out

`genome_relations.csv`

* A table of how all the game's animals (including the player) feel toward eachother.
* Can be edited freely; eg. make everyone friendly towards the player.

`translations/common.csv`

* Includes all the basic translations for the game. Good for cross-linking certain spell names, because most of the text seen in-game is not actually present in the code, but here.
* Can also be edited freely, if you want to eg. rename/rewrite any spells or parts of the game.

`scripts/wang_scripts.csv`

* "Global" biome function registration. Any names defined here are available to all wang tiles and pixel scenes.
* Note: All biome will still need an implementation for these functions separately.
* Can be edited freely to add your own.

`post_final.frag`

* The "main" OpenGL shader file, can be edited and appended freely to create your own shader effects. Not recommended without a prior experience of programming / shaders.

### Lists of interest

* Materials: `data/materials.xml`
* Perks: `data/scripts/perks/perk_list.lua`
* Potions: `data/scripts/items/potion.lua`
* Spells: `data/scripts/gun/gun_actions.lua`
* Status effects: `data/scripts/status_effects/status_list.lua`
* See above section on init.lua for how to extend these

## Debugging

Noita does not currently have any sort of dedicated IDE / testing environment for development. A lot of the development time will indeed be spent just restarting the game to try out new changes. Below is a list of the most common ways for debugging Noita mods:

1. The development build `noita_dev.exe`:
	* Starts with a development console. This is the best way to directly find out any errors in your Lua / XML as they happen.
	* The executable should be located in `tools_modding/`, but can only be started from the root folder (ie. next to the `noita.exe`). If you've followed instructions so far, it should already be there.
	* Press `F1` to show a list of keybinds. Press `F5` to enable most debugging features.
	* Required for seeing `print()` output in the console, but `GamePrint()` still works anywhere (rendered in-game on the lower left corner).
	* Note: This is known to decrease performance for many people. So it's totally fine to switch between the two builds frequently.
2. Logging to file:
	* Enable via specific magic numbers.
	* Or download a nice mod which does this for you: Modworkshop
	* Logs to `Noita/logger.txt`
3. CheatGUI Mod:
	* Great for testing gameplay features, without fiddling with any debug features.
	* Spawn items/perks/wands, teleport, increase health, etc. All straight from in-game HUD
	* Download: [Steam Workshop | Github]
4. Noita also supports the Decoda Lua debugger:
	* Needs separate setup. Instructions found in `tools_modding/lua_debugging.txt`

**Note:** The game is supposed to detect when any mod files change and consequently hard-restart the game upon starting a new game, but this is known to not work too reliably currently. Thus for now it's advised to always restart your game entirely via manually exiting / killing the game first.