ksaab

  • Content Count

    72
  • Joined

  • Last visited

 Content Type 

Profiles

Forums

Downloads

Klei Bug Tracker

Game Updates

Hot Lava Bug Reporter

Posts posted by ksaab


  1. 3) Check out the forum, there is a pinned thread about them;

    4) I offer variants to you. How you will implement dependng on your preferences. The last variant doesn't requie a client stategraph;

    5) How do you use dodge without weapon? GetPointSpecialActions? Or key binding? How do you realize cooldown on the client side? You can remove my AddComponentAction if you don't need them.


  2. AddAction("DODGE", "Dodge", function(act)
        act.doer:PushEvent("letsdododge", {pos = act.pos or Vector3(act.target.Transform:GetWorldPosition())})
        return true
    end)
    
    ACTIONS.DODGE.distance = math.huge
    ACTIONS.DODGE.instant = true
    
    AddStategraphEvent("wilson", EventHandler("letsdododge", function(inst, data)
    		inst.sg:GoToState("dodge_pre", data)
    	end)
    )

    I think you want something like this — use dodge while player is moving. It's a bit dirty-tricky and looks weird on clients. But the second could be fixed I suppose.

    Client-stategraph doesn't need any code at all. Other things from the previous post. Dodge direction should be handled in the "pre" state with data.pos.

    One more thing — pos = act.pos will work incorrect after RoT release. Klei has changed this filed. Check out bufferedaction.lua after update.

    Two more thing — 0.25 widnow is too short. Clients with high unstable ping will have problems (I did a parry spell with 0.5 seconds window in The Forge Battle Pack and recieved a lot of complaints)


  3. AddStategraphState("wilson", State 
    {
    	name = "dodge_pre",
    	tags = {"doing", "busy", "evade", "no_stun"},
    
    	onenter = function(inst)
    		inst.components.locomotor:Stop()
    		inst.AnimState:PlayAnimation("parry_pre", false)
    	end,
    
    	events =
    	{
    		EventHandler("animover", function(inst)
    			inst.sg:GoToState("dodge")
    		end),
    	}
    })
    
    AddStategraphState("wilson", State
    {
    	name = "dodge",
    	tags = {"doing", "evade", "no_stun", "nopredict", "nomorph",},
    
    	onenter = function(inst)
    		inst.AnimState:PlayAnimation("parry_loop", true)
    		inst.components.health:SetInvincible(true)
    		inst.components.locomotor:EnableGroundSpeedMultiplier(true)
    		inst.last_dodge_time = GLOBAL.GetTime()
    		inst.Physics:SetMotorVel(-20,0,0)
    
    		--this should be moved to the action code
    		-----------------------------------------
    		if GLOBAL.TheNet:GetServerGameMode() == "lavaarena" then
    			COMMON_FNS.DoFrontAOE(inst, 5, 0, 3.25, "strong", nil)
    			inst:DoTaskInTime(0.5, function()
    				COMMON_FNS.DoFrontAOE(inst, 5, 0, 3.25, "strong", nil)
    			end)
    
    			if inst.laserdodge ~= nil then
    				inst.laserdodge:Cancel()
    				inst.laserdodge = nil
    			end
    		end
    		-----------------------------------------
    
    		inst.SoundEmitter:PlaySound("dontstarve_DLC003/characters/wheeler/slide")
    		inst:PerformBufferedAction()
    
    		--not the best idea
    		--because of latency character will dash for shorter distance then you're expecting
    		--will be much better to calculate target postion and use onupdate until character reaches it; 
    		--after fire your custom event and catch it in EventHandler("yourevent", function() place code from ontimeout end)
    		inst.sg:SetTimeout(0.25)
    	end,
    
    	events =
    	{
    		EventHandler("animqueueover", function(inst)
    			--this happens only after inst.AnimState:PlayAnimation("parry_pst", false) will be complete
    			if inst.AnimState:AnimDone() then
    				inst.sg:GoToState("idle")
    			end
    		end),
    	},
    
    	ontimeout = function(inst)
    		inst.Physics:SetMotorVel(0, 0, 0)
    		inst.components.health:SetInvincible(false)
    		inst.components.locomotor:EnableGroundSpeedMultiplier(false)
    		inst.AnimState:PlayAnimation("parry_pst", false)
    	end,
    
    	onexit = function(inst)
    		--need to double this code — case when we will leave the state before timeout
    		inst.Physics:SetMotorVel(0, 0, 0)
    		inst.components.health:SetInvincible(false)
    		inst.components.locomotor:EnableGroundSpeedMultiplier(false)
    	end,
    })
    
    AddStategraphActionHandler("wilson", ActionHandler(ACTIONS.DODGE, "dodge_pre"))
    
    AddStategraphState("wilson_client",
    	State {
        name = "dodge_pre",
        tags = {"doing", "busy", "evade", "no_stun"},
    
        onenter = function(inst)
            inst.components.locomotor:Stop()
            inst.AnimState:PlayAnimation("parry_pre", false)
    		inst.AnimState:PushAnimation("parry_loop", true)
            inst:PerformPreviewBufferedAction()
            inst.sg:SetTimeout(2)
        end,
    	
    	onupdate = function(inst) 
    		if inst:HasTag("doing") then
    			if inst.entity:FlattenMovementPrediction() then
    				inst.sg:GoToState("idle", "noanim")
    			end
    		elseif inst.bufferedaction == nil then
    			inst.sg:GoToState("idle")
    		end
    	end,
    	
        ontimeout = function(inst)
    		inst:ClearBufferedAction()
    		inst.sg:GoToState("idle")
    	end,
    	}
    )
    
    AddStategraphActionHandler("wilson_client", ActionHandler(ACTIONS.DODGE, "dodge_pre"))
    
    --actions collectors, dodge will work with any equipped weapon. Not good but it's just an example
    --I'm not sure overriding onlocomote event handler is a good idea
    --check out the wortox prefab if you want use dodge like wortox's soul hop
    AddComponentAction("EQUIPPED", "weapon", function(inst, doer, target, actions, right)
    	if right then table.insert(actions, ACTIONS.DODGE) end
    end)
    
    AddComponentAction("POINT", "weapon", function(inst, doer, pos, actions, right)
    	if right then table.insert(actions, ACTIONS.DODGE) end
    end)

    don't forget to rename animation


  4. Your character moves after dodging because of inst.Physics:SetMotorVelOverride(20,0,0). Client draws fake position for character, but real doesn't change (actually it doesn't work at all). Any client state should contain animation and tag things only.

    AddStategraphState("wilson_client", State {
        name = "dodge",
        tags = {"busy", "evade", "no_stun"},
    
        onenter = function(inst)
            inst.components.locomotor:Stop()
            inst.AnimState:PlayAnimation("slide_pre", false) --only pre animation, the slide animation command must come from server
            inst:PerformPreviewBufferedAction() --this thing says to server "hey man, I'm doing something"
            inst.sg:SetTimeout(2)
        end,
        
        --[[I use this chunk, but I don't have idea what it really does. Sometime state doesn't work without "onupdate". It requires "doing" tag for server and client states
        onupdate = function(inst)
    		if inst:HasTag("doing") then
    			if inst.entity:FlattenMovementPrediction() then
    				inst.sg:GoToState("idle", "noanim")
    			end
    		elseif inst.bufferedaction == nil then
    			inst.sg:GoToState("idle")
    		end
    	end,
        ]]--
    
        --timeout happens only if player have a lag
        ontimeout = function(inst)
    		inst:ClearBufferedAction()
    		inst.sg:GoToState("idle")
    	end,
    )

    I think the second state is not requied for the client stategraph.


  5. There is "wilson_client" stategraph. The game uses it if lag compenstion is enabled on client (when you're playing as host without caves you are actually not a client and don't use this stategraph). It is almost the same but doesn't contain any game logic.

    The solution for the problem — you need to add lightweight versions of your states to "wilson_client".


  6. DS and DST are very different. It's not the best idea to reuse a code from the singleplayer. I think your code breaks the player stategraph and ALL characters will pick/mine quickly if your character is/was in a game session.

    1) You should use AddStategraphPostInit in modmain instead:
     

    AddStategraphPostInit("wilson", function(self)
        local _oldMineHandler = self.actionhandlers[GLOBAL.ACTIONS.MINE].deststate 
        self.actionhandlers[GLOBAL.ACTIONS.MINE].deststate = function(inst)
            if inst:HasTag("unique_tag_for_you_character") then
                return "doshortaction"
            end
    
            return _oldMineHandler(inst)
        end
    end)

    2) doshortaction is actually a state and this state doesn't have a mining sound. You need to create your own or play a sound in the deststate function (second is a really bad option);

    3) You also need to update wilson_client stategraph;

    4) poison "green screen" requires using of net variables.


  7. 1 hour ago, Lumina said:

    I'm encountering a problem : i have some "debris" prefab i would like to make repairable.

    The repairer component has a super-weird implementation. So...

    The first strange thing: actioncomponents.lua (this collects all valid actions at current frame):

    for k, v in pairs(MATERIALS) do
    	if target:HasTag("repairable_"..v) then
    		if (inst:HasTag("work_"..v) and target:HasTag("workrepairable"))
    			or (inst:HasTag("health_"..v) and target.replica.health ~= nil and not target.replica.health:IsFull())
    			or (inst:HasTag("freshen_"..v) and (target:HasTag("fresh") or target:HasTag("stale") or target:HasTag("spoiled"))) 
    		then
    			table.insert(actions, ACTIONS.REPAIR)
    		end
    		return
    	end
    end

    inst is an item you use as material and target is a broken thing you want to repair.

    Your items doesn't have a health component and they aren't perishable, so you need to adapt it for the first condition:

    1. You have the "workrepairable" tag from the workable component;
    2. Material must have the "work_stone" tag. This is a second strange thing — it doesn't.

    Look at the repairer component code:

    local function onrepairvalue(self)
        if self.repairmaterial ~= nil then
            onremovematerial(self, self.repairmaterial)
            if self.workrepairvalue > 0 then
                self.inst:AddTag("work_"..self.repairmaterial)
            end
            if self.healthrepairvalue > 0 or self.healthrepairpercent > 0 then
                self.inst:AddTag("health_"..self.repairmaterial)
            end
            if self.perishrepairpercent > 0 then
                self.inst:AddTag("freshen_"..self.repairmaterial)
            end
        end
    end

    It will add the "work_stone" tag only if repairer.workrepairvalue will be setted for a material-item prefab (stone materials have only healthrepeirvalue).

    So you have 3 ways to fix your issue:

    • Set the workrepairvalue for all materials you want to use (good, but maybe It will make some thing repairable with incorrect materials);
    • Push the tag to all materials (unsafe I think);
    • Add the health component and tune the health value (health without combat? not sure).

    May be there is a way to fix this weird component with debug-upvalue, but it's a bit complicated.


  8. You need something like this:

    --prefab file
    local MULT = 1.25
    
    local _newHealthDoDelta(self, amount, cause, ...)
    	if amont > 0 and cause and cause.components.healer then amount = amount * MULT end
    	return self.inst._oldHealthDoDelta(self, amount, cause, ...)
    end
    
    --prefab fn
    inst._oldHealthDoDelta = inst.components.health.DoDelta
    inst.components.health.DoDelta = _newHealthDoDelta

    Or you can add a custom tag to a character and add PostInit to the healer component.


  9. You have wrong condtions. Let the temperature be 65:

    if temp > 40 and temp <= 69  then --true
    	inst.components.health:StartRegen(1.5, 2)    
    end
    if temp > 30 and temp <= 39 then --false
    	inst.components.health:StartRegen(0.5, 3)    
    end
    if temp > 29 then --true
    	inst.components.health:StartRegen(0, 0)    
    end

    2 of 3 conditions are valid and the last regen overwrites the first. But I think you just place wrong symbol for the last one (temp < 29).

    And as @Wolf_EX said — it will be better to replace inst:DoPeriodicTask by inst:ListenForEvent("temperaturedelta", CheckTemperature) and move it to the main prefab function.


  10. AddStategraphPostInit("stategraph_name", some_function)

    When the stategraph will be initiated, the some_function will be called with stategraph itself as the first argument. It gives you an access to all state's variables:

    local function some_function(sg) --sg is the statgraph "stategraph_name"
    	local _onenter = sg.states["some_state"].onenter --saving old onenter function as a local var
    	sg.states["some_state"].onenter = function(inst) --replacing it with a new one
    		_onenter(inst) --call an original onenter if needed
    		inst.SoundEmitter:PlaySound("some_sound") --new action for the state
    	end
    end

    This code plays a sound when a creature comes to this state in addition to the original code.

    Or you can completely replace state with this:

    local function some_function(sg) --sg is the statgraph "stategraph_name"
          sg.states["some_state"] = State{ your state params }
          --or
          sg.states["some_state"] = state_var --if you've created a state before
    end

    Now look at your code:

    AddStategraphPostInit("SGbeequeenp", new_BeeQeen_Spawn)

    new_BeeQeen_Spawn is a state-type object (table generally). The game will try to call it as a function and... it will crash, because it's a table.

     

    AddStategraphState("stategraph_name", state_var)

    This adds a new state to the stategraph and the state_var is a state-type object. May be if you add a state with an existing name (state name, not a variable name), it will overwrite an original state. I'm not sure, but it should.


  11. Why do not you use AddPrefabPostInit? It is a bad practice to replace original game files.

    local addMoistTo = {"frog", "spider", "beefalo", etc}
    
    for _, prefab in pairs(addMoistTo) do
    	AddPrefabPostInit(prefab, function(inst) inst:AddComponent("moisture") end)
    end

    And there aren't any details in your crash log, you should check the server_log.txt


  12. require "stategraphs/commonstates"
    local CommonHandlers = GLOBAL.CommonHandlers

    It will work only it this order. You need to use require because GLOBAL.CommonHadlers doesn't exist when the modmain is called (I think it will be defined by the game when the first stategraph will be initiated).

    Anyway, it is not your main problem. You are trying to add a custom state, not to reinit existing:

    AddStategraphPostInit("SGbeequeenp", new_BeeQeen_Spawn)

    Must be used in another way. For example:
     

    local function SGWilsonPostInit(sg)
    	local _onenter = sg.states["run_start"].onenter
    	sg.states["run_start"].onenter = function(inst)
    		
    		_onenter(inst)
    		if not (inst.sg.statemem.riding or inst.sg.statemem.heavy)
    		  and SlowEnoughSandWalk(inst) then
    			inst.sg.statemem.sandstorm = true
    			inst.AnimState:PushAnimation("sand_walk_pre")
    		end
    	end
    
    	local _onenter2 = sg.states["run"].onenter
    	sg.states["run"].onenter = function(inst)
    		if not (inst.components.rider:IsRiding() or inst.components.inventory:IsHeavyLifting())
    		  and SlowEnoughSandWalk(inst) then
    			inst.components.locomotor:RunForward()
    			if not inst.AnimState:IsCurrentAnimation("sand_walk") then
    				inst.AnimState:PlayAnimation("sand_walk", true)
    			end
    			inst.sg:SetTimeout(inst.AnimState:GetCurrentAnimationLength())
    		else
    			_onenter2(inst)
    		end
    	end
    end
    
    AddStategraphPostInit("wilson", SGWilsonPostInit)

    If you want to add a custom state, use 

    AddStategraphState("stategraph_name", state_var)

     


  13. Hello. I don't think that modders can help me with this, but may be devs will read this thread.

    There is a problem and I do not see any good solutions — Woodie has too many tags.

    Error serializing tags for entity woodie[109330] - 32 tags; exceeds maximum size of 31

    My mod uses 2 replicable components (it's mean 2 additional tags) and when Woodie turns into a beaver, he reaches the tag-list limit. One more and the game will crush. I see only one solution — remove some original tags (polite and woodcutter for ex.).

    I can't release my echanced-magic framework and update the Green World (it also suffers) because of this.

    Maybe someone already dealt with this problem?

    Thanks for your time.


  14. On 11.07.2018 at 9:24 PM, shield angel said:

    i'm out of solutions and i'm getting desperate, anyone would know how to fix this?

    The default tile (not noise) texture for The Forge tiles isn't correct (well, it causes some С errors). Use an another tile (again, not noise) texture and it will work fine. You've asked about it in the Custom Tile Adder thread, but the forum didn't notify me. 


  15. Hey.

    Are there any interested modders, who want to create items (like The Forge weapons) or characters with active spells? I've moved and improved the spells and effects stuff from the Green World to the standalone mod-framework, which I plan to use later. Here you can find the preview version of this mod and some examples for it: 

    • Character spells for Wilson, Wigfrid, WX. WX spell requires electrical doodads;
    • Weapon with spells (some spells can be switched by rmb on an equipped item). Bee dart uses bees as an ammo;
    • Amulet with an ability to duplicate spells (lightning spear spells can be duplcated).
    • Example items are available on the Magic tab;

  16. I don't know about LoadPostPass, but you can use something like that:

    Disable saving:

    local function AddChild(self, child)
    	if self.children[child] ~= nil then
    		print("Already added child", child)
    		return
    	end
    
      	child.persists = false --disable saving for this entity, we will save/load it in another way
    	self.children[child] = child
    	self.numchildren = GetTableSize(self.children)
    end

    Save/Load

    function BeeSummoner:OnSave()
    	local children = {}
    
    	for k, v in pairs(self.children) do	
    		table.insert(children, v:GetSaveRecord())	
    	end
    
    	return {children = children}
    end
    
    function BeeSummoner:OnLoad(data)
    	if data and data.children then
        	for k, v in pairs(data.children) do
    			local obj = SpawnSaveRecord(v)
          		--some code  attach the spawned bee to the component owner
    		end
        end
    end

    I use the similar code to save/load the prefab info (Ancient Watcher in the Green World), not sure about components.


  17. if not GLOBAL.TheWorld.ismastersim then return end
    local _oldOnActivate = inst.components.teleporter.onActivate
    
    inst.components.teleporter.onActivate = function(inst, doer)
    	if doer.prefab == "esctemplate" and doer.components.sanity and not doer.components.sanity.ignore then
    		doer.components.sanity.ignore = true
    		_oldOnActivate(inst, doer)
    		doer.components.sanity.ignore = false
    	else
    		_oldOnActivate(inst, doer)
    	end
    end

     


  18. 1 hour ago, Silisiban said:

     I'm assuming "DoTaskInTime" is a LUA function or specific to DST API.  

    DoTaskInTime is the entity function:

    object:DoTaskInTime(delay, fn(object, somedata), somedata) --delay in seconds; somedata isn't necessary

    So you need something like this:

    v:PushEvent("respawnfromghost")
    v:DoTaskInTime(1, function(player)
    	player.components.health:SetPercent(1)
    	player.components.hunger:SetPercent(1)
    	player.components.sanity:SetPercent(1)
    end)

     

    55 minutes ago, Silisiban said:

    Also, in your code I'm seeing that your setting the prev var to ignoring the players sanity then setting the value but then also setting it back to ignore.  Wouldn't this then not allow the player to have sanity drain?  

    Just in case (devs had a reason to do it), the flag will be setted to the correct value by the game later.


  19. Inventory is empty (doesn't check equip slots and backpack), when 

    inst.components.inventory:NumItems() == 0

    If you need to check equip slots and backpack:

    #(inst.components.inventory:ReferenceAllItems()) == 0

    If you want to check inventory for an item with specified prefab:

    inst.components.inventory:Has("prefab", count)

    For a specified item (inst is the item, owner is the inventory owner):

    inst.components.inventoryitem:IsHeldBy(owner)

     


  20. Player death and revive events are pretty complicated things in the game code. Maybe devs will do somethnig with it, but now it is a hardcore part of modding. Anyway:
    1) Objects collect events at the next frame the event was pushed. So, when you do: PushEvent("respawnfromghost"), the player is still dead and don't know about the resurrection event. But the player has health, hunger and sanity components (it's wierd, but "after-resurrect" values are setted when the player dies). For some reason the sanity component for the ghost form has the "ignore" flag, so it ignores any attempts to change the sanity value. Thats why "v.components.sanity:SetPercent(1)" doesn't work. So, I think there are two ways:

    1. Do it with a small delay with DoTaskInTime, and it should be longer then one frame (because "respawnfromghost" pushes another events)
    2. Do something like this:
      local prev = v.components.sanity.ignore
      v.components.sanity.ignore = false
      v.components.sanity:SetPercent(1)
      v.components.sanity.ignore = prev

       

    2) SuUsed is a some kind of a log(?), I don't think it can be useful.