Jump to content

Add "sun immunity" effect on umbrellas, and freeze screen help


SolidZero

Recommended Posts

Hello everyone, today I am here to ask for some help on this:

 

I got a "vampire" custom character, it is working perfectly. Since she is a vampire, during the day, she looses heath(with negative health regeneration) over time, and during Dusk and Night she start regenerating health.

Again, this is working perfectly, but the is something I don't know how to start: I want to make her stop loosing heath during the day by wearing an umbrella(any of them). That's my main goal here, but I want help with something else:

 

My character(the same vampire) is immune to freeze damage since I used the fallowing comand:

 

inst.components.temperature.hurtrate = 0

 

But there is a problem: I don't take damage from freezing, but the game keep showing the "damage screen".

What can I do to stop it? Thank you everyone c: SOLVED!

Link to comment
Share on other sites

	inst.components.temperature.mintemp = 20

is the code I've used for my yeti-themed character to disable cold from freezing them. About the umbrella thing, I don't have much of a clue sadly

 

 

Thank you a lot, you just solved this problem for me =D.

In my case, I used " inst.components.temperature.mintemp = 1 ", so I get the freezing screen, but don't get the "red damage screen effect".

 

Now I just need to find a way to the other problem.

Link to comment
Share on other sites

Would checking for what type of item she has equipped when calling the negative regen function not work for you? By default the game adds the tag "umbrella" to both the pretty parasol and umbrella, so you could call HasTag()  on the equipped item.  If there are any additional umbrella items in your mod you can simply use AddTag("umbrella") in their constructor.  You might also consider checking the hat slot for eyebrella as well.

Link to comment
Share on other sites

Would checking for what type of item she has equipped when calling the negative regen function not work for you? By default the game adds the tag "umbrella" to both the pretty parasol and umbrella, so you could call HasTag()  on the equipped item.  If there are any additional umbrella items in your mod you can simply use AddTag("umbrella") in their constructor.  You might also consider checking the hat slot for eyebrella as well.

 @Corrosive That really helped me a lot, but I don't know exactly the path to the equipped item, and obviously it crashes. Here what I did:

 

if inst.components.equippable.HasTag("umbrella") -- Umbrella sunblock

inst.components.health:StartRegen(0, 2) -- Life loss during day

 
 
in " inst.components.equippable.HasTag("umbrella") " you know what path I should use?
Link to comment
Share on other sites

I would highly, highly recommend downloading the n_tools mod-- the "dig" function allows you to explore object properties inside the game while it runs.

 

 

In any case, there seems to be a few bits of confusion you're having regarding how the game engine and Lua work, so I'll try to clarify those:

 

Problem 1:

 

When calling a method like HasTag() that requires itself be passed as an argument, you should to use a colon( : ) instead of a period ( . ).  Think of it this way:

object.something <-----this tells Lua to look at the table referenced by the "object" variable, and return the value stored in the "something" index.  If there is a function in the "something" index, Lua will return a reference to that function, but it won't execute it.

 

object.something() <----- this tells Lua to look at the table referenced by the "object" variable, and call(execute the function stored in the "something" index, and possibly return a value, depending on the function.  The only arguments that get passed to the function are those specified within the parentheses (no arguments would be passed in this example.)

 

object:something() <----- this tells Lua to look at the table referenced by the "object" variable, and call(execute) the function stored in the "something" index, and possibly return a value, depending on the function.  In addition to any arguments specified within the parentheses, this form also passes a reference to object as the first argument.  Arguments that you enter will be the second, third, etc.

 

Problem 2

 

"inst" refers to a specific instance of an object, and will almost always be an "entity" object, since entities comprise most of the things you'll want to deal with(unless you're making a custom component or something like that.)  The word "inst" is used many times, but doesn't always refer to the same type of thing.  Sometimes it refers to the character, some times it refers to an item, etc.

 

HasTag() is a method that needs to be used directly on the entity, and not a component or some other property of that entity(unless that property happens to be a reference to ANOTHER entity!)

 

Assuming that "inst" is your character, you can access the equipped item through "inst.components.inventory.equipslots.hands".  It's VERY important to note though, that this property won't exist if nothing is in the hand slot, so you need to test for it first if you don't want to crash whenever nothing's equipped.

 

