Sign in to follow this  
Amalleus

Custom food port issue

Recommended Posts

Amalleus    13

Hello. I have issue with the custom food which i ported to DST. The issue is that spell doesn't work properly.

DS code:

local assets={Asset("ANIM", "anim/geralt_swallowpot.zip"),Asset("ATLAS", "images/inventoryimages/geralt_swallowpot.xml"), } local prefabs={  }  local function item_oneaten(inst, eater)     if eater.geralt_swallowpot then        eater.geralt_swallowpot.components.spell.lifetime = 0        eater.geralt_swallowpot.components.spell:ResumeSpell()    else        local regen = SpawnPrefab("geralt_swallowpot_regen")         regen.components.spell:SetTarget(eater)         if not regen.components.spell.target then             regen:Remove()        end        regen.components.spell:StartSpell()     endend  local function fn()local inst = CreateEntity()inst.entity:AddTransform()inst.entity:AddAnimState()         inst.AnimState:SetBank("geralt_swallowpot")    inst.AnimState:SetBuild("geralt_swallowpot")    inst.AnimState:PlayAnimation("idle")    MakeInventoryPhysics(inst)         inst:AddComponent("inspectable")  inst:AddComponent("stackable")inst.components.stackable.maxsize = TUNING.STACK_SIZE_LARGEITEM         inst:AddComponent("inventoryitem")inst.components.inventoryitem.atlasname = "images/inventoryimages/geralt_swallowpot.xml"      --inst:AddComponent("tradable")         inst:AddComponent("edible")    inst.components.edible.foodtype = "POTION"    --inst.components.edible.healthvalue = 0    inst.components.edible.hungervalue = -TUNING.CALORIES_SMALL    inst.components.edible.sanityvalue = 0    inst.components.edible:SetOnEatenFn(item_oneaten)        return instend  local function regen_resume(inst, time)    local percent = time/inst.components.spell.duration    local var = inst.components.spell.variablesGetPlayer().components.hunger.hungerrate = GetPlayer().components.hunger.hungerrate + GERALT_SWALLOWPOT_HUNGERRATE   end  local function regen_onsave(inst, data)    data.timealive = inst:GetTimeAlive()end  local function regen_onload(inst, data)    if data and data.timealive then        regen_resume(inst, data.timealive)    endend  local function regen_spellfn(inst, target)local player=GetPlayer()if target==playerthen player.components.health:DoDelta(GERALT_SWALLOWPOT_REGENRATE, false, "geralt_swallowpot", true) end;end  local function regen_start(inst) GetPlayer().components.hunger.hungerrate = GetPlayer().components.hunger.hungerrate + GERALT_SWALLOWPOT_HUNGERRATE end  local function regen_ontarget(inst, target)    if not target then return end    target.geralt_swallowpot = inst    target:AddTag(inst.components.spell.spellname)end  local function regen_onfinish(inst)    if not inst.components.spell.target then        return    end    inst.components.spell.target.geralt_swallowpot = nilGetPlayer().components.hunger.hungerrate = GetPlayer().components.hunger.hungerrate - GERALT_SWALLOWPOT_HUNGERRATE end  local function regenfn()       local inst = CreateEntity()    inst.entity:AddTransform()      inst:AddTag("FX")    inst:AddTag("NOCLICK")    local spell = inst:AddComponent("spell")    inst.components.spell.spellname = "geralt_swallowpot"inst.components.spell.period = 1    inst.components.spell.duration = (30 * 2)    inst.components.spell.ontargetfn = regen_ontarget    inst.components.spell.onstartfn = regen_start    inst.components.spell.onfinishfn = regen_onfinish    inst.components.spell.fn = regen_spellfn    inst.components.spell.resumefn = regen_resume    inst.components.spell.removeonfinish = true     return instend  return Prefab( "common/inventory/geralt_swallowpot", fn, assets, prefabs),Prefab("common/inventory/geralt_swallowpot_regen", regenfn)

DST code:

local assets={	Asset("ANIM", "anim/geralt_swallowpot.zip"),	Asset("ATLAS", "images/inventoryimages/geralt_swallowpot.xml"),}local prefabs={}local function item_oneaten(inst, eater)	eater = ThePlayer    if eater.geralt_swallowpot then        eater.geralt_swallowpot.components.spell.lifetime = 0        eater.geralt_swallowpot.components.spell:ResumeSpell()    else        local regen = SpawnPrefab("geralt_swallowpot_regen")	        regen.components.spell:SetTarget(eater)		        if not regen.components.spell.target then	            regen:Remove()        end        regen.components.spell:StartSpell()			    endendlocal function fn()	local inst = CreateEntity()	inst.entity:AddTransform()	inst.entity:AddAnimState()        inst.AnimState:SetBank("geralt_swallowpot")    inst.AnimState:SetBuild("geralt_swallowpot")    inst.AnimState:PlayAnimation("idle")    MakeInventoryPhysics(inst)        inst:AddComponent("inspectable")		inst:AddComponent("stackable")	inst.components.stackable.maxsize = TUNING.STACK_SIZE_LARGEITEM        inst:AddComponent("inventoryitem")	inst.components.inventoryitem.atlasname = "images/inventoryimages/geralt_swallowpot.xml"    --inst:AddComponent("tradable")        inst:AddComponent("edible")    inst.components.edible.foodtype = FOODTYPE.POTION    --inst.components.edible.healthvalue = 0    inst.components.edible.hungervalue = -TUNING.CALORIES_SMALL    inst.components.edible.sanityvalue = 0    inst.components.edible:SetOnEatenFn(item_oneaten)    return instendlocal function regen_resume(inst, time)    local percent = time/inst.components.spell.duration    local var = inst.components.spell.variables	inst:DoTaskInTime(0.2, function()	ThePlayer.components.hunger.hungerrate = ThePlayer.components.hunger.hungerrate + GERALT_SWALLOWPOT_HUNGERRATE 	end)endlocal function regen_onsave(inst, data)    data.timealive = inst:GetTimeAlive()endlocal function regen_onload(inst, data)    if data and data.timealive then        regen_resume(inst, data.timealive)    endendlocal function regen_spellfn(inst, target)	local player=ThePlayer	if target==player	then player.components.health:DoDelta(GERALT_SWALLOWPOT_REGENRATE, false, "geralt_swallowpot", true) end;end local function regen_start(inst) 	ThePlayer.components.hunger.hungerrate = ThePlayer.components.hunger.hungerrate + GERALT_SWALLOWPOT_HUNGERRATE endlocal function regen_ontarget(inst, target)    if not target then return end    target.geralt_swallowpot = inst    target:AddTag(inst.components.spell.spellname)    --target.AnimState:SetBloomEffectHandle("shaders/anim.ksh")endlocal function regen_onfinish(inst)    if not inst.components.spell.target then        return    end    inst.components.spell.target.geralt_swallowpot = nil	ThePlayer.components.hunger.hungerrate = ThePlayer.components.hunger.hungerrate - GERALT_SWALLOWPOT_HUNGERRATE endlocal function regenfn()     local inst = CreateEntity()    inst.entity:AddTransform()    inst:AddTag("FX")    inst:AddTag("NOCLICK")    local spell = inst:AddComponent("spell")    inst.components.spell.spellname = "geralt_swallowpot"	   inst.components.spell.period = 1    inst.components.spell.duration = 60    inst.components.spell.ontargetfn = regen_ontarget    inst.components.spell.onstartfn = regen_start    inst.components.spell.onfinishfn = regen_onfinish    inst.components.spell.fn = regen_spellfn    inst.components.spell.resumefn = regen_resume    inst.components.spell.removeonfinish = true    return instendreturn Prefab( "common/inventory/geralt_swallowpot", fn, assets, prefabs),Prefab("common/inventory/geralt_swallowpot_regen", regenfn)

So, what issue? look, food cast spell after i eat it. If i didn't go off game (DS or DST) during spell duration, issue will not appear - works correctly. But if i came off game during its duration in DST then after come online i get next thing : spell doesn't work (if it even had duration) at all. I found out that only one function worked - "regen_resume" after i came online. How to fix that issue, so spell will continue work after i came online again?

P.S.: If you ask me why I did dotaskintime in regen_resume - this is because function starts before character was initialized (so i get nil on ThePlayer)

Share this post


Link to post
Share on other sites
rezecib    3181

 

eater = ThePlayer

 

This isn't your problem while testing, but you shouldn't do this. It will always execute on the server and refer to the host player. Leave eater as is, it's already referring to a player (or maybe a mob that ate it off the ground).

 

As for where the actual problem is, it's hard to tell. You're using a pretty strange setup-- making a prefab for the regen, and attaching components to a weird place on the player... If you're making the prefab for it, loading the regen back up on rejoining should be handled by that prefab, I guess, in an onsave/onload.

Share this post


