Jump to content

[Solved] Add own set pieces to worldgeneration?!


Recommended Posts

Since hours I'm trying around, but it seems very hard to do so...

I tried using this setpiece.lua, but the things are not added to the world... only to Layouts...
http://forums.kleientertainment.com/topic/32602-tutorial-making-a-custom-setpiece/?do=findComment&comment=680970

Map = {}
modimport("setpiece.lua") -- replace directory with location setpiece.lua is located.
--[[
	This function acts as a wrapper which we will use to add our custom layout as a set piece.
	------------------------------------------------------------------------------------------------------------
	level_id		-	Level Identification (LEVELTYPE.ALL, LEVELTYPE.SURVIVAL, LEVELTYPE.CAVE, LEVELTYPE.ADVENTURE, 
						LEVELTYPE.TEST, LEVELTYPE.UNKNOWN, LEVELTYPE.CUSTOM)
	layout_name 	-	The name of the layout which we will be adding, if the layout has not already been added we will add the layout.
	layout_type			-	Layout Type (LAYOUT_TYPE.RANDOM, LAYOUT_TYPE.GROUND, LAYOUT_TYPE.ROOM) Default is LAYOUT_TYPE.RANDOM.
	count			-	The amount of set pieces we want to spawn. Default is 1.
	chance			-	The percentage change the layout will spawn. Default is 100.
	terrain			-	Identification of the ground or room. Identification must coincide with the type or it will error out.
						Ground Example: GROUND.GRASS or GROUND.DIRT
						Room Example: Badlands or BGGrass
    isalreadylayout - if it is already in layout style in scripts/map/layouts directory -- added by serp
    
    -- The layout will automatically be loaded with this function, as long as the static_layout is located in the proper folder directory: scripts/map/static_layouts
    -- Map.CreateSetPiece( Map.LEVELTYPE.ALL, "example", Map.LAYOUT_TYPE.GROUND, count, chance, GROUND.DIRT )
    -- Map.CreateSetPiece( Map.LEVELTYPE.ALL, "example", Map.LAYOUT_TYPE.GROUND, count, chance, "Any" )
    -- Map.CreateSetPiece( Map.LEVELTYPE.ALL, "example", Map.LAYOUT_TYPE.ROOM, count, chance, "Graveyard" )
    -- Map.CreateSetPiece( Map.LEVELTYPE.ALL, "example", Map.LAYOUT_TYPE.RANDOM )
--]]

Map.CreateSetPiece( Map.LEVELTYPE.ALL, "pigguardseasy2", Map.LAYOUT_TYPE.RANDOM, 50, 100, "Any" )

 

I have a "pigguardseasy2.lua" in my MOD/scripts/map/static_layouts folder. It is just a copy of the games pigguards_easy.lua .

I know I can also just use the code I wrote here:
http://forums.kleientertainment.com/topic/69863-worldgeneration-warning-could-not-find-a-spot-for-teleportatobaselayout/
but I like the possibility of setting the leveltpye, terrain and chance.

 

Edited by Serpens
Link to comment
Share on other sites

And how do I add things to a static_layout as a layout?!

I try in scripts/map/layouts/PigGuardsWithChest4.lua:

return  StaticLayout.Get("map/static_layouts/pigguards_easy",
      {
      areas = {
            construction_area = function() return PickSome(2, { "birdcage", "cookpot", "firepit", "homesign", "beebox", "meatrack", "icebox", "tent" }) end,
        },
      })

and in modworldgenmain.lua:

local Layouts = GLOBAL.require("map/layouts").Layouts
Layouts["PigGuardsWithChest4"] = GLOBAL.require("map/layouts/PigGuardsWithChest4")

AddTaskSetPreInitAny(function(tasksetdata)
    if tasksetdata.location ~= "forest" then
        return
    end

    tasksetdata.set_pieces["PigGuardsWithChest4"] = { count = 20, tasks={"Make a pick", "Dig that rock", "Great Plains", "Squeltch", "Beeeees!", "Speak to the king", "Forest hunters", "Badlands", "Befriend the pigs", "For a nice walk", "The hunters", "Magic meadow", "Frogs and bugs"}}
end)


but none of the structures are added to the pigguards...

 