I also imagine that you are applying regen once upon each day state change, which means you need to execute that code every time the game would heal/hurt you from regen, which means you would need to provide your own version of the regen function and insert the umbrella check there.  Then, in the part of your code that has StartRegen(), you would instead use your custom regen function.  You may also need to pass the regen target in your custom regen method, so you can check the inventory each time.  Ideally you would add this on to the health component so you could call components.health:VampireRegen(inst).  But that's a whole 'nother topic, and I don't have the time to write the whole mod for you.  I would suggest popping open the game files and looking at how the devs do things.

 

From within your custom regen method, you would do something like:

 

if not ( inst.components.inventory.equipslots.hands and inst.components.inventory.equipslots.hands:HasTag("umbrella") ) then

     <regen as normal>

end

 

Problem 3:

 

Your original post mentioned "inst.components.temperature.hurtrate = 0", which suggests that the "inst" variable inside this block of code is a reference to your character. "inst.components.equippable.HasTag("umbrella")" suggests that the "inst" variable is a reference to the equipped item.  I can't tell if you have two different "inst" variables in different code blocks, or are mistakenly referring to the same one because you didn't post any context.

 

 

Problem 4:

 

This is my own problem.  It's 3 in the morning and I'm really tired, so I apologize if I committed egregious typos there.

 

 

Edit: Fixed issue that Blueberrys pointed out.  The writer of that section has been sacked and replaced with a new one.

Link to comment
Share on other sites

 Thank you very much again. Yes, I am kind of new to Lua =S sometimes I forget some details, but what you wrote has very helpful.

right now, I am trying to add a new instance to the "health" component. So, I tried to implement it from "modmain.lua" , like this:

 

....

local function VampireRegen()
if GetClock():IsDay() then
if self.components.inventory.equipslots.hands:HasTag("umbrella") then
self.components.health:StartRegen(0, 2)
else
self.components.health:StartRegen(-1, 2)
end
end
if GetClock():IsDusk() then
self.components.health:StartRegen(1, 2)
end
if GetClock():IsNight() then
inst.components.health:StartRegen(1, 2)
end
end

AddPrefabPostInit("health",VampireRegen)

 

and I replaced my regen lines with this:

 

if GetClock():IsDay() then
inst.components.health:VampireRegen()
inst.components.health.maxhealth = 150

 

but the game crashes, and that what the log says:

 

" ...rve/data/../mods/Remilia/scripts/prefabs/remilia.lua:84: attempt to call method 'VampireRegen' (a nil value) "

 

I know that I have other things to fix here, but right now, I am trying to call the method, and then I will take care of the rest.
I also tried calling the "hand equiped slot"  to compare with the "umbrella" tag, but as you said, it crashes when I am wearing nothing, OR it simply does nothing, since the "checker" only runs wen the daytime changes(day/dusk/night)(I tried both methods, just to have an idea of what would be the result)

 

Here is my actual mod file for anyone who want to help here, and to clarify "problem 3":

local MakePlayerCharacter = require "prefabs/player_common"


local assets = {

Asset( "ANIM", "anim/player_basic.zip" ),
Asset( "ANIM", "anim/player_idles_shiver.zip" ),
Asset( "ANIM", "anim/player_actions.zip" ),
Asset( "ANIM", "anim/player_actions_axe.zip" ),
Asset( "ANIM", "anim/player_actions_pickaxe.zip" ),
Asset( "ANIM", "anim/player_actions_shovel.zip" ),
Asset( "ANIM", "anim/player_actions_blowdart.zip" ),
Asset( "ANIM", "anim/player_actions_eat.zip" ),
Asset( "ANIM", "anim/player_actions_item.zip" ),
Asset( "ANIM", "anim/player_actions_uniqueitem.zip" ),
Asset( "ANIM", "anim/player_actions_bugnet.zip" ),
Asset( "ANIM", "anim/player_actions_fishing.zip" ),
Asset( "ANIM", "anim/player_actions_boomerang.zip" ),
Asset( "ANIM", "anim/player_bush_hat.zip" ),
Asset( "ANIM", "anim/player_attacks.zip" ),
Asset( "ANIM", "anim/player_idles.zip" ),
Asset( "ANIM", "anim/player_rebirth.zip" ),
Asset( "ANIM", "anim/player_jump.zip" ),
Asset( "ANIM", "anim/player_amulet_resurrect.zip" ),
Asset( "ANIM", "anim/player_teleport.zip" ),
Asset( "ANIM", "anim/wilson_fx.zip" ),
Asset( "ANIM", "anim/player_one_man_band.zip" ),
Asset( "ANIM", "anim/shadow_hands.zip" ),
Asset( "SOUND", "sound/sfx.fsb" ),
Asset( "SOUND", "sound/wilson.fsb" ),
Asset( "ANIM", "anim/beard.zip" ),

-- Don't forget to include your character's custom assets!
Asset( "ANIM", "anim/remilia.zip" ),
}
local prefabs = {}

