Moai personal thread

I discover some interesting things while creeping through the Lua code. I would like to keep a reference of them in a single spot, and thus this thread. Note: I am not responsible for bugs, broken mods, crashing games, burning computers, lawsuits, or vengeance plots resulting from using any of the (mis-)information in this thread. Note 2: I have grown and now I use SMODs. But still, back yo stuff up.

Table of contents
I post a lot of random stuff here, but here’s an index of things you might find useful.

Other resources
These are resources I’ve used and personally recommend for modding.

Location

Stonehearth appears to be using two different coordinate systems for its movement/location needs: Point3 and Point2. Point3 takes on the form of (0,0,0) – the x,y,z coordinates – while Point2 is (0,0), just the x and z coordinates. Point2 seems to be used to get grid coordinates, where hearthlings move. Because hearthlings do not fly, they do not need to consider the third (y) coordinate in their everyday activities, and so Point2s are used seemingly for speed in pathfinding. Uncertainty: How does SH’s pathfinder differentiate between two identical Point2s on different elevations? Are there multiple layer-grids?

The specific coordinate (0,0,0) refers to the very center of where you clicked when embarking.

An interesting function is :distance_squared. An example of it can be seen in /stonehearth/ai/actions/pet/pet_fns.lua. There, we want to find friends for our pet to play with. The pet has a certain radius within which it will search for a friendly NPC to hang out with. This radius is squared, then we take the difference between the pet and the friendly NPC and call the :distance_squared() function on it. Usually, finding the difference between two points on a Cartesian plane requires squaring the coordinates and then taking the square root from them; here, apparently to save on performance, Radiant foregoes square-rooting the distance, and instead squares the thing they’re comparing to the distance (the range). I’m not sure if this is actually to save on performance or not, but that seems to be the most likely reason it’s done this way; square-rooting is a lot more computationally expensive than squaring.

Things to investigate:
Cube3 (presumably used for building and mining)
Point2 grid pathing on different z-levels

4 Likes

##Console

Ctrl+C opens the Stonehearth console. The user can interact with the game world to an almost unlimited degree through the console. I decided I wanted to add my own commands to it. In the end, I succeeded; it took me about half an hour of trial and error to do so. It took a lot longer to write this. I do hope, however, that my thought process in resolving this issue will help someone with their own coding challenges down the line. Skip to the bottom for the tl;dr.

Objective
Following from the last post, I want to add a method that gives me an entity’s location in the game world. I expect this location to be in the form of (x,y,z) Cartesian coordinates. Ideally, we’d like to click on an in-game element/entity, run the command, and receive the coordinates in the console.

Investigation
Generally speaking, plaintext is a great “code anchor”: if we see plaintext in an in-game area that interests us, there is a high chance that searching for it in the game files will lead us to relevant code. Let’s get some plaintext! We can run the help command in the vanilla console. This gives us a number of options we can search for; I’ve picked dump_inaccessible_items because of its nice and long name. A search reveals a JS file: /stonehearth/ui/debug/console/console_commands.js.

A cursory read-through of console_commands offers the following tidbits. We have a selected variable that appears to correspond to whatever entity is currently selected by the player (yay! That’s part of our objective solved!). There are two ways to call a Lua function from this JS file: via radiant.call and radiant.call_obj. The difference between these is not yet clear. Finally, these caller functions themselves accept multiple arguments. The first is the name of the function, the second (and everything after the second) are arguments we want to pass into the Lua function. Looks doable enough, let’s make one of our own!

Implementation
There’s no better way to learn how the code works than by breaking it. My first step was to register another console command called get_loc, like so:

    radiant.console.register('get_loc', {
     call: function(cmdobj, fn, args) {
       return radiant.call('radiant.entities.get_world_location', selected);
     },
     description: "Returns location of selected entity"
    })

radiant.entities.get_world_location is a valid function that can be checked out in /radiant/modules/entities.lua. It indeed accepts one parameter, the entity whose location we want to check, and it should return us a Point3 describing the current location of the entity.

Firing up stonehearth and opening the console, we can type help again and we will now see our newly registered function. And yet, running it, we get an explosion: could not dispatch [function fn:474 route:radiant.entities.get_world_location]. Hey: at least we got the function to show up in the list of console commands, right? That’s a start!

