Modding: Difference between revisions

From APICO Wiki
Jump to navigation Jump to search
Content added Content deleted
No edit summary
No edit summary
 
(48 intermediate revisions by the same user not shown)
Line 1: Line 1:
<div class="aw">
This is a WIP page that I'm using to document the game's modding API as I go


<div class="aw-panel">
Modder application form:
https://forms.gle/gaqa4S6jUcoxePrA6


<div class="aw-panel--left">
Mods for APICO are written in [https://www.lua.org/ LUA].
<p>In APICO, you can add mods to expand your game and add new mechanics and features not available in the base game! If you're interested, you can even [[Modding Guide | start modding the game]] yourself!</p>
All standard LUA libraries are available except <span class="aw-inline--code">io</span> and <span class="aw-inline--code">os</span> for security reasons. If there is something you specifically need let us know and we can look into a way of exposing it through the API! The LUA interface we use into GameMaker is [https://yal.cc/r/17/lua/ Apollo].
<p>Here you can find all the general information and resources around mods and modding.<br/>You can find a list of all mods on the [[Mod List]] page.</p>
</div>


<div class="aw-panel--right">
When creating a mod you will need to decide on a unique mod ID, which is a lowercase name with no spaces (underscores are accepted) - this will be used as not only your mod directory folder within the game but also for identifying your game in various debugging (so we can see which one of you keeps breaking stuff :P).
<div class="aw-info">
<p class="h"><i>Modding</i></p>
<p class="sh">Mods for APICO!</p>
</div>
</div>


</div>
In all the documentation examples, we use <span class="aw-inline--code">sample_mod</span> which is also the name of the sample mod you can download to test out the various API methods.


<h1 id="Downloading Mods">[[File:Microscope_Item.png|32px]]Downloading Mods</h1>
<h1>Current Mods</h1>
<p>If you're playing APICO and want to download mods, you can do it right from inside the game! From the home screen you can click on "Mods" and the game will retrieve all the approved mods that are available. Once downloaded you can choose to activate or deactivate any of your mods before starting your game. Mods are not available in the [[Demo]].<br/><br/>[[File:Mods_Page_2.png|600px]]<br/><br/>You can also install mods not from the approved list by downloading them from the main [[Mod List]], and using the [[Mod Sideloading|Sideloader]].<br/><b>We cannot recommend these mods for security reasons so install unapproved mods at your own risk!</b></p>
sample_mod by @ellraiser


<h1 id="Creating Mods">[[File:Stone_Hammer_Item.png|32px]]Creating Mods</h1>
<h1>Glossary</h1>
<p>If you want to start creating mods, you can read our [[Modding Guide | Getting Started]] and get stuck in!<br/> To make your mod "official" you will need to upload it to [https://apico.mod.io/ mod.io] via our [https://apico-modloader.herokuapp.com/ APICO Modloader] tool and get it approved. Although players can choose to side-load APICO with any mod they download, they will get shown a warning around the mod when activated.</p>
<b>Dictionary</b>
<p>In the game every single item, object, machine, NPC, or button has a definition in the Dictionary. If there is not a definition the game will not recognise what you are trying to reference.
<br/><br/>
You can add definitions through the <span class="aw-inline--code">api_define_*()</span> methods.
</p>
<br/>
<b>Modding Console</b>
<p>The Modding Console is a debugging tool available by pressing <span class="aw-inline--code">.</span>, and it shows all messages from both the Modding API itself as well as any logs or errors from individual mods</p>
<br/>
<b>In-Game Console</b>
<p>When enabled, the In-Game Console is an inline command runner you can use while playing the game by typing <span class="aw-inline--code">/</span> followed by a command.
<br/><br/>
For example to spawn in 5 logs you'd use <span class="aw-inline--code">/gimme log 5</span>
</p>


<h1 id="Modding API">[[File:Book1_Item.png|32px]]Modding API</h1>
<p>If you've read out [[Modding Guide | Getting Started]] guide and are eager to get modding, check out our [[Modding API | API Reference]] which details all API methods along with example LUA code for a bunch of different use-cases.</p>
<p>You can also check out our [https://github.com/APICO-Modders APICO Modders Github] for ideas and inspiration!</p>


<h1>Getting Started</h1>
<p>To get started you just need your favourite IDE! You don't need to compile your LUA code in advance, the game does this when it loads your mod, so outside of any LUA extensions you might find useful you don't need any other resources<br/><br/>When modding is properly live, a player will be able to see all mods from the "Modding" section thats accessible from the home screen. They'll be able to pick the mods they want and download them, which will save the mod into the /mods directory, as well as update their settings.json file. For now you'll need to do this manually.<br/><br/>For example if your mod is called "sample_mod", you'll need to set the "downloaded_mods" key inside settings.json as ["sample_mod"], as well as add your mod in a folder called "sample_mod". Your directory will look like this:</p>

<img/>

<p>If done correctly, when you go to the "Modding" section you'll see your mod in the right-hand section, just inactive.Pressing the "tick" button will activate the mod, and now any time you start a game you'll see a "Loading mods..." screen</p>


<h1>Mod Structure</h1>
<p>For mods to work they need to follow a basic structure so that APICO can register and load mods correctly, as well as handle any errors that might occur when trying to compile your code.<br/><br/>Every mod needs to start with a <span class="aw-inline--code">mod.lua</span> file at the root of your mod folder. This is the main file that initially gets loaded - a basic mod file would look something like this:</p>

<div class="aw-code">mod_id = <span class="cs">"sample_mod"</span><br/><br/><span class="ck">function</span> <span class="cf">register</span>()<br/>&nbsp;&nbsp;<span class="ck">return</span> mod_id<br/><span class="ck">end</span><br/><br/><span class="ck">function</span> <span class="cf">init</span>(<span class="cp">working_directory</span>)<br/>&nbsp;&nbsp;<span class="cf">api_create_log</span>(<span class="cs">"init"</span>, <span class="cs">"Hello world!"</span>)<br/><span class="ck">end</span><span class="ck">function</span> <span class="cf">clock</span>()<br/>&nbsp;&nbsp;<span class="cf">api_create_log</span>(<span class="cs">"clock"</span>, <span class="cs">"1 second has passed!"</span>)<br/>&nbsp;&nbsp;<span class="ck">return</span> <span class="cs">"Success"</span><br/><span class="ck">end</span></div>

<p>When the game loads, it'll retrieve the player's mod settings to get a list of all active mods. It will then try and init these mods through the <span class="aw-inline--code">sc_init_mod()</span> method that you'll see in the Modding Console.<br/><br/>This method will first try and call a <span class="aw-inline--code">register()</span> method in your mod, which will check for duplicate names, and will then call an <span class="aw-inline--code">init()</span> method.<br/><br/>Your <span class="aw-inline--code">init()</span> method is a place for you to setup all mod code you need at the start, and needs to return either <span class="aw-inline--code">Success</span> or an error message - this lets the game know if your mod loaded correctly.<br/><br/>If there are any errors while trying to define an item you'll see one of the following codes logged in the Modding Console</p>

<div class="aw-code">{{Error|err=Mod Failed To Init|desc=This means your mods code could not be compiled correctly! You'll also see related LUA errors logged after this error message that should help narrow down the issue}}<br/>{{Error|err=Duplicate Mod Registered|desc=This means the mod id you have provided is already registered by the game, try a different mod id!}}<br/>{{Error|err=Mod Failed To Load|desc=This is called if your init() method does not return "Success", allowing you to debug your mods setup}}</div>


<h1>General Notes</h1>
<p>The game completely trusts the save file when it loads - because of this your players save file will load with any mod content in the save (custom items, objects, etc). If it can't then find these items in the dictionary you'll end up with a bunch of pink cubes - basically undefined instances.</p>

<h1>API Reference</h1>

<h2>Set Methods</h2>

<h3>api_set_devmode()</h3>
<p>This turns on Dev Mode, which allows you to run commands through the In-Game Console</p>

<div class="aw-code">{{Method|method=api_set_devmode|param1=devmode}}{{Parameter|param=devmode|datatype=Boolean|desc=whether to set devmode to truefalse}}{{Return|datatype=Nil}}</div>

<p>Example LUA code to turn on dev mode:</p>
<div class="aw-code"><span class="cf">api_enable_dev</span>(<span class="cl">true</span>)</div>

<p>There are no associated errors with this method</p>

<h2>Create Methods</h2>

<h3>api_create_log()</h3>
<p>Allows you to log messages to the Modding Console</p>

<div class="aw-code">{{Method|method=api_create_log|param1=func|param2=message}}{{Parameter|param=func|datatype=String|desc=the name of the method calling this log, shown in the console}}{{Parameter|param=message|datatype=String|desc=the message to show in the console}}{{Return|datatype=Nil}}
</div>
</div>

<p>Example LUA code to log "Hello World!" to the Modding Console:</p>
<div class="aw-code"><span class="cf">api_log</span>(<span class="cs">"my_method"</span>, <span class="cs">"Hello World!"</span>)</div>

<p>There are no associated errors with this method</p>

<h3>api_create_item()</h3>
<p>Allows you to create an item at a specific point. The item needs to already exist in the Dictionary, so if you are trying to spawn in an item from your mod be sure to use <span class="aw-inline--code">api_define_item()</span> first.</p>

<div class="aw-code">{{Method|method=api_create_item|param1=item_id|param2=amount|param3=x|param4=y}}{{Parameter|param=item_id|datatype=String|desc=the item_id of the item to spawn, must be in the Dictionary}}{{Parameter|param=amount|datatype=Integer|desc=the amount of the item to spawn}}{{Parameter|param=x|datatype=Integer|desc=the x position to spawn the item}}{{Parameter|param=y|datatype=Integer|desc=the y position to spawn the item}}{{Return|datatype=Integer|desc=either returns the created item instance ID or Nil if it fails}}
</div>

<p>Example LUA code to spawn 5 planks at a specific point:</p>
<div class="aw-code"><span class="cf">api_create_item</span>(<span class="cs">"planks1"</span>, <span class="cs">5</span>, <span class="cs">200</span>, <span class="cs">300</span>)</div>

<p>If this method fails you'll see the following errors in the Modding Console:</p>

<h3>api_create_object()</h3>
<p>Allows you to create an object instance at a specific point. The object needs to already exist in the Dictionary, so if you are trying to spawn in an object from your mod be sure to use <span class="aw-inline--code">api_define_object()</span> first.</p>

<div class="aw-code">{{Method|method=api_create_object|param1=object_id|param2=x|param3=y}}{{Parameter|param=object_id|datatype=String|desc=the object_id/oid of the item to spawn, must be in the Dictionary}}{{Parameter|param=x|datatype=Integer|desc=the x position to spawn the item}}{{Parameter|param=y|datatype=Integer|desc=the y position to spawn the item}}{{Return|datatype=Integer|desc=either returns the created object instance ID or Nil if it fails}}
</div>

<p>Example LUA code to spawn a tree at a specific point:</p>
<div class="aw-code"><span class="cf">api_create_object</span>(<span class="cs">"tree"</span>, <span class="cs">50</span>, <span class="cs">50</span>)</div>

<p>If this method fails you'll see the following errors in the Modding Console:</p>

<div class="aw-code">{{Error|err=Failed To Create Object|desc=This means the object could not be created, most likely an incorrect object_id, or badly defined object definition for the given object_id}}</div>

<h2>Define Methods</h2>

<h3>api_define_item()</h3>
<p>
This allows you to define a new Item for the game and add it to the Dictionary
<br/><br/>
This is needed before you can spawn the item in, add it to recipes, or use it with machines.
<br><br>
The sprite you use needs to be a 60x16px image within your mods resources, made up of 4 frames (normal, highlighted, undiscovered, undiscovered + highlighted). The sprite will be loaded virtually and used automatically when drawing your item.
<br/><br/>
Once you've created an item with this method you'll be able to access your sprite reference with <span class="aw-inline--code">TODO</span> if you need it during a custom draw call. You'll also be able to directly get the definition from the dictionary with <span class="aw-inline--code">TODO</span>
</p>

<div class="aw-code">{{Method|method=api_define_item|param1=definition|param2=sprite_image}}{{Parameter|param=mod_name|datatype=String|desc=the name of your mod, the same as your mod folder, i.e. "sample_mod"}}{{Parameter|param=definition|datatype=Table|desc=the item definition with the following keys:|
key1=id|key1_datatype=String|key1_req|key1_desc= a unique identifier for the item. This will be prepended with "mod_name_"|
key2=name|key2_datatype=String|key2_req|key2_desc=a name for the item that will be shown in tooltips|
key3=category|key3_datatype=String|key3_req|key3_desc=a category for the item that will be shown in tooltips|
key4=tooltip|key4_datatype=String|key4_req|key4_desc=a tooltip description for the item that will be shown in tooltips|
key5=shop_buy|key5_datatype=Decimal|key5_req|key5_desc=a buying price for the item if this item was to be bought|
key6=shop_sell|key6_datatype=Decimal|key6_req|key6_desc=a selling price for item if this item was to be sold|
key7=shop_key|key7_datatype=Boolean|key7_desc=whether this is a key item which means it can't be sold|
key8=machines|key8_datatype=Array|key8_desc=a list of machine this item can be used in i.e. ["workbench", "sawbench"]|
key9=durability|key9_datatype=Integer|key9_desc=if this item is a tool that gets used up you can specify a total durability here|
key10=singular|key10_datatype=Boolean|key10_desc=if true then this item will not be able to be stacked|
key11=placeable|key11_datatype=Boolean|key11_desc=whether this item can be placed - if specified you must also give an "obj" key|
key12=place_grass|key12_datatype=Boolean|key12_desc=if placeable, this specifies the item can only be placed on grass only|
key13=place_water|key13_datatype=Boolean|key13_desc=if placeable, this specifies the item can be placed on shallow water|
key14=place_deep|key14_datatype=Boolean|key14_desc=if placeable, this specifies the item can be placed on deep water|
key15=obj|key15_datatype=String|key15_desc=if placeable, this specifies the object that will be created when the item is placed. If this is not a different object id, you should be making an Object instead of an Item|
key16=honeycore|key16_datatype=Boolean|key16_desc=specifies whether this item will buy/sell for Honeycore instead of Rubees|
}}{{Return|datatype=String|desc=either your new item's id (i.e. "sample_mod_item_name") or nil if there was an error}}</div>

<p>Example LUA code to define an axe item with an ID of "sample_mod_super_axe":</p>

<div class="aw-code">item = {}<br/>item[<span class="cs">"id"</span>] = <span class="cs">"super_axe"</span><br/>item[<span class="cs">"category"</span>] = <span class="cs">"Tools"</span><br/>item[<span class="cs">"tooltip"</span>] = <span class="cs">"This is my first mod tool!"</span><br/>item[<span class="cs">"shop_buy"</span>] = <span class="cn">0.0</span><br/>item[<span class="cs">"shop_sell"</span>] = <span class="cn">0.0</span><br/>item[<span class="cs">"shop_key"</span>] = <span class="cl">false</span><br/>item[<span class="cs">"singular"</span>] = <span class="cl">true</span><br/>item[<span class="cs">"durability"</span>] = <span class="cn">9999</span><br/>item[<span class="cs">"name"</span>] = <span class="cs">"Super Axe"</span><br/>
def_item = <span class="cf">api_define_item</span>(<span class="cs">"sample_mod"</span>, item, <span class="cs">"sample_item_sprite.png"</span>)</div>

<p>If this method fails you'll see the following errors in the Modding Console:</p>

<div class="aw-code">{{Error|err=Missing Required Key (KEY)|desc=This means one of the required keys above was missing from your definition parameter}}<br/>{{Error|err=ID Already Defined|desc=This means the id key provided is already in the Dictionary, either it's already used or you defined the item twice.}}<br/>{{Error|err=Failed To Map Keys|desc=This means one of your keys couldn't be mapped properly - possible a null value.}}<br/>{{Error|err=Failed To Add Sprite|desc=This means your sprite couldn't be added, either an incorrect format or file not found}}</div>

<h2>Hook Methods</h2>

<h3>register()</h3>
<p>This is a required hook that your main mod file needs to have in order for your mod to be loaded correctly. This hook is called when the game tries to first "register" your mod to avoid mod name clashes, and the hook needs to return the unique name of your mod (this will be the "approved" name from TNgineers) i.e. <span class="aw-inline--code">sample_mod</span></p>

<div class="aw-code">{{Method|method=register}}{{Return|datatype=String|desc=the hook should return your unique mod name, i.e. "sample_mod"}}
</div>

<p>Example LUA code that your main mod file needs to include:</p>
<div class="aw-code">function <span class="cf">register</span>()<br/>&nbsp;&nbsp;<span class="ck">return</span> <span class="cs">"sample_mod"</span><br/><span class="ck">end</span>
</div>

<p>If this method fails you'll see the following errors in the Modding Console:</p>
<div class="aw-code">{{Error|err=Mod Failed To Register|desc=This means either your mod does not have a register() hook, or the code inside the register method failed to compile correctly}}<br/>{{Error|err=Duplicate Mod Registered|desc=This means somehow the game has managed to register your mod twice, you probably won't see this error ever.}}</div>

<h3>init()</h3>
<p>This is a required hook that your main mod file needs in order for your mod to be loaded correctly. This hook is what is called for the first time by the game to your mod, allowing you to setup all the things your mod needs to run! You should return a "Success" message to this mod if everything goes well so the game knows if things went A-OK from your end.<br/><br/>You can (and should) put all the setup code you need here - you can call out to methods in other functions but this is your one and only chance to define all the stuff you need at runtime to get everything the mod needs ready. After this you'll only be able to use the other hooks like <span class="aw-inline--code">clock()</span> to directly call mod code.</p>

<div class="aw-code">{{Method|method=init}}{{Return|datatype=String|desc=this should return either "Success" if things went as expected or an error message. Any error will be shown in the Modding Console}}
</div>

<p>Example LUA code that your main mod file needs to include:</p>
<div class="aw-code">function <span class="cf">init</span>()<br/>&nbsp;&nbsp;check = <span class="cn">1</span> + <span class="cn">1</span><br/>&nbsp;&nbsp;<span class="ck">if</span> check != <span class="cn">2</span> <span class="ck">return</span> <span class="cs">"Error: Maths is made up!"</span><br/>&nbsp;&nbsp;<span class="ck">return</span> <span class="cs">"Success"</span><br/><span class="ck">end</span></div>

<p>If this method fails you'll see the following errors in the Modding Console:</p>
<div class="aw-code">{{Error|err=Mod Failed To Init|desc=this means either your mod does not have a init() hook, or the code inside the init method failed to compile correctly}}<br/>{{Error|err=Mod Failed To Load|desc=this is shown when you do not return "Success" to the init() hook, and is shown along with whatever error message you returned.}}</div>

Latest revision as of 07:27, 22 June 2022

In APICO, you can add mods to expand your game and add new mechanics and features not available in the base game! If you're interested, you can even start modding the game yourself!

Here you can find all the general information and resources around mods and modding.
You can find a list of all mods on the Mod List page.

Modding

Mods for APICO!

Downloading Mods

If you're playing APICO and want to download mods, you can do it right from inside the game! From the home screen you can click on "Mods" and the game will retrieve all the approved mods that are available. Once downloaded you can choose to activate or deactivate any of your mods before starting your game. Mods are not available in the Demo.



You can also install mods not from the approved list by downloading them from the main Mod List, and using the Sideloader.
We cannot recommend these mods for security reasons so install unapproved mods at your own risk!

Creating Mods

If you want to start creating mods, you can read our Getting Started and get stuck in!
To make your mod "official" you will need to upload it to mod.io via our APICO Modloader tool and get it approved. Although players can choose to side-load APICO with any mod they download, they will get shown a warning around the mod when activated.

Modding API

If you've read out Getting Started guide and are eager to get modding, check out our API Reference which details all API methods along with example LUA code for a bunch of different use-cases.

You can also check out our APICO Modders Github for ideas and inspiration!