local commentMoonlight = 1


local function remiliafood(inst, food)
if inst.components.eater and food.components.edible.foodtype == "VEGGIE" then
inst.components.sanity:DoDelta(-10)
inst.components.talker:Say("Its good, but I can't eat only vegetables.")

elseif inst.components.eater and food.prefab == "fish" then
inst.components.sanity:DoDelta(5)
inst.components.health:DoDelta(20)
inst.components.hunger:DoDelta(25)

elseif inst.components.eater and food.prefab == "monstermeat" then
inst.components.sanity:DoDelta(15)
inst.components.health:DoDelta(25)
inst.components.hunger:DoDelta(25)

elseif inst.components.eater and food.prefab == "cookedmonstermeat" then
inst.components.sanity:DoDelta(20)
inst.components.health:DoDelta(30)
inst.components.hunger:DoDelta(35)
inst.components.talker:Say("Delicious!")

elseif inst.components.eater and food.prefab == "monstermeat_dried" then
inst.components.sanity:DoDelta(0)
inst.components.health:DoDelta(5)
inst.components.hunger:DoDelta(10)
inst.components.talker:Say("There is no blood in it.")

elseif inst.components.eater and food:HasTag("rawmeat") then
inst.components.sanity:DoDelta(20)
inst.components.health:DoDelta(20)
end


end

local function updatestats(inst)


inst.components.hunger.maxhunger = 150


if GetClock():IsDay() then
inst.components.health:VampireRegen()
inst.components.health.maxhealth = 150
inst.Light:Enable(false)
inst.components.sanity.dapperness = TUNING.DAPPERNESS_SMALL*-1
inst.components.hunger:SetRate(TUNING.WILSON_HUNGER_RATE * 1.1)
inst.components.locomotor.walkspeed = (TUNING.WILSON_WALK_SPEED * 0.9)
inst.components.locomotor.runspeed = (TUNING.WILSON_RUN_SPEED * 1)
inst.components.combat.damagemultiplier = 0.5
elseif GetClock():IsDusk() then
inst.components.health.maxhealth = 200
inst.Light:Enable(false)
inst.components.sanity.neg_aura_mult = 1.0
inst.components.locomotor.walkspeed = (TUNING.WILSON_WALK_SPEED * 1.2)
inst.components.locomotor.runspeed = (TUNING.WILSON_RUN_SPEED * 1.4)
inst.components.combat.damagemultiplier = 1.5
inst.components.hunger:SetRate(TUNING.WILSON_HUNGER_RATE * 0.1)
elseif GetClock():IsNight() then
if GetClock():GetMoonPhase() == "full" then
inst.components.health.maxhealth = 400
--regeneration speed 2hp/ 1sec
inst.components.combat.damagemultiplier = 2.3
inst.Light:Enable(true) -- Only works on FullMoon
inst.components.hunger:SetRate(-5*TUNING.WILSON_HUNGER_RATE)
inst.components.sanity.night_drain_mult = -0.5
inst.components.locomotor.walkspeed = (TUNING.WILSON_WALK_SPEED * 1.8)
inst.components.locomotor.runspeed = (TUNING.WILSON_RUN_SPEED * 1.8)

if commentMoonlight == 1 then
inst.components.talker:Say("The Moon...yes...I can feel all her power inside me!")
commentMoonlight = 0
end