edit:
Ah, I have to define "chests" in the static layout...

Edited by Serpens
solved
Link to comment
Share on other sites

The topic linked is about DS and pretty old, i guess the DST code is too different to make it work.

There is no easy way to add set piece as far as i know, especially if you want to control the number and have at least one garanteed.

 

I use the code that Ysovuka/Kzisor made, but it is old and some parts aren't working anymore as far as i know, at least in the beta. (But since the beta will sooner or later be in the main game... )

@PeterA made some changes so at least one part is still working, here is the code if it could help you.

 

-- GLOBAL variables which are needed.
local require = GLOBAL.require

--[[	Copyright © 2015 Ysovuka/Kzisor	 ]]
	
-- This LEVELTYPE table is used to indicate the level identification.
-- The file is provided in "scripts/map" of the original game.
require("map/level")
local LEVELTYPE = GLOBAL.LEVELTYPE
LEVELTYPE.ALL = "ALL"


-- This function acts as a wrapper which we will use to add our custom layout to the game.
-- The name of the layout will be identical to the filename.
local function AddLayout(name)
	-- The Layouts table is where we will add our set piece layout.
	-- The file is provided in "scripts/map" of the original game.
	local Layouts = require("map/layouts").Layouts

	-- The StaticLayouts variable is used to get the layout of our set piece.
	-- The file is provided in "scripts/map" of the original game.
	local StaticLayouts = require("map/static_layout")

	-- Puts our layout in the Layouts table.
	Layouts[name] = StaticLayouts.Get("map/static_layout/"..name)

	-- We return the name and the layout for later usage.
	return name, Layouts[name]
end


-- This function acts as a wrapper which we will use to add our custom layout as a random set piece.
-- This function requires a Level Identification (LEVELTYPE.ALL, LEVELTYPE.SURVIVAL, LEVELTYPE.CAVE, LEVELTYPE.ADVENTURE, LEVELTYPE.TEST, LEVELTYPE.UNKNOWN, LEVELTYPE.CUSTOM).
local function AddRandomSetPiece(levelid, layout_name)
	-- Check to see if we want it on all level types.
	if levelid == LEVELTYPE.ALL then
		AddLevelPreInitAny(function(level)
			-- Ensure that we have a variable to store our data.
			if not level.random_set_pieces then
				level.random_set_pieces = {}
			end

			table.insert(level.random_set_pieces, layout_name)
		end)

	else -- We want it on a specific level type.
		AddLevelPreInit(levelid, function(level)
			-- Ensure that we have a variable to store our data.
			if not level.random_set_pieces then
				level.random_set_pieces = {}
			end

			table.insert(level.random_set_pieces, layout_name)
		end)
	end
end

-- This function acts as a wrapper which we will use to add our custom layout as a static set piece per level.
-- This function requires Level Identification (LEVELTYPE.ALL, LEVELTYPE.SURVIVAL, LEVELTYPE.CAVE, LEVELTYPE.ADVENTURE, LEVELTYPE.TEST, LEVELTYPE.UNKNOWN, LEVELTYPE.CUSTOM).
-- This function requires a list of tasks which can be located in "scripts/map/tasks.lua" file of the original game.
function env.AddSetPiece(levelid, layout_name, count, tasks, chance)
     
    -- Set chance to chance or 100 to ensure it spawns.
    chance = chance or 100

    -- Set count to count or 1 to ensure it spawns.
    count = count or 1
	
	local function RandomChance()
        local _count = 0
 
        for i = 0, count do
            if (chance * 10) >= math.random(1, 1000) then
                _count = _count + 1
            end
        end
 
        return _count
    end
    -- Check to see if we want it on all level types.
    if levelid == LEVELTYPE.ALL then
        AddLevelPreInitAny(function(level)
            -- Ensure that we have a variable to store our data.
            if not level.set_pieces then
                level.set_pieces = {}
            end
 
            -- Initial set up.
            level.set_pieces[layout_name] = { count = 0, tasks = tasks }
 
            -- Add our layout to the room layouts ensuring at least one spawns.
            level.set_pieces[layout_name].count = RandomChance() or 0
        end)
 
    else -- We want it on a specific level type.
        AddLevelPreInit(levelid, function(level)
            -- Ensure that we have a variable to store our data.
            if not level.set_pieces then
                level.set_pieces = {}
            end
 
            -- Initial set up.
            level.set_pieces[layout_name] = { count = 0, tasks = tasks }

            -- Add our layout to the room layouts ensuring at least one spawns.
            level.set_pieces[layout_name].count = RandomChance() or 0
        end)
    end
