Jump to content

Recommended Posts

[MENTION=55]Ipsquiggle[/MENTION] and so u don't get bored, u can take a look in here. :shttp://forums.kleientertainment.com/showthread.php?19911-Desperate-cry-for-help!-lt-Anim-file-!I rly could use some help there and nobody seems to have an idea. :<EDIT: Never mind. I fixed it. I never understood anything what happend there, but it worked. xD

Edited by milson

Anyone know how to replace a function in a component with your own (via modmain.lua)? I actually want to dig into pickables.lua and do this:

function Pickable:Fertilize(fertilizer)

--fertilizer:Remove() --This is all I want to do, but I don't want to include the whole file in the mod

self.cycles_left = self.max_cycles

self:MakeEmpty()

end

I don't think you can without including the class, you can replace it on an instance for pickable not on the class itself. Actually on second thought, what about attaching to the ComponentPostInit and replacing there the method, I do not know for sure but I belive you do get the component so

AddComponentPostInit("pickable", function(comp)      comp.Fertilize = function(fertilize)           comp.cycles_left = comp.max_cycles           comp:MakeEmpty()      end)end)
Had not tested so don't know if it works.

Last night/this morning I tried to add my first full prefab to the game. Thanks to lots of people here, like [MENTION=10271]WrathOf[/MENTION], [MENTION=12700]Heavenfall[/MENTION], [MENTION=38597]zeidrich[/MENTION], and [MENTION=43228]milson[/MENTION] and his potions mod, everything was really easy, EXCEPT for some weirdness around inventory/recipe images.

Empirically, here are some things I think are right, but I haven't seen something corresponding in the forums to confirm or deny it. I am leaving off the stuff that is well documented in the forums and seems accurate.

mymod/prefab.xml

[*]The file attribute of the Prefab element doesn't seem to be used at all. I put bad words to test it, and no matter what I put, after my prefab was working, it never seemed to matter what went here.

[*]The pathname attribute of the Prefab element is just a string. The directory/namespace structure of it is stylistic not syntactic.

[*]Assets of type IMAGE must (?) live in the mod folder? (I tested this quite a bit and I never got things working until I moved inventory assets to my mod folder.)

[*]Each File in the Files element is extremely important, however do not put the full mod path as some docs on the forums state you should do. Put the path relative to mods/mymod/scripts/.

mymod/modmain.lua

From what heavenfall said in what I think is the 20th post of this thread, and mixed in with what I first saw WrathOf do, these are my first two lines of my modmain.lua.

-- First thing in the file, outside of any callback, eventqueue, etc.GLOBAL.package.path = MODROOT.."scripts\\?.lua;"..GLOBAL.package.pathGLOBAL.TheSim:LoadPrefabDefs(MODROOT.."prefab.xml")
This seems to be key to making all new instances created of my prefabs work, as well as make sure that previously saved instances actually load.

The above was not enough to make sure my inventory images and recipes worked.

fixing inventory/recipe images

Looking at milson's potion mod, I copied what he did because it was the only thing that seemed to work. This is a minimal version of what I'm doing in my code.