end
elseif GetClock():IsNight() then
inst.components.health.maxhealth = 250
--regeneration speed 1hp/ 1sec
inst.components.combat.damagemultiplier = 2.0
inst.components.hunger:SetRate(1*TUNING.WILSON_HUNGER_RATE)
inst.components.sanity.night_drain_mult = -0.5
inst.components.temperature.inherentinsulation = 30*6
inst.components.locomotor.walkspeed = (TUNING.WILSON_WALK_SPEED * 1.3)
inst.components.locomotor.runspeed = (TUNING.WILSON_RUN_SPEED * 1.3)
end
end

local fn = function(inst)

-- choose which sounds this character will play
inst.soundsname = "willow"

-- a minimap icon must be specified
inst.MiniMapEntity:SetIcon( "wilson.png" )

inst.components.eater:SetOnEatFn(remiliafood)

-- todo: Add an example special power here.
inst.components.sanity.max = 130
-- time listeners
inst:ListenForEvent( "dusktime", function() updatestats(inst) end , GetWorld())
inst:ListenForEvent( "daytime", function() updatestats(inst) end , GetWorld())
inst:ListenForEvent( "nighttime", function() updatestats(inst) end , GetWorld())
inst.components.temperature.mintemp = 1





-- Night-vision
inst.entity:AddLight()
inst.Light:Enable(false)
inst.Light:SetRadius(4)
inst.Light:SetFalloff(0.2)
inst.Light:SetIntensity(0.9)
inst.Light:SetColour(100/255,100/255,245/255)

updatestats(inst)

return inst

end


return MakePlayerCharacter("remilia", prefabs, assets, fn)

 

Again, you really helped me a lot here. Still, any kind of help from you or anyone are welcome. thx.

Link to comment
Share on other sites

@SolidZero,

 

AddPrefabPostInit does not add your function as a callable child to the prefab. It simply executes that function once after initializing the prefab. Hence the "post init".

Additionally, "health" is not a prefab, it is a component. You would need to use AddComponentPostInit to modify it.

 

To add a child function which you can later call, you would have to append it as a child yourself, as so.

local function your_funct()    -- stuffendlocal function component_post(self)    -- Here "self" is (or should be) referencing the component's instance.    self.your_function_name = your_functend-- This runs the component_post function after initializing the component.AddComponentPostInit("component_to_modify", component_post)
Link to comment
Share on other sites

object.something <-----this tells Lua to look at the table referenced by the "object" variable, and return the value stored in the "something" index.  If there is a function in the "something" index, Lua will return a reference to that function, but it won't execute it.

 

From my experience, the dot syntax does execute if used with parentheses, just not in the same manner. The colon is used to automatically pass "self" as the first parameter, and a dot when referencing the function as-is.

I'm no expert on lua, but.. Stack Overflow post on the issue.

 

I believe you are correct that object.something without the parentheses only references the function, but that fact doesn't explain the difference in using either;

object.something()

or

object:something()

 

Link to comment
Share on other sites

Dang.....I am really out of date with coding e-e sry. 

 

A did some new tests, at first got some crashes, but after some fixes the game runs normally, but with no effect.(nothing special on the log)

 

So, here is what I did on my modmain.lua: 

local function VampireRegen(daytime)
-- stuff
if daytime == "day" then
if self.components.inventory.equipslots.hands:HasTag("umbrella") then
self.components.health:StartRegen(0, 2)
else
self.components.health:StartRegen(-1, 2)
end
end
if daytime == "dusk" then
self.components.health:StartRegen(1, 2)
end
if daytime == "night" then
inst.components.health:StartRegen(1, 2)
end
end

local function health_post(self)
-- Here "self" is (or should be) referencing the component's instance.
self.VampireRegen = VampireRegen              -- not sure if I did it right
end

-- This runs the component_post function after initializing the component.
AddComponentPostInit("health", health_post)

 

And here, how I called it from my prefab(remilia.lua):

 

local function updatestats(inst)


inst.components.hunger.maxhunger = 150


if GetClock():IsDay() then
inst.components.health:VampireRegen("day") -- calling the function
inst.components.health.maxhealth = 150

.............

 

 

Link to comment
Share on other sites

@Blueberrys Sry to look like a dumb, I don't know exactly if did what you said. I tried some things that crashed the game, and some simple still did nothing. here how I let it:

 

 

