Jump to content

Lifesteal aura


Near4422

Recommended Posts

Hello guys!

----------------- EDIT --------------------
I've created a separate subject that gathers all stuff around my very first character. So to anyone who'd like to contribute - Please head here -> 

local function onattack(inst, owner, target)
    owner.components.health:DoDelta(3,false,"scare_scythe")
end

I'd like to change it to aura that deals 1 point of damage to every monster unit (only monsters, not animals etc) every tick... (let's say every 2 secs) within certain distance from the character and heal character for the damage dealt.

Could someone help me with it? 
Thank you in advance!

Link to comment
Share on other sites

local function IsVaildTarget(target)
	return target ~= nil and target.components.health ~= nil
	--can't drain when target has no health
 		and not ((target:HasTag("prey") and not target:HasTag("hostile")) or
			target:HasTag("structure") or
			target:HasTag("wall") or
			target:HasTag("companion"))
end

local function onattack(inst, owner, target)
	target:AddTag("scythevictim")
	local count = 0
	local pt = Vector3(target.Transform:GetWorldPosition())
	local range = 5
	local ents = TheSim:FindEntities(pt.x, pt.y, pt.z, range)
	for i,ent in ipairs(ents) do
		if IsVaildTarget(ent) or ent and ent.components.health and ent:HasTag("scythevictim") then
			--because of scythevictim tag, you can even drain 'not vaild target(like wall, chester)' when it's directly attacked.
			if ent._scythedmgtask ~= nil then
				ent._scythedmgtask:Cancel()
				--it will reset its timer when it's re-attacked before timer done
			end
			ent._scythedmgtask = ent:DoTaskInTime(10, function(ent) ent._scythecount = nil ent._scythedmg = nil ent._scythedmgtask = nil end)
			--reset everything when 10 sec timer is done
			if ent._scythecount == nil then
				ent._scythedmg = ent:DoPeriodicTask(2, function(ent) ent.components.health:DoDelta(-1, false, owner) end)
				--every 2 seconds, it will deal 1 damage
				ent._scythecount = ent._scythecount + 1
			else
				--it can be applied multiple times!
				ent._scythedmg = ent:DoPeriodicTask(2, function(ent) ent.components.health:DoDelta(-ent._scythecount, false, owner) end)
				--every 2 seconds, it will deal 'ent._scythecount' damage
				ent._scythecount = ent._scythecount + 1
			end
			count = count + ent._scythecount
			--since ent._scythecount = damage dealt, you will heal total amount of damage dealt
		end
	end
	if owner._draintask ~= nil then
		owner._draintask:Cancel()
		owner._drain:Cancel()
	end
	--it will reset its timer when it is attacked before time done
	owner._draintask = owner:DoTaskInTime(10, function(owner) owner._drain = nil owner._draintask = nil end)
	owner._drain = owner:DoPeriodicTask(2, function(owner) owner.components.health:DoDelta(count) end)
end

Phew, that was lot of codes!

I'm not good at modding but at least this is what I can do.

If you encounter an error, please let me know by uploading game logs so I can fix it.

Link to comment
Share on other sites

@Combustiblemon Thank you for your work so much!
That's a looot of code... i wouldn't be able to figure it out on my own.
Tho i must inform you it doesn't work as intended... or i failed to implement it propperly.

Thing is when i attack anything all characters freeze for a brief moment in place and then the server disconnects me.
I've searched through the server files and hopefully attatched the propper file with logs - hopefully this will help you understand what happened (because i have no idea at all....) 

And i wanted to ask a few questions to better understand your code.
First of all - thank you for comments!
Second - this code triggers on attack right? Since this function was originally placed inside the 'scythe' item. My intention was to create an aura that works all the time (or at least when the scythe is equipped) without the need to hit an enemy (but if that's the only way to get targets for the function - it'll be still fine).
Tag 'prey' is being applied to characters i've attacked - do I get it right?

server_log.txt

Link to comment
Share on other sites

55 minutes ago, Combustiblemon said:

local function IsVaildTarget(target)
	return target ~= nil and target.components.health ~= nil
	--can't drain when target has no health
 		and not ((target:HasTag("prey") and not target:HasTag("hostile")) or
			target:HasTag("structure") or
			target:HasTag("wall") or
			target:HasTag("companion"))
end