AddGamePostInit(function()    -- EDIT: variable no longer needed. See note below.    --local imgpath = "mods/Potions/inventoryimages/"    -- Reload/rereference images with the fixed package.path reference.    GLOBAL.TheSim:LoadPrefabDefs( MODROOT.."prefabs.xml" )    GLOBAL.LoadPrefabFile("prefabs/perpetulog.lua")    GLOBAL.TheSim:LoadPrefabs({"perpetulog"})    -- EDIT: Pointed out by heavenfall that this isn't needed. After testing,    -- I can confirm he is correct. Commenting out. It was harmless before,    -- but extraneous.    --GLOBAL.Recipes("perpetulog").image = imgpath.."perpetulog.tex"end)
Unless I do all of these things together, I might have any of the following problems.

[*]Prefabs saved from a previous game will not load (this seems to happen if I don't put the first LoadPrefabDefs in modmain.lua)

[*]Doing what I do in modmain.lua fixes instances of prefabs that get created. the first time, but it's not enough to make sure the inventory images show up after an item has been saved.

[*]To fix the inventory images on prefab instances that have been saved/reloaded I need to reload the prefabs and correct image references (most of which is done automagically if I just call the functions again after package.path has been updated, except for the Recipe(?) ).

This is cool to do, but if I'm missing something I'd like to know. I'd also like to know if there is an update in the future that will break what I'm doing above, or better render it unnecessary, if we could get a warning about it so we can change our mods.

YET ANOTHER EDIT I'm just updating this so as not to spam anyone else who doesn't care about this. I was really OCD about whether or not I needed to do all of the above, and it turns out that by copying some other mod (I forget which) I was placing an errant image setting in my prefabpostinits:

    -- After this...    inst:AddComponent("inventoryitem")    -- ... I was doing this and it is wrong, wrong, wrong.    --inst.components.inventoryitem.imagename = "artwork"
By getting rid of that errant, and incorrect assignment, all I need are the two lines in modmain.lua, a correct prefab, and all the files in the right directories and things Just Work. Phew. Edited by borrisb
IMAGE, not ANIM, corrected code, small grammar changes

[MENTION=44717]borrisb[/MENTION] looks like you have all the necessary steps. Are images not showing up somewhere?

GLOBAL.Recipes("perpetulog").image = imgpath.."perpetulog.tex"
the above should not be necessary when you have added the package.path

Hi [MENTION=12700]Heavenfall[/MENTION].The images are showing up in all places they should: crafting recipe tab, inventory, when first created, after loading a pre-existing character. All is good.Thank you for the correction to the recipe. I confirmed it is not needed, and edited my code above. Yay! It's nice to have a complete template to work from.

  • Developer

I would love to know where to look to find the draw distance? I have searched all the camera scripts but I'm assuming it's coded somewhere else? I didn't see anything in the tuning scripts either. Also, is it possible to set the camera to free movement as opposed to the incremented rotations?

Most of the camera rendering bits are controlled engine-side, completely separated from Lua, so that won't be accessible from a mod.As far as rotating the camera, have a look at scripts\components\playercontroller.lua. In PlayerController:RotLeft() and PlayerController:RotRight(), you can see how the camera is being rotated by the arrow keys. You could use a similar method in your own code to rotate the camera.
  • Developer

@Ipsquiggle

And one other thing, could you add hooks before serializing and after deserializing the map? I already did this however main.lua and gamelogic.lua need to be changed and I don't want to provide my own overwrites for these. main.lua will not event load from the mod because that is where the mods are initialized, gamelogic perhaps (did not check).

I can have a look into this.. But is there anything in particular you gain from this that you can't do by spawning an entity (say, an invisible one) which stores the data and gamestate that your mod requires? (This is they way we try to do things internally, every bit of functionality is always "owned" by some component or entity that exists in the world somewhere.) This way that entity can have multiple components, get de/serialized, have update functions, etc. etc. If you are doing something that is kind of "long term" like a quest system or scenarios or anything like that, I recommend doing this anyways, having a "questmanager" component, and only using modmain to "set up" the situation.

However, your goals are of course slightly different than ours, and if that seems like the wrong approach, let me know! :)

[MENTION=55]Ipsquiggle[/MENTION]So we have OnSave and OnLoad functions in the SeasonManager component, I added winter and summer length variables to both of them.

function SeasonManager:OnSave()    print("OnSave")    return     {	    winterlength = self.winterlength,		print("winter", self.winterlength),	    summerlength = self.summerlength,		print("summer", self.summerlength),		noise_time = self.noise_time,		percent_season = self.percent_season,		current_season = self.current_season,		ground_snow_level = self.ground_snow_level,		atmo_moisture = self.atmo_moisture,		moisture_limit = self.moisture_limit,		precip = self.precip,		precip_rate = self.precip_rate,		preciptype = self.preciptype,		moisture_floor = self.moisture_floor,		peak_precip_intensity = self.peak_precip_intensity,		nextlightningtime = self.nextlightningtime	}end
function SeasonManager:OnLoad(data)    print("OnLoad Called")    self.winterlength = data.winterlength	print("winter", data.winterlength)    self.summerlength = data.summerlength	print("summer", data.summerlength) 	self.noise_time = data.noise_time or self.noise_time	self.percent_season = data.percent_season or self.percent_season	self.current_season = data.current_season or self.current_season	self.ground_snow_level = data.ground_snow_level or self.ground_snow_level	self.atmo_moisture = data.atmo_moisture or self.atmo_moisture	self.moisture_limit = data.moisture_limit or self.moisture_limit	self.precip = data.precip or self.precip	self.precip_rate = data.precip_rate or self.precip_rate	self.preciptype = data.preciptype or self.preciptype	self.moisture_floor = data.moisture_floor or self.moisture_floor	self.peak_precip_intensity = data.peak_precip_intensity or self.peak_precip_intensity	self.nextlightningtime = data.nextlightningtime or self.nextlightningtime		self.inst:PushEvent("snowcoverchange", {snow = self.ground_snow_level})	if self:IsWinter() then		self:ApplyDSP(0)		self.inst:PushEvent( "seasonChange", {season = self.current_season} )			end		if self.precip and self.preciptype == "rain" then		self.inst.SoundEmitter:PlaySound("dontstarve/rain/rainAMB", "rain")	end	self:UpdateSegs()end
Next I added function to get my random numbers from tuning overrides like this:
function SeasonManager:SetRandomSeasonLengths(summer, winter)    local per = self:GetPercentSeason()	self.winterlength = winter	self.summerlength = summer	self:SetPercentSeason(per)	self:UpdateSegs()end
Added lines to tuning overrides
GetSeasonManager():SetRandomSeasonLengths(15 + math.random(-10, 10), 15 + math.random(-10, 10))
It all works quite well, but the problem is the function SetRandomSeasonLengths(summer, winter) if fired up every time I load the game and overwrites the saved values with new one, is there a way to tell to that function that we loaded saved data and stop it?Something like if saved data not nil then return.
  • Developer

@Ipsquiggle

I need to get random world coordinates, is there a way to find spawn point for something not dependent on player position in the world? Right now it gets player position and then spawn things at some distance from the player, If I use big distance from the player it can't find valid spawn point.

We don't have a specific clean way of doing this at the moment. GetWorld().topology.nodes contains a list of all the "areas" in the level, with .cent being the center of the area, and .poly being a polygon which covers the area. You could pick a random node and then spawn something near its center or within its polygon.

I also need access to list of prefabs and their coordinates in the world, FindEntity function with big radius can slow down the game.

I'm not entirely sure what you mean by this.. as mentioned, GLOBAL.Ents is a list of every entity; FindEntities is a tool for making a small list of entities centered around a point; any other kind of list you'd have to build yourself according to your needs, probably by searching into GLOBAL.Ents or collecting the list in another way. If you're a bit more specific we could see if we can figure something out for you.

Next thing is I would like to despawn some chunk of terrain on map and then spawn different room or set piece in place of old terrain so world would be changing with time.

If you look at components/terraformer.lua you can see how things like the Pitchfork change a single tile at a time, it would be the same process, over all the points/tiles that you deem worthy.

Next thing is can you make set pieces independent from world settings, right now if I have berry bushes set piece (static layout) and I set berries to less I have like the half ring of berry bushes instead of full, you can find some other ways to disable static layout if the recourse that static layout is using is disabled or make versions of static layouts for every resource setting.

I'm not quite sure what you mean here. Do you mean that if say, there was a "berry patch" static layout with lots of berries in it, and your world settings were set to "no berries" you would not want that layout to spawn at all, or something?
  • Developer

GetSeasonManager():SetRandomSeasonLengths(15 + math.random(-10, 10), 15 + math.random(-10, 10))
It all works quite well, but the problem is the function SetRandomSeasonLengths(summer, winter) if fired up every time I load the game and overwrites the saved values with new one, is there a way to tell to that function that we loaded saved data and stop it?Something like if saved data not nil then return.
Yep, you almost got it!
if GetSeasonManager().winterlength == nil and GetSeasonManager().summerlength == nil then    GetSeasonManager():SetRandomSeasonLengths(15 + math.random(-10, 10), 15 + math.random(-10, 10))end
Does that make sense?
  • Developer

So Destroy "spiderden", I know that i need to track the prefab spiderden, however this means I need to modify the modmain.lua and add a PostPrefabInit handler for that prefab, I don't want that, I want in the Destroy class to take the ent given and search all ents of that type to add a handler. If I change my mind and want a pighouse again I need to go to modmain and change that

One way of handling this would be to have a component on the player which listens for the "killed" event. When an entity is attacked and dies, it sends "killed" to its attacker with it's own instance. You can then use that to determine what the player just killed:
-- on some component on the player:self.inst:ListenForEvent("killed", function(inst, data)    print("I just killed a " .. data.victim.prefab) -- data.victim is the ent that was just killed.end )
Would that work for you?

I'm not quite sure what you mean here. Do you mean that if say, there was a "berry patch" static layout with lots of berries in it, and your world settings were set to "no berries" you would not want that layout to spawn at all, or something?

The number of prefabs spawned in static layout is related to the world settings. Lets say that world generation script picked to spawn circle of stones in my world, but then it saw that world is set to less stones, and instead of spawning circle of stones it spawns only half of the circle. It looks bad that way.So we could have static layouts for every setting, or simply protect them in some way so they will look like they should.Thank You for help with season manager.If you find a while can you look at my mushroom mess in this thread http://forums.kleientertainment.com/showthread.php?19890-Help-Mushrroms-prefab Edited by _Q_
  • Developer

Here's another thought, maybe @Ipsquiggle can confirm/deny. Findentity and findentities are probably just functions that access the global prefab list. I think it is unlikely that they are performing some kind of search on each square within distance, because that would require them to access the global prefab list anyway first to build the square information.

Effectively that's how it works. We use some optimizations that make the search fast for small radii, but that's the gist of it.

Nope, not doing anything. Not sure I'm calling it right tho...

AddComponentPostInit("pickable", function(comp)      comp.Fertilize = function(fertilize)	  print "we are here!"           comp.cycles_left = comp.max_cycles           comp:MakeEmpty()      endend)AddComponentPostInit("pickable", fertilize)

I don't think you can without including the class, you can replace it on an instance for pickable not on the class itself. Actually on second thought, what about attaching to the ComponentPostInit and replacing there the method, I do not know for sure but I belive you do get the component so

AddComponentPostInit("pickable", function(comp)      comp.Fertilize = function(fertilize)           comp.cycles_left = comp.max_cycles           comp:MakeEmpty()      end)end)
Had not tested so don't know if it works.