end

-- This function acts as a wrapper which we will use to add our custom layout as a static set piece per room.
-- This function requires Room Idenficiation (A list of rooms can be found in rooms.lua).
-- The rooms.lua file is located in the root directory of this mod.
function env.AddRoomSetPiece(room_name, layout_name, count, chance)
   
    -- Set chance to chance or 100 to ensure it spawns.
    chance = chance or 100

    -- Set count to count or 1 to ensure it spawns.
    count = count or 1

	local function RandomChance()
        local _count = 0
 
        for i = 0, count do
            if (chance * 10) >= math.random(1, 1000) then
                _count = _count + 1
            end
        end
 
        return _count
    end
 
    AddRoomPreInit(room_name, function(room)
		-- Initialize our custom layout.
		AddLayout(layout_name)
		
        -- Ensure that we have a variable to store our data.
        if not room.contents.countstaticlayouts then
            room.contents.countstaticlayouts = {}
        end
	
        -- Initial set up.
        room.contents.countstaticlayouts[layout_name] = 0
 
        -- Add our layout to the room layouts ensuring at least one spawns.
        room.contents.countstaticlayouts[layout_name] = RandomChance() or 0
    end)
 
end

local Tasks = require("map/tasks")


AddRoomSetPiece("BGCrappyForest", "hound_warg_coat", 1, 5)

But since you add set piece to room, you have a very, very random number of setpiece, because you could have a great number of one room in one world, and fewer in the next world.
In some of my setpiece, even with low number i sometime end with a great number of set piece... Or none.

Link to comment
Share on other sites

Quote

But since you add set piece to room, you have a very, very random number of setpiece, because you could have a great number of one room in one world, and fewer in the next world.
In some of my setpiece, even with low number i sometime end with a great number of set piece... Or none.

so it is better to add it the way I did it in my second post? That way I give the exact amount that should spawn in the whole world...
But I don't know, if and how I control the ground/biomes where it can spawn. That's the only reason why I'm looking for another solution...
 

Link to comment
Share on other sites

8 hours ago, Serpens said:

so it is better to add it the way I did it in my second post? That way I give the exact amount that should spawn in the whole world...
But I don't know, if and how I control the ground/biomes where it can spawn. That's the only reason why I'm looking for another solution...
 

This is defined in tasksets. 

set_pieces = {
			["ResurrectionStone"] = { count = 2, tasks=tasks_1 },
			["WormholeGrass"] = { count = 8, tasks=tasks_2 },
			["MooseNest"] = { count = 8, tasks=tasks_3 },
			["CaveEntrance"] = { count = 10, tasks=tasks_4 },
		},

Where tasks_1, ..., tasks_4 can be (but do not have to be) different sets of tasks (i.e., biomes).

For example, if you do not want a setpiece to spawn in the pigking area, then you leave "Speak to the king" out of the tasks associated with that setpiece.

Edited by Joachim
Link to comment
Share on other sites

9 minutes ago, Joachim said:

This is defined in tasksets. 


set_pieces = {
			["ResurrectionStone"] = { count = 2, tasks=tasks_1 },
			["WormholeGrass"] = { count = 8, tasks=tasks_2 },
			["MooseNest"] = { count = 8, tasks=tasks_3 },
			["CaveEntrance"] = { count = 10, tasks=tasks_4 },
		},

Where tasks_1, ..., tasks_4 can be (but do not have to be) different sets of tasks (i.e., biomes).

thx
but this is exactly the way I wrote it in my second post ^^ (second code).

I think I will stay with this method, since this is the global worldcount.. and for now I only have set_pieces that I want 1-5 times per world.

Link to comment
Share on other sites

2 hours ago, Arlesienne said:

I would pester ask Deja Vu nicely. Advanced World Generation has many new setpieces and works with ANR.

The code in my second post is the one Deja Vu uses in is Advanced World generation mod.

