Jump to content

Fullmoon Perk


Recommended Posts

Hey there

I'm trying to cook up a perk for my custom character, however I'm having trouble with LUA.

Essentially I'm trying to have my character automatically fall asleep when a Fullmoon begins, as if eating a mandrake.

I then want the character to lose 10% of their size as a means to hamper kiting efficiency. I want this to stack each fullmoon (-20%, -30% etc) but the scale should reset back to 1 if they die.

what I had so far didn't really work.

 

local function OnIsFullmoon(inst, isfullmoon)
    if isfullmoon then
		doareasleep(inst, TUNING.MANDRAKE_SLEEP_RANGE, TUNING.MANDRAKE_SLEEP_TIME)
		inst:DoTaskInTime(3)
		inst.Transform:SetScale(0.9, 0.9, 0.9)
    end
end

truth is I don't really know what "inst" is, and I don't know how to reference the player either.

Link to comment
Share on other sites

Obviously you're no good at it if you're just starting out ;)
Not to worry, I have a nice starter post right here for you. It should help you get started.

Your current code might be correct, but it won't do anything if your function isn't called anywhere. I could write out the whole thing, but since you have quite a bit of work to do if you want those perks, it'd be better if you took some time to get into LUA. Especially if you want to publish the mod, because then you have to make sure everything is done in a way that is compatible with other mods.

Link to comment
Share on other sites

11 hours ago, Ultroman said:

Obviously you're no good at it if you're just starting out ;)
Not to worry, I have a nice starter post right here for you. It should help you get started.

Your current code might be correct, but it won't do anything if your function isn't called anywhere. I could write out the whole thing, but since you have quite a bit of work to do if you want those perks, it'd be better if you took some time to get into LUA. Especially if you want to publish the mod, because then you have to make sure everything is done in a way that is compatible with other mods.

Thank you, I appreciate it

Link to comment
Share on other sites

As ungrateful as it may sound, I strongly doubt I'll learn an entire programming language for what probably comes down to about 5 or 6 lines of lua. I have no plans for further modding outside of this perk.

Link to comment
Share on other sites

Please do not read this as an angry message or something. I'm just being real here :)