I’ve learned that when you cannot figure out how something works, generally speaking it’s a good idea to parrot somebody who does know. Radiant’s native console function kill looks like a good candidate: it too accepts an argument in the form of an entity. The actual Lua function being called appears to be named stonehearth:kill_entity. Let’s see where (and how) this function is defined.

I searched for what seemed to be the function’s name, but found nothing. This is fine; we dumb it down until we find something. I search for kill_entity. I get a few results. A few of them are calling radiant.entities.kill_entity. Considering that we already tried to run a method from radiant.entities before, I am willing to bet that these are not the files we’re looking for.

There is one that calls stonehearth:kill_entity, but because it’s calling it rather than defining it, I skip it and continue looking. Eventually, I happen upon /stonehearth/call_handlers/entity_call_handler.lua, which looks about right. And inside, we find our kill_entity defined and calling another function, this time radiant.entities.kill_entity. Well, that looks real close to what we tried to do, but couldn’t, with .get_world_location. I don’t understand it yet, but it appears this entity_call_handler is a necessary in-between to pass commands from JS to Lua. I define my function here this time:

function EntityCallHandler:get_loc(session, response, entity)
  assert(entity)
  
  return radiant.entities.get_world_location(entity)
end

Then I change the calling part of my console command registration code to the following: return radiant.call('stonehearth:get_loc', selected);

And yet, again… could not dispatch [function fn:474 route:stonehearth:get_loc].

Around here, I started to get a hunch. Isn’t it just peculiar that Stonehearth seems to magically add stonehearth: in front of my function? I expand my search for kill_entity from Lua files to include everything, and up comes my answer: we get a result for manifest.json! Looks like the manifest is the ultimate glue between the different components of the program. We must register our function in the manifest before the console will know what we’re talking about:

"functions" : {
  ...
  "get_loc": {
    "controller": "file(call_handlers/entities_call_handler.lua)",
    "endpoint": "server"
  },
  ...
}

Now when we run Stonehearth, open the console, select a hearthling, and run get_loc, we get a beautiful 3-dimensional output: {x: 100, y: 230, z: 4}. Yay!

##tl;dr
To register a new console function, you must:

  1. Know what you want it to do, and find in-game Lua functions that will help you do it
  2. Using the above, create a method for your console command in the appropriate call handler
  3. Add the new command to the manifest using the name in the call handler
  4. Register the command by its manifest name inside console_commands.js

Happy scripting!

3 Likes

My girlfriend left me because of your information! You’re liable as it’s not listed!

Oh yeah? Whatcha gonna do about it? No vengeance plots!
######besides, if you got this stuff to work and she still left you, you deserve better <3

4 Likes