local function VampireRegen(self, daytime)
-- stuff
if daytime == "day" then
if self.components.inventory.equipslots.hands:HasTag("umbrella") then
self.components.health:StartRegen(0, 2)
else
self.components.health:StartRegen(-1, 2)
end
end
if daytime == "dusk" then
self.components.health:StartRegen(1, 2)
end
if daytime == "night" then
inst.components.health:StartRegen(1, 2)
end
end

 

How I called it:

if GetClock():IsDay() then
inst.components.health:VampireRegen("self", "day")

.........

 

I tried to use " self " without quotes, but the game crashes since I have not initiated a variable with this name.

Link to comment
Share on other sites

"self" with quotes does nothing but pass the string "self" as a parameter.

 

I don't think the way this code is written will get you the effect you're looking for.  Take a step back and think about exactly what you're trying to achieve.  Then read slowly through your code and think about what is happening at each step, and what it will result in.  You are certainly getting closer, though.

 

 

Let me append what I said above about functions, because I didn't quite tell the whole story as Blueberrys pointed out.

 

Let's create an object and give it a method (this is in global namespace for simplicity):

function SomeMethod(self)    print(self.value)end-- create a blank tableobject = {} -- add a "SomeMethod" index, and point it at the SomeMethod function aboveobject.SomeMethod = SomeMethod  -- give the object a "value" index, and store the string "Hi there :D" in itobject.value = "Hi there :D"  

Now, since our SomeMethod function makes use of "self", we need to pass it somehow.  There are two ways to do this.

 

The first way is to pass the object as an argument to the "self" parameter:

-- Note that this form uses a period.  -- With this form, you must manually enter the self argument.object.SomeMethod(object)

The second way uses the colon syntax:

-- Note that this form uses a colon.-- With this form, object is automatically passed as the first argument.object:SomeMethod()

Since the colon form automatically passes itself, you only need to enter arguments past the first parameter.  In the case of your Vampire regen, it can be written as:

inst.components.health:VampireRegen("day")

This will pass both arguments as you intend.  I have a love/hate relationship with languages that do this type of shorthand.  In my opinion it can make code a bit harder to understand at a glance.

 

I apologize if I caused any confusion due to my earlier post.

 

Link to comment
Share on other sites

"self" with quotes does nothing but pass the string "self" as a parameter.

Yeah, I knew about that, I tried just to make sure I tested everything.

 

I did what you said, but now, the game crashes, and the log says:

 

" .../common/dont_starve/data/../mods/Remilia/modmain.lua:50: attempt to index field 'hands' (a nil value) "

 

in this line:

 

if self.components.inventory.equipslots.hands:HasTag("umbrella") then " (here it crashes in "components")

 

I tried to chande "self" to "GetPlayer()" in the same line, and then I got the actual error.

 

if GetPlayer().components.inventory.equipslots.hands:HasTag("umbrella")"

Link to comment
Share on other sites

@SolidZero, you are trying to access an item in the "hands" slot, but there isn't any there, so it gives you a nil value. You should check whether an item exists in the slot before trying to access a function within it.

if self.components.inventory.equipslots.hands and self.components.inventory.equipslots.hands:HasTag("umbrella") then    -- ...end

Edit: You can use self.components when "self" is referring to the player. It does not work in places where "self" refers to something else, or is nil.

For example, you can use it in the character's create fn, but not in an item's create fn.

Link to comment
Share on other sites

Ok, it works. Here what I did:

local function VampireRegen(self, daytime)

-- stuff
if daytime == "day" then
if GetPlayer().components.inventory.equipslots.hands and GetPlayer().components.inventory.equipslots.hands:HasTag("umbrella") then
GetPlayer().components.health:StartRegen(0, 2)
else GetPlayer().components.health:StartRegen(-1, 2)
end
end
if daytime == "dusk" then
self.components.health:StartRegen(1, 2)
end
if daytime == "night" then
inst.components.health:StartRegen(1, 2)
end
end

 

Now, the thing is: I know the function is being called successfully, but, if I equip my Umbrella, I keep loosing HP. I think it is because the function is only being called at the start of a new daytime(day/dusk/night).

If I remember right, I have to deal with "DoPeriodicTask" now, right? Already taking a look at this. Any other tip/advice are welcome. You guys being awesome ;D 

Link to comment
Share on other sites

Got it fully working!