You need to pass additional variables when you do this kind of overwriting/adding. This is the example from tutorial:

function clockpostinit(inst)	--create the colour for the fullmoon	inst.fullColour = Point(120/255, 120/255, 160/255)	--save the original function	inst.StartNightPre = inst.StartNight	--override a class function, and call the original function first	--doing it this way allows the game to change while still keeping your own edits[B]	inst.StartNight = function(self,inst)[/B]		self:StartNightPre(inst)	        if self.phase ~= self.previous_phase then	                self.previous_phase = self.phase	                if self:GetMoonPhase() == "full" then	                     self:LerpAmbientColour(self.currentColour, self.fullColour, instant and 0 or 8)	                else	                     self:LerpAmbientColour(self.currentColour, self.nightColour, instant and 0 or 8)	                end		end	endendAddComponentPostInit("clock", clockpostinit)
Pay attention to how they pass self as an extra variable in the function declaration.
  • Developer

@Ipsquiggle BTW, I made a mod to remap the keys for AZERTY keyboards and that's all it does and felt like sharing it. But it would be nice if we didn't need it at all. Do you know if there are plans for an update to recognize automatically the keyboard localization ? (didn't want to create a thread just for this so I posted it here)

Better keyboard options are something we're aware we need... :( Using mods to solve the problem in the short-term is a great idea though!
  • Developer

Last night/this morning I tried to add my first full prefab to the game. Thanks to lots of people here, like @WrathOf, @Heavenfall, @zeidrich, and @milson and his potions mod, everything was really easy, EXCEPT for some weirdness around inventory/recipe images.

*snip*prefabs.xml, modmain.lua, inventory images*snip*

The first major thing I'm working on (spurred by Heavenfall's helpful suggestions) is to make this part of the process much easier. It will require just one line of code per prefab, and you shouldn't have to use prefabs.xml at all for standard prefabs. (It's something we use for static dependency checking, something you shouldn't have to worry about!) Keep an eye out for this on the next release. :)