But at the moment it is okay for me to use this. I just wondered why nothing from the so called "Tutorial" for setpieces, worked :D

Link to comment
Share on other sites

@DarkXero thanks :)
To not open a new thread:
1) How can I prevent set_pieces to spawn at the DragonFly arena? I guess I could remove "Badlands" from the tasks, but it should be possible to spawn somewhere in the badlands, but just not at the arena :D

2) How can I prevent that set_pieces spawn at exactly the same location? I had 2 setpieces in the same game at the same location last time :D

Link to comment
Share on other sites

7 hours ago, Serpens said:

The code in my second post is the one Deja Vu uses in is Advanced World generation mod.

But at the moment it is okay for me to use this. I just wondered why nothing from the so called "Tutorial" for setpieces, worked :D

Oh no, I wasn't talking about reusing the code as is. I meant asking nicely if he could take a look into this thread. He's helpful.

Link to comment
Share on other sites

16 hours ago, Serpens said:

thx
but this is exactly the way I wrote it in my second post ^^ (second code).

I think I will stay with this method, since this is the global worldcount.. and for now I only have set_pieces that I want 1-5 times per world.

Sorry. I missed that.

Link to comment
Share on other sites

Deja Vu replied at his Advances World Generation Mod:

Quote

@Serpens66
Sorry the other mod project [his improved version of Worldgeneration Mod I asked for] is unfinished and due to that, it has unfinished code in it, is not documented and the mod does not work at all (crashes etc). Due to that, i don't think it can help you at all. I myself can't help you either as my focus from DST has gone to a new direction and since i am always in a hurry, even now i am almost stepping out the door as we speak to go to work, i don't have much time either.

My quick help/tip for you would be to GREP (if you are on Windows, use grepWIN software) around the game files and see how Klei does it. Then try and search for API traces found around the code.

Anyway good luck to you, be stronger and have more patients than i had with this game.

As I already said, I will use the way it is in the actual world generation mod from Deja Vu (only define biomes to spawn and global world count) ...

And for not spawning in dragonfly area... I think I have no choice then excluding Badlands from the possible biomes...
And I can't prevent, that two or more setpieces spawn at exactly the same location... (of course I could remove the "ignore_impassable" thing, but then I would have again the problem, that the setpiece is not placed at the world at all)
 

Link to comment
Share on other sites

They were default setpiece of Teleportato parts, but I added:

but it also happens with that Pigguards set piece and the farmplot set piece from game.
Last game I had teleportato part, guarding pigs (these one with wodden walls, called "PigGuardsB") and a farmplot at the same location.

All they had in common is the way I activated them
 

local Layouts = GLOBAL.require("map/layouts").Layouts

AddTaskSetPreInitAny(function(tasksetdata)
    if tasksetdata.location ~= "forest" then
        return
    end

    tasksetdata.set_pieces["PigGuardsB"] = { count = 10, tasks={"Make a pick", "Dig that rock", "Great Plains", "Squeltch", "Beeeees!", "Speak to the king", "Forest hunters", "Badlands", "Befriend the pigs", "For a nice walk", "The hunters", "Magic meadow", "Frogs and bugs"}}
end)

 

Edited by Serpens
Link to comment
Share on other sites

Not only the setpieces can spawn at the same location, they also don't care about:
1) DragonFlyArena

Spoiler

20160831163455_1.jpg


2) Pigking
3) And also they can spawn at the same location like the Portal :D

Spoiler

20160906211241_1.jpg



All I made was adding some chests to the static layout.
And the layout code is:

require("constants")
local StaticLayout = require("map/static_layout")

return  StaticLayout.Get("map/static_layouts/pigguards_easy2",
      {
      start_mask = PLACE_MASK.NORMAL,
      fill_mask = PLACE_MASK.IGNORE_IMPASSABLE_BARREN_RESERVED,
      layout_position = LAYOUT_POSITION.CENTER,
      defs = -- Define a choice list for substitution below
          {
              chest = { "pandoraschest" },
          },
      })

which does, except of the chest, exactly the same like the game intern PigGuards...
And it is added the way I posted in the previous post...

I really don't get, why they spawn at the same location as other setpieces...

Edited by Serpens
Link to comment
Share on other sites

17 minutes ago, Serpens said:

I really don't get, why they spawn at the same location as other setpieces...

Bad luck. You don't even need mods enabled.

I remember getting one of those pig torch things right next to the portal.

Or a cave entrance right on top of the dragonfly arena.

Use

PLACE_MASK.IGNORE_IMPASSABLE_BARREN

so they don't appear on the reserved space of other set pieces.

Edited by DarkXero
Link to comment
Share on other sites

9 minutes ago, DarkXero said:

Bad luck.

I remember getting one of those pig torch things right next to the portal.

Or a cave entrance right on top of the dragonfly entrance.

Use


PLACE_MASK.IGNORE_IMPASSABLE_BARREN

so they don't appear on the reserved space of other set pieces.

thank you :)

but this will again decrease the chance for spawning them at all, right?
So I guess I will at least add this for the smaller pigtorch setpiece :) And also try it for the bigger one and see if it decrease the chance it spawns..

Link to comment
Share on other sites

5 hours ago, Serpens said:

but this will again decrease the chance for spawning them at all, right?

Correct.

But then again, this happens for all the other set pieces of the game, as you already saw those "Warning! Game couldn't find space for Wasps in node Beeees!" in the logs. Can't be really dodged unless the procedural generation is redone.

Another thing you can do, is make a table with many rooms, and then preinit your layout there.

local rooms_for_pigs = {"BGCrappyForest", "BGForest"}
local pick = math.random(#rooms_for_pigs)

AddRoomPreInit(rooms_for_pigs[pick], function(room)
	if room.contents.countstaticlayouts == nil then
		room.contents.countstaticlayouts = {}
	end
	room.contents.countstaticlayouts["PigGuardsB"] = function() return 1 end
end)

Instead of appending the set pieces of the task set, you append a room picked at random.

Maybe use "BarePlain" and "BGBadlands" instead of ditching away the entire task.

 

Another thing you can do, is keep all masks normal, and make a custom component and attach it to the world.

Then make your layouts have a special prefab (create one named "serpensmarker").

When the world is generated, the component counts how many instances of the special prefab exist (one per layout).

If you got less instances (a set piece failed to find space), you run a c_regenerateworld() command.

If you got the exact number of instances, you save that result in the component, so the world won't regenerate.

 

You can also add your brand new tasks to the taskset, and include your set pieces on those. That way they will only collide with other set pieces of yours (since the game set pieces will go to the default tasks or specific rooms you can avoid including in your tasks).

So yeah, have fun.

Link to comment
Share on other sites

11 minutes ago, DarkXero said:

Correct.

But then again, this happens for all the other set pieces of the game, as you already saw those "Warning! Game couldn't find space for Wasps in node Beeees!" in the logs. Can't be really dodged unless the procedural generation is redone.

Another thing you can do, is make a table with many rooms, and then preinit your layout there.


local rooms_for_pigs = {"BGCrappyForest", "BGForest"}
local pick = math.random(#rooms_for_pigs)

AddRoomPreInit(rooms_for_pigs[pick], function(room)
	if room.contents.countstaticlayouts == nil then
		room.contents.countstaticlayouts = {}
	end
	room.contents.countstaticlayouts["PigGuardsB"] = function() return 1 end
end)

Instead of appending the set pieces of the task set, you append a room picked at random.

Maybe use "BarePlain" and "BGBadlands" instead of ditching away the entire task.

 

Another thing you can do, is keep all masks normal, and make a custom component and attach it to the world.

Then make your layouts have a special prefab (create one named "serpensmarker").

When the world is generated, the component counts how many instances of the special prefab exist (one per layout).

If you got less instances (a set piece failed to find space), you run a c_regenerateworld() command.

If you got the exact number of instances, you save that result in the component, so the world won't regenerate.

 

You can also add your brand new tasks to the taskset, and include your set pieces on those. That way they will only collide with other set pieces of yours (since the game set pieces will go to the default tasks or specific rooms you can avoid including in your tasks).

So yeah, have fun.

thank you very much :) I will bookmark this and if I have again problems with that, I will try it :)

At the moment I made an additional option to the mod settings. You can choose whether you want
PLACE_MASK.NORMAL
PLACE_MASK.IGNORE_IMPASSABLE_BARREN
PLACE_MASK.IGNORE_IMPASSABLE_BARREN_RESERVED

