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.

suteneko

[SOLVED] How to give a character Life Steal similar to Wigfrid?

Recommended Posts

suteneko    8

I wanted to give my character a life steal ability, similar to Wigfrid's ability to gain HP and Sanity.

Is it also possible to transform during battle? After consecutively attacking mobs the character will gain stacks (like filling a gauge) and then transform, gaining a damage boost and speed boost but lose sanity.

Share this post


Link to post
Share on other sites
thomas4846    675

you shouldn't try to do hard code rn you should start simple then build up. a lot of beginners will get help and not know what to do after

Share this post


Link to post
Share on other sites
Ultroman    619

UPDATE: I've fixed the code in this first post, so you don't need to go through the rest of the thread to get it working.

Life-steal is easy. I would advise you to read combat.lua to get familiar with the combat component. Specifically the functions DoAttack and GetAttacked. They push events on your character and the target depending on what happens.

You have to be thorough when reading through those, because not all the events relay all the information you might want. E.g. you can simply listen for the event "onattackother" which is pushed in the DoAttack function and add a static amount of health to the player, but that event does not relay how much damage was done to the target, so you cannot base the amount of health stolen on the damage dealt. If you want to do that, then you have to look at the GetAttacked function. If an "attacker" is present, it will push the event "onhitother" on the attacker, and this event DOES include the damage dealt. So, to do life-steal based on the damage dealt, you have to listen for the event "onhitother" on your character, and use the data it relays in its "data" object to calculate your life-steal.

inst:ListenForEvent("onhitother", function(inst, data)
	-- The data object holds the following variables for you to use:
	-- target, damage, damageresolved, stimuli, weapon, redirected
	-- damageresolved is the actual damage dealt, after going through armor, damage redirection, reflect etc.
	-- redirected is the entity that some damage was redirected to, if any.
	-- I can't remember what stimuli is.
	-- So you can do this to apply 25% life-steal of the damage dealt
	if not inst.components.health:IsDead() then
		inst.components.health:DoDelta(data.damageresolved * 0.25)
	end
end)

This will also solve the counting of hits you were talking about. You can use this same event to count up hits your character makes, and then store the time of the last time they did damage, as well, so the gauge runs out after a while.

I ended up basically writing the whole thing. Plop that in at the bottom of your character prefab Lua's master_postinit function.

