Web/main/_posts/2019-10-03-rock_bottom_mod.md
Ell 28e938feb6
All checks were successful
Web/pipeline/head This commit looks good
added emoji to the website because they look hella good
2021-11-10 23:18:12 +01:00

185 lines
No EOL
10 KiB
Markdown

---
layout: blog
title: ⬇️ How to make a Rock Bottom mod
summary: My adventures back into a game I stopped working on about two years ago and how I start on a mod for it
tags: [Programming]
discuss: https://twitter.com/Ellpeck/status/1180092634410487808
---
So it's been a hot minute since I stopped working on my first big game project, [Rock Bottom](https://rockbottomgame.com). Since then, I've changed a lot, but the game hasn't changed that much: For a long time, the project was vacant, until I decided to make it open source. From that point on, a couple of my friends started working on it, adding some new features and fixing some bugs, until it seemingly fell back into vacancy over the last couple of weeks.
So let's port my recent Minecraft mod, [Nature's Aura](https://www.curseforge.com/minecraft/mc-mods/natures-aura), to Rock Bottom!
***Note: This is not a tutorial.***
# Setting up the dev environment
Now I *somewhat* remember that, back when I was working on the game, I set up a pretty in-depth modding API as well as an actual Github project to easily allow people to set up a mod themselves.
*Aha, here it is: [the modding repo](https://github.com/RockBottomGame/Modding).* It even has some nice documentation that I apparently made! Okay, here goes.
Okay, finding [a build of the game](https://github.com/RockBottomGame/RockBottom/releases) was pretty easy. It even has an in-depth changelog! Good on you, raphy.
Hm. Okay.
So something's already broken.
<img src="/blog/res/rock_bottom_mod/1.png" width="100%">
It looks like here
```
dependencies {
compile group: 'de.ellpeck.rockbottom', name: 'RockBottomAPI', version: '+'
}
```
where it says `+`, it should actally be saying a specific version. So let's replace that with `0.4.6-753` and refresh.
That didn't seem to fix it either. Huh. Taking a look at [the maven](https://maven.chaosfield.at/de/ellpeck/rockbottom/RockBottomAPI/0.4.6-753/), it seems to be currently down because of `502 bad gateway`. Fun.
Okay, it's now the next day and it looks like the maven has been fixed, which is nice. So all the compile issues are finally resolved, I put the build into the `/gamedata` folder like explained in the tutorial, I renamed the examplemod to `NaturesAura`, and I can now finally try running the game!
<img src="/blog/res/rock_bottom_mod/2.png" width="100%">
Ah! That worked quite well in the end.
# Actually making something
So I feel like the first thing I should add to the game is the golden leaves, because they're also the first thing that you have to create when getting started with the Minecraft version of Nature's Aura.
So I quickly made a new class that extends `TileBasic`, which I vaguely remember was like an extended, better version of the `Tile` class with some useful helper methods.
```java
package de.ellpeck.naturesaura.tiles;
import de.ellpeck.rockbottom.api.tile.TileBasic;
import de.ellpeck.rockbottom.api.util.reg.ResourceName;
public class TileGoldenLeaves extends TileBasic {
public TileGoldenLeaves(ResourceName name) {
super(name);
}
}
```
My Java is pretty rusty, but I seem to remember all of the naming conventions and, coming from C#, do find myself having trouble with the lowerCamelCase methods (because C# uses UpperCamelCase for method names, conventionally). I quickly made a `Tiles` class that's going to house all the tiles I create and made an instance of the golden leaves tile.
```java
package de.ellpeck.naturesaura.tiles;
import de.ellpeck.naturesaura.NaturesAura;
import de.ellpeck.rockbottom.api.tile.Tile;
public final class Tiles {
public static final Tile GOLDEN_LEAVES = new TileGoldenLeaves(NaturesAura.createRes("golden_leaves"));
}
```
I also created a dummy instance of the Tiles class so that the static variables in it are loaded. If you're not extremely familiar with Java, then this might appear a bit weird for you, but static variables of a class only get instantiated once the class is first used in some way (be it instance creation, method calling, stuff like that), and because I want the golden leaves to be initialized in the `preInit` phase of the game, I just do this:
```java
@Override
public void preInit(IGameInstance game, IApiHandler apiHandler, IEventHandler eventHandler) {
this.modLogger.info("Starting Nature's Aura for RockBottom");
new Tiles();
}
```
So far, so good. Let's figure out how to add a texture to the thing.
I somewhat remember that I made a horrible json-based asset system (instead of just loading all of the assets in the mod's jar automatically), so I'm going to try to add my tile to the `assets.json` file the example mod provided me with and also add a texture into the actual file system.
<img src="/blog/res/rock_bottom_mod/3.png" width="100%">
This is the folder structure I decided on. I also created a quick golden version of the game's leaves texture by going to the [asset repository](https://github.com/RockBottomGame/Assets), stealing the leaves texture and recoloring it to be golden-ish.
Also, I put this in the assets file, but I have no idea if that's actually the right path. We'll find out.
```json
{
"loc.": {
"us_english": "/loc/us_english.json"
},
"tex.": {
"tiles.": {
"golden_leaves": "/tex/tiles/golden_leaves.png"
}
}
}
```
Okay, let's launch and see what happens. So I started the game, opened a lan server, opened the chat, typed `/items` to get a cheat menu, and... it's not showing up. What did I do wrong?
After some thinking, it turns out that I forgot to actually *register* the tile. So I went back into my `Tiles` class and appended `.register()` to the end of the whole thing, like this:
```
public static final Tile GOLDEN_LEAVES = new TileGoldenLeaves(NaturesAura.createRes("golden_leaves")).register();
```
*Crisis averted*.
<img src="/blog/res/rock_bottom_mod/4.png" width="100%">
Yaaay, it... worked? *Somewhat?*
You're probably yelling at your screen by now, but yes, I finally noticed it as well: My assets path says `examplemod` instead of `naturesaura`. Easy fix, though.
While I'm at it though, I can also add a localization entry for the golden leaves. When hovering over it, the console informed me that `Localization with name naturesaura/item.golden_leaves is missing from locale with name us_english!`, so I quickly added it:
```json
{
"naturesaura": {
"item.": {
"golden_leaves": "Golden Leaves"
}
}
}
```
Let's try again.
<img src="/blog/res/rock_bottom_mod/5.png" width="100%">
Ta-da! Success, at last.
After some investigation, I realized that Rock Bottom's normal leaves can be walked through, so let's see how to make that happen. Typing `@Override` while in the `TileGoldenLeaves` class causes my IDE to list all of the methods I can override. Among them are two that interest me:
```java
@Override
public boolean isFullTile() {
return false;
}
@Override
public BoundBox getBoundBox(IWorld world, TileState state, int x, int y, TileLayer layer) {
return null;
}
```
It seems like this is what I have to do to make the tile walk-through...able. Let's try it out.
<img src="/blog/res/rock_bottom_mod/6.png" width="100%">
Yay, that seems to have worked. Great.
# Making an item
Now that I have somewhat of a grip of this whole Rock Bottom stuff again, I'm quickly going to make an item. I won't bore you with the details as it's pretty similar to making a tile, but the gist of it is this: I made an `ItemGoldPowder` class that extends `ItemBasic`, and I initialized and registered an instance of that in my newly created `Items` class, of which I created an instance in my mod class's `preInit` method so that it gets initialized at the right time. Also, I did all of the annoying asset mumbo jumbo.
<img src="/blog/res/rock_bottom_mod/7.png" width="100%">
*Oh, C#, you've ruined me.*
# Making stuff happen
Now, to actually make the gold powder turn normal leaves into golden leaves, I have to write some actual code that isn't just boring boilerplate. When right clicking the golden powder onto a leaves tile, I want that tile to turn into the golden leaves. Eventually, they should then *transition* somehow, but... I'll leave that for later.
Let's write some code!
```java
@Override
public boolean onInteractWith(IWorld world, int x, int y, TileLayer layer, double mouseX, double mouseY, AbstractEntityPlayer player, ItemInstance instance) {
var state = world.getState(layer, x, y);
if (state.getTile() != GameContent.TILE_LEAVES)
return false;
world.setState(layer, x, y, Tiles.GOLDEN_LEAVES.getDefState());
player.getInv().remove(player.getSelectedSlot(), 1);
return true;
}
```
While writing this code, I quickly remembered that Rock Bottom has a tile state system similar to Minecraft, so I had to work with that. Before I realized, I sat there wondering why `IWorld` doesn't have a `getTile` method anywhere. Anyway, this should check the interacted tile; see if it's leaves; replace the leaves with golden leaves and deduct one from the item being currently held (which is obviously the gold powder, as this is in the `ItemGoldPowder` class. No need for checks there.)
Let's try it out! ...yea, no. The Java version that gradle uses to compile a Rock Bottom mod isn't new enough yet: You can't use the `var` keyword. Oh, my poor, poor C# soul. So let's swap that `var` out for a `TileState`.
<img src="/blog/res/rock_bottom_mod/8.gif" width="100%">
Yay, it works! *Except that, in the gif, the mouse position is weirdly offset for some reason. It's correct in person, I promise!*
# Conclusion
So yea, that was the start of my adventure back into Java, back into my old game and back into... well, modding, I guess. I hope you enjoyed reading this post and seeing my process of working out how to do stuff.
I think it's important to remember that, as a developer (especially an indie developer), you don't have to code everything perfectly or work stuff out correctly the first try. I mean, heck, this is my own game, and I completely forgot how to make a mod for it. But it was fun to figure it out again, and to get back into something I made two years ago.
Oh, also, if you really want, [here's a build of the mod](https://ellpeck.de/blog/res/rock_bottom_mod/NaturesAuraRockBottom-0.1.jar) that you can try out yourself, as the game is now actually open source and available to everyone! I created this jar with the command `gradlew build`, and all you have to do to run it is [download the game](https://github.com/RockBottomGame/RockBottom/releases), run it once, and then stick the mod jar into its `mods` folder. It really doesn't do that much right now, though, so I don't know why you'd bother.
As always, thanks for reading!