Here:

 

if GetClock():IsDay() then
inst.task = inst:DoPeriodicTask(2, function() inst.components.health:VampireRegen("day") end)
 
Now, I think I just need to balance some perks, and then adapt it to DST.
 
Again, Thank you very much guyz. Both of you will be at this mod credits.
Link to comment
Share on other sites

Now, the thing is: I know the function is being called successfully, but, if I equip my Umbrella, I keep loosing HP. I think it is because the function is only being called at the start of a new daytime(day/dusk/night).

If I remember right, I have to deal with "DoPeriodicTask" now, right? Already taking a look at this. Any other tip/advice are welcome. You guys being awesome ;D 

 

This is exactly the problem I was talking about when I said it might not work how you intended ;)  I just wanted you to figure that one out for yourself.  You're on the right track! Keep pushing forward and you'll be done before you know it.

Link to comment
Share on other sites

Got another question: Is there any way better to heal/damage my character during daytime? I am actually using "StartRegen", but it is a bit buggy, sometimes after an entire day, it stops healing and damaging my character, and I thing that is the reason.

 

Also, I searched the forums for more details on this "timing" system(I think it is called "tick", right?) with no luck. If there is any thread/tutorial/anything about this, can anyone link it to me? Thanks

Link to comment
Share on other sites

inst.task = inst:DoPeriodicTask(2, function() inst.components.health:VampireRegen("day") end)

 

The DoPeriodicTask method returns an object containing task information and methods which you've stored in inst.task.  You may wish to call inst.task:Cancel() whenever the daytime period changes, before starting a new type of regen.  This is not optimal, however.

 

From my first post:

 

Then, in the part of your code that has StartRegen(), you would instead use your custom regen function.

 

Why exactly?  Your VampireRegen function should provide the functionality of StartRegen(), rather than call it.  If you look in health.lua, you'll notice

function Health:StartRegen(amount, period, interruptcurrentregen)

By default any new regen instance will override the last regen task, which is not what you want happening to your VampireRegen.  You should only need one instance of the task. I would name it VampireRegenTask to ensure that it isn't being overridden by something else.  Start this task in the prefab's initialization function.

 

You probably wont need all the functionality of DoRegen.  In fact, you'll really only need a small amount(you can add on functionality as you see necessary-- I would just get the basics working first),  If you follow the trail of functions, you'll notice that all it's doing that's important is making calls to Health:SetVal(val, cause).  The val parameter is just the amount healed, and the cause parameter is just a string that the game can use to check certain special cases(for example, "ressurection_penalty" or "file_load").

 

 

 

 

Link to comment
Share on other sites

Sry for taking too long to answer. I got it working by myself. The thing is, wen I used to call my "vampireRegen", every single time it was called, it activates "StartRegen". It completely ****** up with the game. During the night, it I was loosing and regenerating health at the same time, and sometimes, on day 2, nothing happens! my health simply stayed still.

 

To solve that, I put a "StopRegen" on the start of every instance of day/dusk/time(since they are called just once) and called my "VampireRegen" only during the day, so, Dusk and Night now have they own "StartRegen"(obviously, after the "StopRegen").

With that, the mod is running just fine now, and the mod itself is already updated with these new features. As I said, I gave credits to you guys. Thak you agan ;D

Link to comment
Share on other sites

Sry for taking too long to answer. I got it working by myself. The thing is, wen I used to call my "vampireRegen", every single time it was called, it activates "StartRegen". It completely ****** up with the game. During the night, it I was loosing and regenerating health at the same time, and sometimes, on day 2, nothing happens! my health simply stayed still.

 

To solve that, I put a "StopRegen" on the start of every instance of day/dusk/time(since they are called just once) and called my "VampireRegen" only during the day, so, Dusk and Night now have they own "StartRegen"(obviously, after the "StopRegen").

With that, the mod is running just fine now, and the mod itself is already updated with these new features. As I said, I gave credits to you guys. Thak you agan ;D

 

That's one of the reasons you'd probably want your own health regen function.  The other reason is because other instances of health regen will override yours until your function is called again.

Link to comment
Share on other sites

Archived

This topic is now archived and is closed to further replies.

Please be aware that the content of this thread may be outdated and no longer applicable.

×
  • Create New...