Link to post
Share on other sites
Amalleus    13

This isn't your problem while testing, but you shouldn't do this. It will always execute on the server and refer to the host player. Leave eater as is, it's already referring to a player (or maybe a mob that ate it off the ground).

 

As for where the actual problem is, it's hard to tell. You're using a pretty strange setup-- making a prefab for the regen, and attaching components to a weird place on the player... If you're making the prefab for it, loading the regen back up on rejoining should be handled by that prefab, I guess, in an onsave/onload.

i am using setup from wormlight.lua - i took everything from it and just modified

can you give a little more information about loading/saving? This really hard to read spell.lua about how it works

Share this post


Link to post
Share on other sites
Amalleus    13

I know that i can try to repeat spell on loading but with lower time. so i need to repeat again function oneat. ofcourse i will need to add local variable which will have remaining time of spell. is it possible?

Share this post


Link to post
Share on other sites
Amalleus    13

I found out that spell function is casted. But after re-logging to the game spell doesn't see the target of the spell

local function regen_spellfn(inst, target)	print("working!")	if target	then print("even with target") 	target.components.health:DoDelta(GERALT_SWALLOWPOT_REGENRATE, false, "geralt_swallowpot", true) 	endend

before reloading it was: "working! even with target working! even with target..." after reloading: "working! working! ...." How to not lose target after reloading?

Share this post


Link to post
Share on other sites
DarkXero    2892

I'm not sure you should be using the spell component.

Wormlight is the only prefab that uses it and it's not available on DST.

 

You shouldn't be losing the target, on the LoadPostPass the target should be reassigned.

 

In the meantime, maybe this could work?

local assets = {	Asset("ANIM", "anim/geralt_swallowpot.zip"),	Asset("ATLAS", "images/inventoryimages/geralt_swallowpot.xml"),}local function item_oneaten(inst, eater)	local regen = SpawnPrefab("geralt_swallowpot_regen")	regen.components.spell:SetTarget(eater)	regen.components.spell:StartSpell()end local function fn()	local inst = CreateEntity()	inst.entity:AddTransform()	inst.entity:AddAnimState()	inst.entity:AddNetwork()	MakeInventoryPhysics(inst)	inst.AnimState:SetBank("geralt_swallowpot")	inst.AnimState:SetBuild("geralt_swallowpot")	inst.AnimState:PlayAnimation("idle")		inst.entity:SetPristine()	if not TheWorld.ismastersim then		return inst	end	inst:AddComponent("edible")	inst.components.edible.foodtype = FOODTYPE.POTION	inst.components.edible.healthvalue = 0	inst.components.edible.hungervalue = -TUNING.CALORIES_SMALL	inst.components.edible.sanityvalue = 0	inst.components.edible:SetOnEatenFn(item_oneaten)	inst:AddComponent("inspectable")	inst:AddComponent("inventoryitem")	inst.components.inventoryitem.atlasname = "images/inventoryimages/geralt_swallowpot.xml"	inst:AddComponent("stackable")	inst.components.stackable.maxsize = TUNING.STACK_SIZE_LARGEITEM	return instendlocal function regen_ontargetfn(spell, user)	if not user:HasTag("playerghost") then		user.components.hunger.hungerrate = user.components.hunger.hungerrate + GERALT_SWALLOWPOT_HUNGERRATE		spell.components.spell.onfinishfn = function()			user.components.hunger.hungerrate = user.components.hunger.hungerrate - GERALT_SWALLOWPOT_HUNGERRATE		end	endendlocal function regen_spellfn(spell, user)	if not user.components.health:IsDead() then		user.components.health:DoDelta(GERALT_SWALLOWPOT_REGENRATE, true, "geralt_swallowpot", true)	else		spell.components.spell:OnFinish()	endendlocal function regenfn()	local inst = CreateEntity()	inst.entity:AddTransform()	inst.entity:AddNetwork()	inst:AddTag("FX")	inst:AddTag("NOCLICK")	inst.entity:SetPristine()	if not TheWorld.ismastersim then		return inst	end	inst:AddComponent("spell")	inst.components.spell.spellname = "geralt_swallowpot"	inst.components.spell.period = 1	inst.components.spell.duration = 60	inst.components.spell.ontargetfn = regen_ontargetfn	inst.components.spell.removeonfinish = true	inst.components.spell.fn = regen_spellfn	return instend return Prefab("common/inventory/geralt_swallowpot", fn, assets), Prefab("common/inventory/geralt_swallowpot_regen", regenfn)