This is common. I did that coding other games too. Instead of:
distance = squareRoot{squared(obj1)-squared(obj2)}
use
squared(distance) = {squared(obj1)-squared(obj2)

Mathematically it is the same, but cpu usage is way lower, making a big difference in performance. Unless you actually need to know the distance as an absolute value (like showing it to the user), then you will need to use the old square root method.
If all you need is compare two distances (to chose the better path for example), it doesn’t matter which method you get, the biggest distance will be bigger than the shortest one, always.

2 Likes

That’s what I figured. Since the last post, I’ve added in another console function get_dist (which, of course, gets the distance between two entities). The results corroborate what you say. I’m square-rooting the output anyway, because it is intended for a user (that is, me). But if I wasn’t displaying it, yes, I would just compare the squared results.

Edit
Interesting: inside the console, radiant.call returns a promise rather than a value. When I tried square-rooting it, it did not work, because the value was not yet received. It would make sense, as this is a call between different languages; Javascript’s execution thread is, of course, independent of Lua’s. It needs to wait for Lua to respond before doing anything itself. However, it appears Radiant aren’t using native JS promises, but a library, like $q or maybe Bluebird or something. Because we cannot unwrap the promise and return a value from within (due to how JS closures work), we have to add another promise to the chain if we want to do anything with the value. I wonder how exactly the console prints out promise-results…

1 Like

@Moai, are you also taking a look at the debug_tools?

The Object Browser gives all kind of info about entities.

Adding your own command to the console sounds interesting, will keep reading this topic :slightly_smiling:

3 Likes

I’m not actually using it right now because I’m not working on anything that needs it. I picked this particular objective simply as something small and digestible to work on and get my toes wet with Lua. I have a mod idea in mind, and I’m approaching it bit by bit with these little objectives.

EDIT: Holy baloney. Just got the debug tools. I can’t believe I was going to hand-code all that. Thanks for bringing this to my attention, @Relyss.

4 Likes

A quick addendum to my console post. I think I’m starting to see the method behind the madness in how Radiant indexes their assets, specifically Lua functions.

The reason my first attempt didn’t work (with radiant.entities.get_world_location) is because it is an “inner” function – that is, it is used only by other Lua functions, never a JSON file or a different mod. That is the purpose behind the call_handler: it is a central reference of functions that we want to expose for one reason or another (*) outside of the Lua environment. When we point to the some_call_handler inside the Manifest, we’re telling Stonehearth: “Hey, we want to call a function xyz. You can find this specific function in some_call_handler. Don’t go asking any other files for it, some_call_handler has the exact function you need!”

And yet, these are still but mere basics. I must tame __saved_variables next: the, for the lack of a better word, “memory” of every Hearthling, fox, and rock!


(*) In my case, to use that function in the console, which is Javascript and therefore doesn’t have direct access to Lua functions. That’s why it was failing – I told it "find function zyx" and it said, “What? Blasphemer! There is no such thing in the manifest! I’m gonna explode now, bai!” In another case, it may be because the function needs to be called by a JSON file, for example when a certain piece of armour is picked up, a function is called to impart some sort of an effect on the Hearthling that wore it.

3 Likes

Alpha 15: Adding an enemy to microworld

(I am using the title as a note to myself to track compatibility with versions.) When needing to test enemies that attack (and are attacked) by your citizens, we’ll need to follow a set of steps. I prefer using Radiant’s Microworld due to its similarity to the main game and simplicity of use. We’ll spawn our enemy and equip him with a weapon, but we’ll also add him to another population hostile to our player.

Stonehearth uses populations as a way to manage, well, populations, players, factions, and the like. They share a relation map (called amenity map in the code) with each other which determines how each population regards the others. I have not yet found a way to make an entity that attacks another entity while belonging to the same faction, though I’m sure some hack is possible.

  1. Let’s create a population. Open microworld.lua. Under where it says local LOCAL_PLAYER = 'player_1', let’s add:
    local LOCAL_ENEMY = 'enemy1'
  2. That was the alias of our testing enemy population. Now to register it as an in-game population, find where it says stonehearth.player:add_player(LOCAL_PLAYER, 'stonehearth:kingdoms:ascendancy'), and add below:
    stonehearth.population:add_population(LOCAL_ENEMY, 'stonehearth:kingdoms:undead')
  3. We added undead to the game. Now below the last line, add the next one to make the undead and the player at war with one another:
    stonehearth.player:set_amenity(LOCAL_ENEMY, LOCAL_PLAYER, 'hostile')

We’re set to add some undead to the game now. While adding a custom enemy faction and a custom enemy are beyond the scope of this note, in general, keep in mind that they should be created with the faction in mind first.

I have used the function MicroWorld:place_citizen() as a template for writing the following function. It was placed immediately following it in the code.

function MicroWorld:place_enemy(x, z)
   local pop = stonehearth.population:get_population(LOCAL_ENEMY)
   local foe = pop:create_new_citizen('skeleton')
   foe:add_component 'render_info':set_scale(0.5)

   local weapon = radiant.entities.create_entity('stonehearth:weapons:jagged_cleaver')
   weapon:add_component 'render_info':set_scale(0.5);
   radiant.entities.equip_item(foe, weapon)

   local town = stonehearth.town:get_town('player_1')

   radiant.terrain.place_entity(foe, Point3(x, 1, z))
   return foe
end

Using this function we are given the ability to spawn a giant enemy skeleton. To make him a garden-variety enemy skeleton, remove the render_info parts.

To use this ability, we navigate to mini_game_world.lua, and add microworld:place_enemy(-16, 10) to the bottom of the function MiniGame:start.

TODO:

  • Engine error after killing skeleton
  • Skeleton’s attacks really slow, why?
1 Like

Did I seriously just see a hearthling named Chopper Pounder?

5 Likes

Perfect name for a Blacksmith that only makes Axes.

3 Likes

Yes. And he will sharpen them on his sideburns.

2 Likes

PSA: Ranged damage considers hearthling strength the same way as melee damage does (as of Alpha 17, build 3013). To be fair, I don’t know what other attribute it would possibly consider.

PSA 1: Have at least one archer with slowdown arrows. This will help you survive against kiting kobolds.

PSA 2: Archer battlements. Use them, they seriously help.

Alpha 17: Writing to the console

There is an old topic by by Tom regarding how to write to the log:
http://discourse.stonehearth.net/t/how-to-log-from-your-mod/5222

However, I could not get this method to work. There are instances of logs in the Lua code, the formula for which I replicated exactly in my own code. However, logging also requires another component: a modification of your user_settings.json. While the method described in Tom’s topic still works to open the console, no matter how much I tried, I couldn’t actually get it to display any logs. SO: If anyone here knows how to get the logging to work right, please reply to this post!

However, as I still have coding to do, I managed to kludge together a way to write to the log regardless. I did so by looking up some of the console text that it was printing out every time the game started, and copying the method from there. Here is the function I now use:

function log(text)
  radiant.log.write('my_mod_name', 0, text)
end

Use it like so:

log("hello world")

Don’t forget to add the console setting to your user_settings.json:

...
"logging": {
  "show_console": true
},
...

Edit
Also, starting from now, I am no longer vivisecting Stonehearth. On one hand, doing so is faster, but it comes at a cost of risking your stuff being overwritten in an update. I’m still not making .smods, but at least I keep my stuff in a separate folder.

Looking at your other post in the same thread, you seem to get it right in your user settings. There is a different way of showing the logs for the entire mod though, one that I’m usually using for my own mods, and that is to give a log level directly to the mod name, rather than an object containing log levels.

Here’s a snippet from my user_settings.json to show what I mean.

"logging" : {
   "show_console" : true,
   "mods" : {
      "stonehearth" : {
         "log_level" : 1,
         "crafter" : 3
      },
      "homf" : 7,
      "smart_crafter" : 5
   }
},

In the code I’d then create a logger and call on the various functions. For example:

local log = radiant.log.create_logger('tag_name')
...
log:info('Some log info')

This works for me at all times, assuming that the logging function is being called on in the first place, of course. What are you doing that’s different from this? Again, from looking at your previously mentioned post, it should’ve worked for you as well, though the __init function isn’t something I would use, but that’s depending on how the controller is used to begin with.

2 Likes

Hmm… :sweat:


This gives me the following output (I pressed the button twice to make sure I wasn’t seeing some other log):

I mean, it’s more than what I was getting before, but it’s still not… quite what I want. The same happens whether I run it with :info or :error.

I actually use HOMF for reference quite a lot. Yesterday I spent half an hour debugging an issue where my stonehearth variable didn’t contain the selection property, but in your code, you were accessing stonehearth.camera without a problem. selection and camera, as services, both initialize at the same time. Would’ve taken me longer if I didn’t finally realize your call handler was targeted at client, and mine to the server! selection did not exist on the server, as it’s a client-end function. Now I know what those endpoints are for.

The code in the other thread is ancient. In retrospect, I shouldn’t have been using __init either.

EDIT

Flurp. “Failed” was another one of my logs, it was further down the code. Nevermind, create_logger is still not working for me at all.

Hrmm, that is odd; by all accounts it should be working. That semicolon at the call to create_logger isn’t needed, though I really doubt that’s the one causing problems here. Would you mind if I took a peek at your mod as it is now?


Yeah, one thing one should be mindful of when adding a new service or call handlers, is on what endpoint to place it on. Having it on the wrong place can cause terrible and confusing bugs. That’s also something I had to learn the hard way…

Hehe, oops. You found the javascript guy.

cannonade.zip (16.7 KB)

And, to reiterate, user_settings.json:

"logging" : {
	"show_console" : true,
	"mods" : {
		"cannonade" : 9
	}
},

Thank you very much for the help!

EDIT
Probably should’ve said what the mod does. Using debug tools, stamp down a cannonade:artillery, select it, and try using its custom command. You can right or left click to get rid of the banner. Clicking the button should output the logs.