local function onattack(inst, owner, target)
	target:AddTag("scythevictim")
	local count = 0
	local pt = Vector3(target.Transform:GetWorldPosition())
	local range = 5
	local ents = TheSim:FindEntities(pt.x, pt.y, pt.z, range)
	for i,ent in ipairs(ents) do
		if IsVaildTarget(ent) or ent and ent.components.health and ent:HasTag("scythevictim") then
			--because of scythevictim tag, you can even drain 'not vaild target(like wall, chester)' when it's directly attacked.
			if ent._scythedmgtask ~= nil then
				ent._scythedmgtask:Cancel()
				--it will reset its timer when it's re-attacked before timer done
			end
			ent._scythedmgtask = ent:DoTaskInTime(10, function(ent) ent._scythecount = nil ent._scythedmg = nil ent._scythedmgtask = nil end)
			--reset everything when 10 sec timer is done
			if ent._scythecount == nil then
				ent._scythedmg = ent:DoPeriodicTask(2, function(ent) ent.components.health:DoDelta(-1, false, owner) end)
				--every 2 seconds, it will deal 1 damage
				ent._scythecount = ent._scythecount + 1
			else
				--it can be applied multiple times!
				ent._scythedmg = ent:DoPeriodicTask(2, function(ent) ent.components.health:DoDelta(-ent._scythecount, false, owner) end)
				--every 2 seconds, it will deal 'ent._scythecount' damage
				ent._scythecount = ent._scythecount + 1
			end
			count = count + ent._scythecount
			--since ent._scythecount = damage dealt, you will heal total amount of damage dealt
		end
	end
	if owner._draintask ~= nil then
		owner._draintask:Cancel()
		owner._drain:Cancel()
	end
	--it will reset its timer when it is attacked before time done
	owner._draintask = owner:DoTaskInTime(10, function(owner) owner._drain = nil owner._draintask = nil end)
	owner._drain = owner:DoPeriodicTask(2, function(owner) owner.components.health:DoDelta(count) end)
end

Phew, that was lot of codes!

I'm not good at modding but at least this is what I can do.

If you encounter an error, please let me know by uploading game logs so I can fix it.

#Ping. 
I don't know how to tag people here yet propperly. Hopefully it works. 
I've added a response above :)

Link to comment
Share on other sites

local function IsVaildTarget(target)
	return target ~= nil and target.components.health ~= nil
	--can't drain when target has no health
 		and not ((target:HasTag("prey") and not target:HasTag("hostile")) or
			target:HasTag("structure") or
			target:HasTag("wall") or
			target:HasTag("companion"))
end

local function DamageAura(inst)
	local owner = inst.components.inventoryitem.owner or nil
	if not owner then return end
  
	local count = 0
	local x,y,z = owner.Transform:GetWorldPosition()
	local pt = owner:GetPosition()
	local range = 5
	local ents = TheSim:FindEntities(pt.x, pt.y, pt.z, range)
	for i,ent in ipairs(ents) do
		if IsVaildTarget(ent) or ent and ent.components.health and ent:HasTag("scythevictim") then
			--because of scythevictim tag, you can even drain 'not vaild target(like wall, chester)' when it's directly attacked.
			if ent._scythedmgtask ~= nil then
				ent._scythedmgtask:Cancel()
				ent._scythedmg:Cancel()
				--it will reset its timer when it's re-attacked before timer done
			end
			ent._scythedmgtask = ent:DoTaskInTime(10, function(ent) ent._scythecount = nil ent._scythedmg = nil ent._scythedmgtask = nil end)
			--reset everything when 10 sec timer is done
			if ent._scythecount == nil then
				ent._scythedmg = ent:DoPeriodicTask(2, function(ent) ent.components.health:DoDelta(-1, false, owner) end)
				--every 2 seconds, it will deal 1 damage
				ent._scythecount = 1
			else
				--it can be applied multiple times!
				ent._scythedmg = ent:DoPeriodicTask(2, function(ent) ent.components.health:DoDelta(-ent._scythecount, false, owner) end)
				--every 2 seconds, it will deal 'ent._scythecount' damage
				ent._scythecount = ent._scythecount + 1
			end
			count = count + ent._scythecount
			--since ent._scythecount = damage dealt, you will heal total amount of damage dealt
		end
	end
	owner.components.health:DoDelta(count)
end

local function onattack(inst, owner, target)
	if target._auratargettask ~= nil then
		target._auratargettask:Cancel()
	end
	if not target:HasTag("scythevictim") then
		target:AddTag("scythevictim")
    end
    target._auratargettask = target:DoTaskInTime(10, function(target) target:RemoveTag("scythevictim") end)
end

----------------------------------------------------------

inst._auratask = inst:DoPeriodicTask(2, DamageAura)
--add this line into weapon main function

 