NOTE: You need to change the build names (for your character's different animation builds) and change "myuniquemodifierkey" to, well, your own unique modifier key name. Make sure they're the same when adding and removing the modifiers, but that you have different modifier keys for the speed and damage multipliers, e.g., "myuniquespeedmodifierkey" and "myuniquedamagemodifierkey".

-- Initialize variables we want to use.
inst.lastdamagetime = 0
inst.damaginghits = 0
inst.istransformed = false

-- Function which changes the player back to normal.
local transformBackToNormal = function(inst)
	-- Set our boolean which we use to check whether the player is currently transformed.
	inst.istransformed = false
	-- Change back to the normal animation build.
	inst.AnimState:SetBuild("myNormalAnimBuildName")
	-- Remove damage bonus.
	inst.components.combat.externaldamagemultipliers:RemoveModifier(inst, "myuniquemodifierkey")
	-- Remove speed bonus.
	inst.components.locomotor:RemoveExternalSpeedMultiplier(inst, "myuniquemodifierkey")
end

-- Function which changes the player into something else.
local transformIntoWhatever = function(inst)
	-- Set our boolean which we use to check whether the player is currently transformed.
	inst.istransformed = true
	-- Change to the alternative animation build.
	inst.AnimState:SetBuild("myOtherAnimBuildName")
	-- Add 10% damage bonus.
	inst.components.combat.externaldamagemultipliers:SetModifier(inst, 1.10, "myuniquemodifierkey")
	-- Add 10% speed bonus.
	inst.components.locomotor:SetExternalSpeedMultiplier(inst, "myuniquemodifierkey", 1.10)
end

-- Start a task which checks whether it has been a long time since the last damaging attack.
inst.damagedealtchecktask = inst:DoPeriodicTask(0.5, function(inst)
	-- When it has been 10.0 seconds since the last damaging attack, we want to do some things...
	if GetTime() - inst.lastdamagetime > 10.0 then
		-- 10.0 seconds after the last damaging attack, we want the player to transform back to normal.
		if inst.istransformed then
			transformBackToNormal(inst)
		end
		-- Also reset damaging hits, if it has been more than 10.0 seconds since the last one.
		if inst.damaginghits > 0 then
			inst.damaginghits = 0
		end
	else
		-- It has not been 10.0 seconds since the last damaging attack, so if the player is
		-- currently transformed, make them lose sanity.
		if not inst.components.health:IsDead() and inst.istransformed then
			-- Lose 1 sanity every check (the check runs every 0.5 seconds).
			inst.components.sanity:DoDelta(-1)
		end
	end
end)

inst:ListenForEvent("onhitother", function(inst, data)
	-- The data object holds the following variables for you to use:
	-- target, damage, damageresolved, stimuli, weapon, redirected
	-- damageresolved is the actual damage dealt, after going through armor, damage redirection, reflect etc.
	-- redirected is the entity that some damage was redirected to, if any.
	-- I can't remember what stimuli is.
	
	-- If the player is not dead and damage was dealt...
	if not inst.components.health:IsDead() and data.damageresolved > 0 then
		-- Do life-steal
		inst.components.health:DoDelta(data.damageresolved * 0.25)
		
		-- Count up damaging hits.
		inst.damaginghits = inst.damaginghits ~= nil and inst.damaginghits + 1 or 1
		
		-- Register the last time a damaging hit was dealt.
		inst.lastdamagetime = GetTime()
		
		-- If there has been enough damaging hits, transform the player.
		if inst.damaginghits >= 10 then
			transformIntoWhatever(inst)
		end
	end
end)

 

Share this post


Link to post
Share on other sites
Ultroman    619
1 minute ago, thomas4845 said:

Senpie your spoiling them 

Yeah, I know xD It wasn't the plan to begin with to write up the whole thing. I tried doing it in pieces, but then I saw that it would probably be easier to follow, if I just wrote the whole thing and commented it. I like doing informative posts, in the hopes that others will find them in their searches.

Share this post


Link to post
Share on other sites
Ultroman    619

It might complain that it cannot find the functions transformBackToNormal and transformIntoWhatever. If it does, you just need to move those up above the master_postinit function.

Share this post


Link to post
Share on other sites
suteneko    8

@Ultroman I can't thank you enough ;u; I'll test it after I get some sleep and see how it goes. Seriously, extremely grateful!

@thomas4845 I do feel rather spoiled and I understand that I may be a tad (or more) ambitious but I really do appreciate all the help vuv

I'm an artist and not a coder so this is all pretty new to me. I can kind of get the gist of the contents of the codes and what they're doing when I look through the coding but the commentary with the code definitely helps me grasp a better understanding of what's going on.

Share this post


Link to post
Share on other sites
suteneko    8

@Ultroman Okay, so I sat down to test the code and it didn't like it. I'm assuming it's the GLOBAL statement. I can't pull it from master_postinit since it's part of a function, though.

Spoiler

[00:00:34]: [string "../mods/Rai'sa the Flame Puppy/scripts/pref..."]:172: variable 'GLOBAL' is not declared
LUA ERROR stack traceback:
=[C]:-1 in (global) error (C) <-1--1>
scripts/strict.lua:23 in () ? (Lua) <21-26>
   t = table: 1603EAC0
   n = GLOBAL
../mods/Rai'sa the Flame Puppy/scripts/prefabs/raisa.lua:172 in (field) fn (Lua) <170-189>
   inst = 110798 - raisa (valid:true)
scripts/scheduler.lua:177 in (method) OnTick (Lua) <155-207>
   self =
      running = table: 15CC0B88
      waitingfortick = table: 15CC0C50
      tasks = table: 15CC0BD8
      waking = table: 7E4EB638
      attime = table: 15CC0ED0
      hibernating = table: 15CC0C78
   tick = 15
   k = PERIODIC 110798: 0.500000
   v = true
   already_dead = nil
scripts/scheduler.lua:371 in (global) RunScheduler (Lua) <369-377>
   tick = 15
scripts/update.lua:180 in () ? (Lua) <159-238>
   dt = 0.033333335071802
   tick = 15
   i = 15

[00:00:34]: [string "../mods/Rai'sa the Flame Puppy/scripts/pref..."]:172: variable 'GLOBAL' is not declared
LUA ERROR stack traceback:
    =[C]:-1 in (global) error (C) <-1--1>
    scripts/strict.lua:23 in () ? (Lua) <21-26>
    ../mods/Rai'sa the Flame Puppy/scripts/prefabs/raisa.lua:172 in (field) fn (Lua) <170-189>
    scripts/scheduler.lua:177 in (method) OnTick (Lua) <155-207>
    scripts/scheduler.lua:371 in (global) RunScheduler (Lua) <369-377>
    scripts/update.lua:180 in () ? (Lua) <159-238>

Eez7p.png

Share this post


Link to post
Share on other sites
suteneko    8
3 hours ago, Ultroman said:

Just remove the GLOBAL. before GetTime() then. I have corrected the code-snippet above.

I ended up getting another error after taking GLOBAL. out.

Spoiler

[00:01:09]: [string "../mods/Rai'sa the Flame Puppy/scripts/pref..."]:175: attempt to compare number with nil
LUA ERROR stack traceback:
../mods/Rai'sa the Flame Puppy/scripts/prefabs/raisa.lua:175 in (field) fn (Lua) <167-186>
   inst = 121911 - raisa (valid:true)
scripts/scheduler.lua:177 in (method) OnTick (Lua) <155-207>
   self =
      running = table: 35A0D238
      waitingfortick = table: 35A0D288
      tasks = table: 35A0D378
      waking = table: 12477398
      attime = table: 35A0D3A0
      hibernating = table: 35A0D260
   tick = 355
   k = PERIODIC 121911: 0.500000
   v = true
   already_dead = nil
scripts/scheduler.lua:371 in (global) RunScheduler (Lua) <369-377>
   tick = 355
scripts/update.lua:180 in () ? (Lua) <159-238>
   dt = 0.033333335071802
   tick = 355
   i = 355

[00:01:09]: [string "../mods/Rai'sa the Flame Puppy/scripts/pref..."]:175: attempt to compare number with nil
LUA ERROR stack traceback:
    ../mods/Rai'sa the Flame Puppy/scripts/prefabs/raisa.lua:175 in (field) fn (Lua) <167-186>
    scripts/scheduler.lua:177 in (method) OnTick (Lua) <155-207>
    scripts/scheduler.lua:371 in (global) RunScheduler (Lua) <369-377>
    scripts/update.lua:180 in () ? (Lua) <159-238>

I've attached my character prefab for reference (it's messy with uncomment lines, sorry--)

raisa.lua

Share this post


Link to post
Share on other sites
suteneko    8

@Ultroman It's golden!! Thank you so much for all your help ;u;

Character went invisible at first because I forgot to add the transformation anim to the assets list oops.

I haven't done the sprite artwork yet but I'll post a gif of the finished project here later to show you >u<

A million thanks!

Share this post


Link to post
Share on other sites