[Questions] Some questions about the mod barriers

Hi there again :smiley:

While my last big hobby project is near the finish line, “Coder ADHD” got me again and I tried some simple modding for stonehearth. As always it’s just much more fun sitting 5 hours learning somethin new instead of working 1 hour on the last “boring” project. I really love stonehearth and I like the opportunities of modding in this early version, so I’m looking forward building my own mod. Writing/changing some code isn’t the problem, but I just wanna know wether my ideas need writing a half new game or can be implemented with little code-changes. I searched most of the old question-threads so I hope, i don’t asking old questions (again^^).

All questions are way more about the “long-term opportunities” then the actual version, but It would be nice to know, wether I can “plan” with some features…

1.) Is it possible to change the mob attacks to some fixed time frames like “every 2 days evening” or “day 8 night”?
2.) Is it possible to clearly define quantity, type and spawn point of the mobs?
3.) Is it possible to skip the map building and start on the same map with a fix campfire position?
4.) Is it possible to call a function after a house is build?
5.) Is it possible to create some building blueprints?

I don’t think these things are big deals, but I better ask before starting the work.

Greetz Programmierer

2 Likes
  1. If not now, then in the future - the devs want things like scripted encounters for their dungeons etc.
  2. Should be - checked the goblin raid stuff?
  3. You often see the devs testing things on their little micro worlds, so it’s certainly possible for them. Not so sure about it for people running the client though. Hmm… the Stonehearth graphics test program they released a while back did something like this though…
  4. This one I’ve no idea about sorry :stuck_out_tongue: .
  5. I believe it’s on the to do list.
2 Likes

hmm… @sdee had just mentioned some options for adjusting goblin behavior elsewhere… I’m not sure if spawn rates were a part of the suggestion though (I think it had to do with health and attack values?)

2 Likes

Do you mean this post? Spawn rates were a part of that (the second suggestion she made).

2 Likes

Thanks for the answers. I just started, hoping for the best =)
I think the important things will work so let’s go :smiley:

(still hoping 4/5 are possible, that would be awesome! ^^)

FYI

:wink:

1 Like

Yeah, I read that =)
It’s totally awesome and by time I have to read all through :smiley:

1)yes but its complex. If I remember there are 3 observers taking place, so simply changing the spawn time isn’t sufficient to make it a fixed entity, see (2).
2)yes and no. You can write a script that spawns a goblin anywhere you want whenever you want and that would be easier than trying to change to current scripts for that much control.
3)Not that I know of, test world maybe?
4)yes but its complex. My guess is that there is an observer already checking if the building is finished so the function creating the work orders stops running, you could tap into that, but I have no idea where it is or what its called.
5)Not implemented.

3 Likes

Well answered, folks!

Here are some elaborations. Almost everything you mention is possible, but at the moment requires quite a bit of lua coding. :slight_smile: If you’re still game, read on:

1.) If you’re okay editing lua, open linear_combat_service.lua and muck around there as much as you like. The variables at the top set the time to first spawn and the spawn recurrence.

2.) Agree with @Wombat85 , above. The goblin brigand scenario takes an input that defines the number of thieves that appear. The spawn location is determined inside goblin_thief.lua and goblin_brigand.lua. By default, it is on the perimeter of the visible region, but you could just pass in an xyz coordinate.

3.) Ah, I see what you’re trying to do–create a reliable starting state for the game, yes? @Albert is your man for this, but it’s kind of complicated. a.) You want to turn off random generation for the map, perhaps starting it with a particular seed and b.) override new_game_call_handler.lua in such a way that the campsite is auto-dropped at the same place every time, sort of what happens with Quickstart, but with fewer random factors. This is definitely code-intensive; still, it’s good for us to know that this is a thing people would want to mod.

4.) From some lua code somewhere, listen on: radiant.events.listen(self._entity, ‘stonehearth:construction:dependencies_finished_changed’, self, your_function_here)

This will call the function after the house is finished but before the door/windows are attached

5.) As Froggy said, not implemented yet. If you want to do this you can theoretically do it the way we used to make Simple House: basically you write a fabricator lua class that always puts walls/roof/door down in the same place, with the location parameters passed in at runtime, and then trigger the fabricator from a button in the UI. Eventually, we want this to be completely configurable from json.

@Programmierer, we look forward to what you build!

4 Likes

[quote=“sdee, post:9, topic:7066”]
Here are some elaborations. Almost everything you mention is possible, but at the moment requires quite a bit of lua coding. If you’re still game, read on:[/quote]
Wow. Thanks for taking your time and writing such long answer =)

And yeah, you’re right, it’s about getting the same starting state for every play. It’s for this one here, but I think this would be a nice opportunity for every “fight/challenge centered mod”. All in all I just wanted to know, wether my ideas would be realizable. And I think sitting some hours in front of code is okay, after spending many hours modelling all the objects.