The error was 'ent._scythecount = ent._scythecount + 1', because it was nil(none value).

I edited so damage aura will work even you didn't attack at all, but attacking specific target(like chester) will add tag "scythevictim" to target, so target can be damaged by aura.

There are no visual effects besides of health effect, so I'd recommend you add some animation effects.

 

You can ping someone by @, @Near4422 like this.

Sorry for being late, I didn't have times to do coding.

Aaaand, this is solo DS-modding category, not DST one. You've mistaken I guess?

There are more active and has more people to help you.

Link to comment
Share on other sites

Neat :)

Just wanted to note that you're leaving all your victims with the "scythevictim" tag on forever. Mostly for future readers who might find this post. It could give problems if one tried to use this code for their "aura" (it's more like AoE with life-steal over time), since if you target things based on a tag that never gets removed, you might end up targetting victims that should not have the tag anymore.

Also, putting public variables on the owner with as mundane names as _draintask and _drain may lead to compatibility problems with other mods that have used the same exact naming. When adding to a "public pool" of variables, always make sure your variables are named sufficiently unique for it to be statistically improbable that others have already used it. Perhaps a moniker for your mod as a prefix e.g. owner._AAA_draintask.

Link to comment
Share on other sites

@Combustiblemon Thank's for you effort! I really appreciate it. I am going to try this out soon :)
Answering your questions - yes i must have misteaken, I'm sorry for that.

@Ultroman That's a very significant notice. Altough I do understand what you mean - I don't feel like i know how to use this knowledge in sake for mods compability :/
Thank you for linking that megathread - I am going to have a lot of reading :)

 

I assume that i should write something that will be removing tag once the target is further away than specific distance from the character... 

Link to comment
Share on other sites

@Combustiblemon It works!
Tho something is clearly wrong with the numbers - I am trying to figure it out.
Don't know why yet, but character gets healed for weird amounts of health points every 2 seconds - even with single spider I get healed by 15, 18 or even 20 HP per tick. 
For the damage dealt - it seems to be stacking up due to this part of code:

else
				--it can be applied multiple times!
				ent._scythedmg = ent:DoPeriodicTask(2, function(ent) ent.components.health:DoDelta(-ent._scythecount, false, owner) end)
				--every 2 seconds, it will deal 'ent._scythecount' damage
				ent._scythecount = ent._scythecount + 1
			end

The longer the fight goes - the more damage is being dealt to a single enemy.
Also In a random moment an enemy stops being damaged by the aura and after (approximetly) 10 seconds it starts to work again.
The healing received also seems to be somehow stacking up to the infinity - i assume due to the constant incrementing the 'count' value.

I've managed to solve the issue by removing this part of the code: 

			else
				--it can be applied multiple times!
				ent._scythedmg = ent:DoPeriodicTask(2, function(ent) ent.components.health:DoDelta(-ent._scythecount, false, owner) end)
				--every 2 seconds, it will deal 'ent._scythecount' damage
				ent._scythecount = ent._scythecount + 1

But there are still issues to be covered. 
For example the character gets healing even after the prey is dead. 
Next up - loosing an agro on mob (walking away from it so it losses interrest) still maintains the tag on him and results in taking damage and receaving healing. 

We're getting close on it! At least the game stopped crashing :D

Link to comment
Share on other sites

-- Outside Fn
          local auraDuration = 0
	
-- Inside Fn
          inst:ListenForEvent("onattackother", function(inst, data)
              auraDuration = 6 -- Turn on our aura for 6 seconds (Hit again to reapply)
          end)

          local function ScytheTick()
              local x, y, z = inst.Transform:GetWorldPosition()
              local ents = TheSim:FindEntities(x, y, z, 4, nil) -- Play around with distance to suit your needs. I put it to "4" as an example.

              for i, v in ipairs(ents) do
                  if v.components.health and not v.components.health:IsDead() then -- Make sure our target isn't dead and has the health component
                      local damage = 1

                      v.components.combat:GetAttacked(inst, damage) -- 1 point of damage, coming from inst (Player)
                      inst.components.health:DoDelta(damage) -- Heal us with it, as well.
                  end
              end
          end

          inst:DoPeriodicTask(2, function() 
              if auraDuration > 0 then 
                  ScytheTick() 
      			auraDuration = auraDuration - 2
              end
          end, nil) -- Every 2 seconds, run ScytheTick function

This is how I'd handle it, assuming you put it in your character prefab lua. Untested, but I'm pretty positive it'll work. Change variables as you need :) @Near4422

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...