In the meantime, I agree, this looks like the correct template.

For standard prefabs? Will you need them for nonstandard prefabs? One thing I think would be nice, speaking of dependencies, would be some way to have a dependency on another mod. Like if you had a ZooKeeper_Core mod which enables a bunch of pet handling functionality, and then a ZooKeeper_Tiger mod that adds a tiger, and ZooKeeper_Monkey that adds a monkey but both require ZooKeeper_Core. If somehow you had ZooKeeper_Tiger enabled, but ZooKeeper_Core disabled or not installed, ZooKeeper_Tiger would somehow be able to tell and show a prompt, maybe explaining where to get ZooKeeper_Core and fail gracefully. Likewise, it could force ZooKeeper_Core to load first before trying to load ZooKeeper_Tiger.Right now, if you try to make such dependencies it's either impossible or it crashes to desktop if the dependency doesn't exist. Don't know if it's needed. I'm just ramblin' I think some cool stuff could be done if we can create more modular mods. Frameworks within frameworks.

  • Developer

For standard prefabs? Will you need them for nonstandard prefabs?

Err, I meant for a "normal" prefab, one that doesn't try to do anything too freaky. Should cover most people's cases.

One thing I think would be nice, speaking of dependencies, would be some way to have a dependency on another mod. Like if you had a ZooKeeper_Core mod which enables a bunch of pet handling functionality, and then a ZooKeeper_Tiger mod that adds a tiger, and ZooKeeper_Monkey that adds a monkey but both require ZooKeeper_Core. If somehow you had ZooKeeper_Tiger enabled, but ZooKeeper_Core disabled or not installed, ZooKeeper_Tiger would somehow be able to tell and show a prompt, maybe explaining where to get ZooKeeper_Core and fail gracefully. Likewise, it could force ZooKeeper_Core to load first before trying to load ZooKeeper_Tiger.Right now, if you try to make such dependencies it's either impossible or it crashes to desktop if the dependency doesn't exist. Don't know if it's needed. I'm just ramblin' I think some cool stuff could be done if we can create more modular mods. Frameworks within frameworks.