And for the teleportato mod, where it is important that one of each thing is in the world, I just use this _RESEVERED thing. It is okay, if two setpieces are on the same location in rare cases.

Link to comment
Share on other sites

@Arlesienne

BGCrappyForest  is not a setpiece. It is a room.
So do you want to know where the setpieces are, or the rooms? :D

Setpieces are mostly defined as Layouts. You can find the Layout definitions in scripts/map folder. Most of the lua files you find there, contains some layouts.
Most layouts use static_layouts, these are in the scripts/map/static_layouts folder. There you can exactly see, what they contain.

Rooms are defined scripts/map/rooms folder. There you have different files for room categories. BGCrappyForest is in the terrain_forest.lua.
There you can see, what BGCrappyForest will contain.

But I also did not find out, how the chances for BGCrappyForest to appaer or any other room are... =/

Edited by Serpens
Link to comment
Share on other sites

7 hours ago, Serpens said:

@Arlesienne

BGCrappyForest  is not a setpiece. It is a room.
So do you want to know where the setpieces are, or the rooms? :D

Setpieces are mostly defined as Layouts. You can find the Layout definitions in scripts/map folder. Most of the lua files you find there, contains some layouts.
Most layouts use static_layouts, these are in the scripts/map/static_layouts folder. There you can exactly see, what they contain.

Rooms are defined scripts/map/rooms folder. There you have different files for room categories. BGCrappyForest is in the terrain_forest.lua.
There you can see, what BGCrappyForest will contain.

But I also did not find out, how the chances for BGCrappyForest to appaer or any other room are... =/

My thanks, good sir/madam. I suppoaw I will have to go static_layouts-hunting. What interests me is a method of counting and teleporting to things like the dragonfly arena, guardian pig gardens (that's probably not a professional name :P) and so on.

Link to comment
Share on other sites

On 7.9.2016 at 2:47 AM, DarkXero said:

local rooms_for_pigs = {"BGCrappyForest", "BGForest"}
local pick = math.random(#rooms_for_pigs)

AddRoomPreInit(rooms_for_pigs[pick], function(room)
	if room.contents.countstaticlayouts == nil then
		room.contents.countstaticlayouts = {}
	end
	room.contents.countstaticlayouts["PigGuardsB"] = function() return 1 end
end)

 

I tried this now.
But there can be 0 to x of each room in the world. So it is not controllable how many will be placed in the end. Or is it?

Is there a way to still define the total number ot setpiece in the world, like it is done in AddTaskSetPreInitAny, but instead of using a task, I want to use a room.
Cause I think the tasks are not a good place to add setpeices. Since it seems all tasks are made for a purpose, and it is not wise to add a setpiece to them.
The rooms are much better to handle and I know which rooms do not contain important things (like pigking, pigvillage and stuff like this). Thats why I would like to add a total world number to a choosen list of rooms.

Link to comment
Share on other sites

3 hours ago, Serpens said:

I tried this now.
But there can be 0 to x of each room in the world. So it is not controllable how many will be placed in the end. Or is it?

Notice that you have a function to return the number of the static layouts to put.

local max_PigGuardsB = 1
local current_PigGuardsB = 0

AddRoomPreInit("BGCrappyForest", function(room)
	if room.contents.countstaticlayouts == nil then
		room.contents.countstaticlayouts = {}
	end
	room.contents.countstaticlayouts["PigGuardsB"] = function()
		if current_PigGuardsB < max_PigGuardsB then
			if math.random() < 0.5 then
				current_PigGuardsB = current_PigGuardsB + 1
				return 1
			end
		end
		return 0
	end
end)

AddRoomPreInit("BGForest", function(room)
	if room.contents.countstaticlayouts == nil then
		room.contents.countstaticlayouts = {}
	end
	room.contents.countstaticlayouts["PigGuardsB"] = function()
		if current_PigGuardsB < max_PigGuardsB then
			if math.random() < 0.3 then
				current_PigGuardsB = current_PigGuardsB + 1
				return 1
			end
		end
		return 0
	end
end)

Game uses math.random, mostly, for stuff like mushroom rings.

But you can introduce your own variables too.

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

×
  • Create New...