So thank your again for your time, looking forward to present my first steps :smiley:

1 Like

So just use this thread for one more thing:
Anybody an idea, which .luac-file need to be edited for adding new trees to the world building process?

I know @RepeatPan did it once. Who else… :smile:

Back then, it was all done in the landscaper. It’s basically what Jelly did (or still does), but there wasn’t any interest at all. People liked overriding the oak trees more than adding or modifying them properly.

1 Like

[quote=“RepeatPan, post:13, topic:7066”]
Back then, it was all done in the landscaper.[/quote]
Oh, I saw the new .luac-files aren’t compiled anymore, so it was a hell easier.
And the landscaper was the missing part, so thank you :slight_smile:

1 Like

It’s actually much harder in my humble opinion. Back then, we had

local TerrainType = require("services.server.world_generation.terrain_type")
local TerrainInfo = require("services.server.world_generation.terrain_info")
local Array2D = require("services.server.world_generation.array_2D")
local FilterFns = require("services.server.world_generation.filter.filter_fns")
local PerturbationGrid = require("services.server.world_generation.perturbation_grid")
local BoulderGenerator = require("services.server.world_generation.boulder_generator")
local log = radiant.log.create_logger("world_generation")
local Point3 = _radiant.csg.Point3
local mod_name = "stonehearth"
local mod_prefix = mod_name .. ":"
local oak = "oak_tree"
local juniper = "juniper_tree"
local tree_types = {oak, juniper}
local small = "small"
local medium = "medium"
local large = "large"
local ancient = "ancient"
local tree_sizes = {
  small,
  medium,
  large,
  ancient
}

local berry_bush_name = mod_prefix .. "berry_bush"
local generic_vegetaion_name = "vegetation"
local boulder_name = "boulder"
local Landscaper = class()

function Landscaper:__init(terrain_info, rng, async)
  if async == nil then
    async = false
  end
  
  self._terrain_info = terrain_info
  self._tile_width = self._terrain_info.tile_size
  self._tile_height = self._terrain_info.tile_size
  self._feature_size = self._terrain_info.feature_size
  self._rng = rng
  self._async = async
  self._boulder_probabilities = {
    [TerrainType.plains] = 0.02,
    [TerrainType.foothills] = 0.02,
    [TerrainType.mountains] = 0.02
  }
  
  self._boulder_generator = BoulderGenerator(self._terrain_info, self._rng)
  self._noise_map_buffer = nil
  self._density_map_buffer = nil
  self._perturbation_grid = PerturbationGrid(self._tile_width, self._tile_height, self._feature_size, self._rng)
  self:_initialize_function_table()
end

whereas nowadays you’ve got to deal with

local n=require'services.server.world_generation.terrain_type'local e=require'services.server.world_generation.terrain_info'local l=require'services.server.world_generation.array_2D'local s=require'services.server.world_generation.filter.filter_fns'local q=require'services.server.world_generation.perturbation_grid'local o=require'services.server.world_generation.boulder_generator'local e=radiant.log.create_logger('world_generation')local a=_radiant.csg.Point3
local e='stonehearth'local b=e..':'local u='oak_tree'local f='juniper_tree'local i={u,f}local c='small'local p='medium'local y='large'local w='ancient'local r={c,p,y,w}local m=b..'berry_bush'local v="vegetation"local z="boulder"local e=class()function e:__init(t,l,e)if e==nil then e=false end
self._terrain_info=t
self._tile_width=self._terrain_info.tile_size
self._tile_height=self._terrain_info.tile_size
self._feature_size=self._terrain_info.feature_size
self._rng=l
self._async=e
self._boulder_probabilities={[n.plains]=.02,[n.foothills]=.02,[n.mountains]=.02}self._boulder_generator=o(self._terrain_info,self._rng)self._noise_map_buffer=nil
self._density_map_buffer=nil
self._perturbation_grid=q(self._tile_width,self._tile_height,self._feature_size,self._rng)self:_initialize_function_table()end

Even if we re-compile/decompile it to get unluac’s nice formatting (that I’ve actually tampered with too) back, it’s still leaving a lot to desire.

[quote=“RepeatPan, post:15, topic:7066”]
It’s actually much harder in my humble opinion.[/quote]
For the coding itself it’s much harder, you’re right.
But when you’re looking for the “missing link”, you know just can search for “tree” in all files instead of decompiling every single .luac.

But yeah, first step was to rename all local variables so I could see through.
It’s a bit annoying, hopefully this will change in the future…

There’ve been tools around such as my unwrp which had extracted and decompiled everything in one step. In the end, each update was for me just “execute the batch which unpacks the files, commit to local git repository and see what has changed”.

2 Likes