I suggest that you look into something else, like directly applying the task into the eater or something.

Share this post


Link to post
Share on other sites
Amalleus    13

 

 

Wormlight is the only prefab that uses it and it's not available on DST.

yes, you are right. but i have several items (not only this item) which using "spell" - i don't want to lose effect after reloading. also i need functions OnStart OnFinish and SpellFunction to make some manipulations

As i can see, OnStart and OnFinish is in the OnTarget function. Hm... you have pretty nice algorithmical thinking. I think this code will work. I will check

EDIT: there is no resume function. so after reloading spell just dissapears and that's it

Edited by Amalleus

Share this post


Link to post
Share on other sites
Amalleus    13
  eater = ThePlayer   This isn't your problem while testing, but you shouldn't do this. It will always execute on the server and refer to the host player. Leave eater as is, it's already referring to a player (or maybe a mob that ate it off the ground).

Am i right that ThePlayer - is always the host player and not the current? But what about choose somebody else? And sorry, I just started to understand DST programming =/

Edited by Amalleus

Share this post


Link to post
Share on other sites
rezecib    3181
Am i right that ThePlayer - is always the host player and not the current?
 Not always-- it depends on where the code is running. Some places where it will always be the host player (or nil on a dedicated server-- watch out for that):
  • Most components (especially ones that are game logic, like health, as opposed to display-related, like transparentonsanity)
  • Any code that comes after a "if not TheWorld.ismastersim then return end" block-- this includes master_postinit
  • Functions for character traits that manipulate game logic (they're usually attached to components)

Places where it code will run on all computers and ThePlayer will refer to the local one:

  • Display elements-- HUD widgets, etc
  • replicas (these are copies of certain game logic components that also need to handle some display, like health needs to be accessed on clients to show how much health players have)
  • anything before "if not TheWorld.ismastersim then return end" block -- this includes common_postinit

However, in virtually all cases there's a better way to get the relevant player. For example, almost all component functions have the player or entity that's interacting with something as one of the arguments (e.g. "eater" in several edible component functions, or self.inst/self.owner in most components that are attached to players, and self.owner in widgets).

Share this post


Link to post
Share on other sites
DarkXero    2892

@Amalleus,

 

The spell component is a challenge, I think. I don't know how Klei will make it work.

 

 

My solution to make the spell component work is the following:

 

a) Put this in modmain.lua

AddPrefabPostInitAny(function(inst)	local target = inst	local old3 = target.OnLoad	target.OnLoad = function(inst, data)		if old3 then			old3(inst, data)		end		if data then			for k, v in pairs(data) do				if string.match(k, "Spell_..") then					local s = string.gsub(k, "Spell_", "")					local spell = GLOBAL.SpawnSaveRecord(data[s])					local spellcomp = spell.components.spell					spellcomp:SetTarget(inst)					if spellcomp.active then						spellcomp:ResumeSpell()					end				end			end		end	endend)

b) Use my attached spell component

 

This should make the target stay.

Up to you to continue editing your potion based on wormlight.

spell.lua

Share this post


Link to post
Share on other sites
Amalleus    13

@Amalleus,

 

The spell component is a challenge, I think. I don't know how Klei will make it work.

 

 

My solution to make the spell component work is the following:

 

a) Put this in modmain.lua

AddPrefabPostInitAny(function(inst)	local target = inst	local old3 = target.OnLoad	target.OnLoad = function(inst, data)		if old3 then			old3(inst, data)		end		if data then			for k, v in pairs(data) do				if string.match(k, "Spell_..") then					local s = string.gsub(k, "Spell_", "")					local spell = GLOBAL.SpawnSaveRecord(data[s])					local spellcomp = spell.components.spell					spellcomp:SetTarget(inst)					if spellcomp.active then						spellcomp:ResumeSpell()					end				end			end		end	endend)

b) Use my attached spell component

 

This should make the target stay.

Up to you to continue editing your potion based on wormlight.

Thank you for help. Yesterday i thought about save/load functions, and i found solution without spells.

First of all, the potion is possible to use by certain character. So, oneat function of potion i added 

eater.potionresttime = 60--+ more hunger loss

and in character inst i added:

inst:DoPeriodicTask...... --every 1 second restores health and decreases inst.potionresttime by 1 --when inst.potionresttime == 0 then return previous hungerrate and stop decreasing of variable

save/load functions loading and saving potionresttime

Edited by Amalleus

Share this post


Link to post
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
Sign in to follow this