I think that's a pretty awesome idea, actually, but probably not a priority at the moment. For the immediate future, I want to focus on just making the basic act of creating mods easier for everyone, and for now the problem of dependencies can be solved (in the most bare way) with good descriptive text. ;) But I think it's great for people to make mods and extensions for other people's mods!

I guess my only concern is that sometimes if there's good descriptive text it needs to be on the download page, or in the mod package. If you do it wrong you just get a "dontstarve.exe has stopped working" or an attempt to access a nil value.I suppose you could do something like:

test = YOU_NEED_TO_DOWNLOAD_ZOOKEEPER_CORE___PLEASE_VISIT_www_website_com_TO_DOWNLOAD+1 
in modmain.luaC:/Games/DontStarve/mods/happiness/modmain.lua:16: attempt to perform arithmetic on global 'YOU_NEED_TO_DOWNLOAD_ZOOKEEPER_CORE___PLEASE_VISIT_www_website_com_TO_DOWNLOAD' (a nil value)I kid. Rough day :)
  • Developer

You should be able to do something like:

local dependencies = { "dependencyname" }for _,v in GLOBAL.ModManager:GetModNames() do  for i,v2 in dependencies do    if v == v2 then      table.remove(dependences, i)      break    end  endendassert(#dependencies > 0, "Not all dependencies are installed")
Note that I haven't tested the above, but in theory it should work ;)
  • Developer

Oh, looks like you can do:

local reqs = table.contains(GLOBAL.ModManager:GetModNames(), "superPoop")GLOBAL.assert(reqs, "This mod requires the super poop mod.")
It's still up to the user to put the mods in modsettings.lua in the correct order, and whatnot, but at least you have an escape hatch. :) Edited by Ipsquiggle

Dependencies to me are good if they work, bad if they aren't great, so if you do implement them, definitely take your time.Is there an easy way to control the load message that pops up if a mod fubars? That would be something nice to have, and at least we could put some good info in it, although I like zeidrich's approach ;)

[MENTION=55]Ipsquiggle[/MENTION] can you please consider making the height of the CraftTabs (crafting.lua) dynamic after the amount of different RECIPETABS? Currently, if we want to add a new craft tab, it will push itself outside the background of the crafttabs, and we have to provide an override for crafting.lua with our mods as a result.

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
×
  • Create New...