This isn't as simple as you're making it sound. It's not super complicated either, but there are quite a few things you need to do, to have this work properly (see below). Unless you want me to write the whole thing (which I don't have time for since I'm occupied with my master thesis), you're either going to have to enlist someone else on the forum, or learn LUA and do it yourself. Programming is an invaluable skill, and what you learn here will translate to any other programming or scripting endeavor you approach later in your life, and increasingly many jobs require some knowledge of programming/scripting (they're even teaching it to kids now). I can highly recommend learning it. LUA won't take long to learn, especially the simple stuff, but it might take a week or two of a few hours per day to master the syntax, and a bit longer to learn how to work with tables/lists (which you won't have to use for this project). After that, it's just reading the game code and code from other mods, to see how things are done. You can easily figure out how things with fullmoon works, by simply searching the game script files for the word "fullmoon", and check out the results (use Notepad++ and use CTRL-SHIFT-F to get the "Find in files" dialog up. See my notes on this in my newcomer's post I linked earlier). There are also examples of how to use some of the most prevalent functions (everything you need, at least) in the last link I sent you. You can do this! Really :)

What I think you'll need to do:

You need to keep a counter variable on the player saying how many full moons have happened, and then in your function apply the appropriate scale by incrementing this counter variable every full moon, and using this counter variable to calculate the appropriate scale, and then apply it. You also need to make sure that there is a minimum scale your character can be, so it doesn't disappear completely.

You then also have to react to the event of your character dying, which should reset the counter variable to 0 and reset the scale of your character.

You also need to make sure to have your character save the counter variable, so when the character enters the game next time, you can load the counter variable, and apply the scale.

I'm not sure what you want

inst:DoTaskInTime(3)

to do, but it's supposed to be used like this

inst:DoTaskInTime(3, function(inst)
	-- Do something after 3 seconds
end)

or

inst:DoTaskInTime(3, myFunction)

 

Edited by Ultroman
Link to comment
Share on other sites

@Ultroman this will be the last I ask of you.

I've managed to get an extremely bare working setup for this. Until I can learn more to polish it all and make it nice and smooth, my last problem is having the game remember the scale on leaving/rejoin. I've found SavedScale.lua which I believe is what I need however I don't understand how I can get it to actually do its thing.

Link to comment
Share on other sites

12 hours ago, Hell-met said:

Thank you for being understanding. your post gives me some motivation and I will embark on this journey.

That's awesome! :D Good luck with it! We'll be ready on the forum for any specific questions you might have.

54 minutes ago, Hell-met said:

@Ultroman this will be the last I ask of you.

I've managed to get an extremely bare working setup for this. Until I can learn more to polish it all and make it nice and smooth, my last problem is having the game remember the scale on leaving/rejoin. I've found SavedScale.lua which I believe is what I need however I don't understand how I can get it to actually do its thing.

Actually, it has nothing to do with SavedScale.lua, although it does sound very much like the right thing :) Sadly, though, I've never made a character mod, so I can't be too sure about this, but I'll give it a try.

Look at the woodie.lua prefab in the game files. The onsave function saves his beaverness in the variable data.isbeaver (data is an object with variables to be saved), and the onpreload reads data.isbeaver and uses its value to make Woodie a beaver if the variable is 'true', while the onload does something after the whole character has been "built".

I think something like this would be sufficient (but look in woodie.lua at the bottom, to see where these functions are linked up to be called):

local function onpreload(inst, data)
	-- Read the variable from the data-object and set it to a global variable on the player.
    inst.myvariable = data.myvariable
end

local function onload(inst)
	-- Do your scaling here, using the global variable on the player (use inst.myvariable).
end

local function onsave(inst, data)
	-- Read the global variable from the player and set it to a variable on the data-object.
    data.myvariable = inst.myvariable
end

 

Edited by Ultroman
Link to comment
Share on other sites

@Ultroman

local function onsave(inst, data)
	local size = inst.Transform:GetScale()
    data.size = self.size
end

local function onload(inst)
    if data.size ~= nil then
	self.size = data.size
	end
end

This does not appear to be working. 

I can't figure out how "data" works, really.

Link to comment
Share on other sites

Well, you don't want to save the scale. You want to save how many full moons have passed since the character last died, right? Since the scale will be calculated from that number.

Also, when you create a local variable, you don't refer to it using "self". Local variables are referred to simply by their name.

If you look at my example above again, you'll see that i do NOT try reading data in onload, since there is no parameter called data. The loaded data is only passed to onpreload, which is where you should read the value and put it on the player.

inst is simply your player prefab. Don't be afraid to use it. If you look at my example, try doing exactly that.

local function calculateScale(numberoffullmoons)
	-- Just a fail-safe
	if numberoffullmoons == nil then
		return 1
	end

	local scale = 1 - (0.1 * numberoffullmoons)
	-- Make sure there is a minimum scale
	if scale < 0.5 then
		scale = 0.5
	end
	return scale
end

local function setScale(inst, scale)
	-- Just a fail-safe
	if inst == nil or scale == nil then
		return
	end

	inst.Transform:SetScale(scale, scale, scale)
end

local function onpreload(inst, data)
	-- Read the variable from the data-object and set it to a global variable on the player.
	inst.numberoffullmoons = data.numberoffullmoons
end

local function onload(inst)
	-- Do your scaling here, using the global variable on the player (use inst.numberoffullmoons).
	-- In the event that inst.numberoffullmoons is nil, we do not want to do any scaling.
	if inst.numberoffullmoons ~= nil then
		-- Do scale calculation and apply scale
		setScale(inst, calculateScale(inst.numberoffullmoons))
	end
end

local function onsave(inst, data)
	-- Read the global variable from the player and set it to a variable on the data-object.
	data.numberoffullmoons = inst.numberoffullmoons
end

 

Then, in your OnFullMoon function, you'd do something like this:

if inst.numberoffullmoons == nil then
	-- The first time we hit a full moon, inst.numberoffullmoons is nil, so we specifically set it to 1
	inst.numberoffullmoons = 1
else
	inst.numberoffullmoons = inst.numberoffullmoons + 1
end

 

Instead of all the nil-checking, you could just always set inst.numberoffullmoons to 0 in your master_postinit, but I'm not sure if preload is called before or after master_postinit, so there might be overwrite issues. Can't know for sure without testing. The approach I've put forth should work regardless (I hope).

Edited by Ultroman
Link to comment
Share on other sites

@Ultroman

I'm afraid your blocks do not work even though I've copy pasted them properly in. Though I'd rather come up with my own material.

How does the game remember data? How do I decide what data is saved? How do I ensure it's loaded each time?

Is there no way to just save the scale itself each time instead of this whole counting thing?

What is it with the prefix "data."? How can I tell if something is a component or just a made up variable? Why doesn't the game recognize "inst" on the preload?

Edited by Hell-met
Link to comment
Share on other sites

Well, even if it should work, it's all just written in this text editor, is not complete, and not tested at all. The theory should work, but in order to see why it doesn't work for you, I would need you to post your prefab LUA file, to see if you've done it properly.

How does the game remember data?
Every prefab and component is "scanned" for OnSave, OnLoad and OnPreload (the latter possibly only for prefabs, since components seem to get the data in their OnLoad function?) functions when the prefab or component is initialized. In the case of a player, whose character is basically also just a prefab, the data is compiled into a big pile when the player quits or the server is shut down. This is what happens in the OnSave function, so you are adding to that pile of data (the data-object). This data-pile is linked to their username or something, so when they log into the server again, the server sees their username, checks if it has a player character saved for that username, and if so, it instantiates the player character prefab, and loads the saved data which is passed to the OnPreload function.

How do I decide what data is saved?
Deciding is one part, and doing it is another. I'll answer about deciding in the last question, and just answer for this one, that you save the data by simply adding variables to the data-object, like you see me doing in my onsave example.

How do I ensure it's loaded each time?
The game engine takes care of that automatically, as long as you "hook up" your functions, like you can see near the bottom of woodie.lua.

inst.OnSave = onsave
inst.OnLoad = onload
inst.OnPreLoad = onpreload

Is there no way to just save the scale itself each time instead of this whole counting thing?
You can just save the scale if you want, but that will not get you the functionality you want. You would have to read the scale from the scale-vector each time, then subtract whatever value you want, make sure it isn't under a certain minimum, and set the scale. Since you'd still be saving a variable, the complexity is about the same, but this way is just a bit neater, although it requires having the numberoffullmoons variable on the inst.

You could use the Scaler-component instead. If you add that to your player with AddComponent("scaler"), it will save and load the scale for you, and you can simply call its SetScale function to set a new scale. It defaults (starts at) 1. It takes a single float, instead of a Vector3, so it's easier to work with. Then you can just do:

-- Getting and changing the scale once per full moon change

-- First, get the current scale. This is a float between 0.0 and 1.0
local currentScale = inst.components.scaler.scale

-- Then make a change to the scale.
currentScale = currentScale - 0.1

-- Do a minimum check
if currentScale < 0.5 then
	currentScale = 0.5
end

-- Set the new scale
inst.components.scaler:SetScale(currentScale)

If you use the scaler component, you don't need to do the save, load and preload stuff I mentioned earlier. I just found the scaler-component. Didn't know about it until now.

Link to comment
Share on other sites

@Ultroman

well I got this now 

local function onsave(inst, data)
    local currentScale = inst.components.scaler.scale
end

local function onpreload(inst, data)
    inst.components.scaler:SetScale(currentScale)
end

local function onload(inst)
    inst.components.scaler:SetScale(currentScale)
end

but it says scale is a "nil value" when I start a fresh new world.

I'd rather keep the unecessary things like minimum size and whatnot out of the way until I get the clear goal working.

What is even the difference between preload and load? What does the (inst, data) mean after the function?

we're so close I can feel it.

Edited by Hell-met
Link to comment
Share on other sites

You should really take the LUA crash course. I can't describe the whole language in a post. You need a grasp of the language you're using, or you'll be wasting a lot of your time going back and forth with me.

As I said, you no longer need the onsave, onpreload and onload stuff, if you're using the scaler-component. Only the code I posted at the bottom of my last post (put that in your OnFullMoon function), and adding the scaler-component to the player (put this in your master_postinit: AddComponent("scaler") ).

The minimum size is not unnecessary in the least, and it's not complex code. You can comment out that if-statement if you want, but it won't make a difference for the problems you're having.

I'm not entirely sure what the difference is between onload and onpreload, other than that they're called at different times during loading the prefab, but the data-parameter is the important part, since it holds the saved data, and onload doesn't have that, so you have to read the data in onpreload. As mentioned earlier, this is different for components, which do not have onpreload, and instead get their data passed to their onload function.

But again, you don't need to worry about that anymore, since the scaler-component takes care of saving and loading the scale.

Edited by Ultroman
Link to comment
Share on other sites

@Ultroman I am sorry but nothing you propose me actually works. I trust I incorporate your blocks well because I get no crash when I test them.

Yet every rejoin is always back to scale 1. Be it counting fullmoons, be it scaler.lua. No small char. It's pointless to have a long term, stacking perk if the game forgets about it every time.

I've fiddled so much with this it has gone past the point of ridiculousness. I do not want to bother you anymore with what is otherwise probably extremely easy for most LUA folks.

SavedScale.lua appears to be my only hope left. I thank you again for your patience and generousness.

Link to comment
Share on other sites

You've obviously tried a lot of things by now, which is all I ask of people; to try things out for themselves. You have something now that I can look at. I only try to not give people the whole solution, because they don't learn anything that way, and it makes me feel like a code-ATM :)

I'll see what you have and get back to you ;)

This should work melium.lua

Change log:

  • I removed the savedscale component, since the scaler component saves the scale internally.
  • You were still reading and setting inst.Transform's scale, which is a Vector3, instead of using the scaler component's scale variable, which is a float, and which saves automatically. Changed that.
  • You were setting the scale directly on inst.Transform, like this, inst.Transform:SetScale(scale, scale, scale), where it expects three floats, but you gave it three Vector3's.
  • I changed it so it just subtracts 0.1 from the scale each time. You can change it back to just multiply the scale by 0.9 if you want.
  • Added the minimum size if-statement, to avoid it going crazy when it gets too small.
  • Made it send isfullmoon parameter to your shrink function.
  • Removed manual call to shrink function from the master_postinit.

If it still doesn't work, you need to bust out some print-statements to see if everything is being called correctly.

Edited by Ultroman
Link to comment
Share on other sites

@Ultroman Unfortunately still doesn't work, and I've changed nothing from your version. Still always rejoin with default scale. Truly maddening.

I shall try reading more about this print business.

Come to think of it, Wolfgang gains size when eating. how does the server remember that scale bonus when they rejoin?

Edited by Hell-met
Link to comment
Share on other sites

That sounds like a great thing to study :)

It actually isn't. They do Wolfgang's size in a completely different way. They rely on a change in hunger (which does happen all the time) to keep triggering mightiness checks, and they don't change his scale, they change his whole skin.

Anyway, if you haven't figured out print-statements, take a look at my newcomer post. It explains it.

I would just put print-statements everywhere, printing out both inst.Transform:GetScale() and inst.components.scaler.scale, and also what is happening e.g. put one in master_postinit, one in your fullmoon function, one inside the if-statement in your OnFullMoon function...everything.

Just get as much information you can. I bet it's something simple and stupid. It always is, haha.

Good luck, and welcome to the world of coding. It's 99% "why doesn't it work?!" or "why does it work?!?" and 1% "Yes, it works!!"

Really, what you have now SHOULD work.

Edited by Ultroman
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...