Jump to content

Cancelling attack animation on failed ammo check and creating an index of acceptable forms of ammo


Recommended Posts

These are mostly quality of life functions of my mod that I won't be too fussed if I can't get to work

My character has a weapon that can, at the moment, use berries as a source of fuel for a ranged attack, this part of the code works, but when the check fails to find any berries in the inventory it still plays the animation for the attack, even though it deals no damage and enemies don't react to it, is there a way I can make it cancel the animation when the attack fails?

Additionally, I want to add more items that can be accepted as ammo for the weapon, though I think the way the code works currently if I were to add more it would try to remove one of each item from the inventory, even if I only want it to remove one of a single type.

Here's the codes for the ammo check

In the modmain

Spoiler

AddComponentPostInit("combat", function(Combat, inst)
	local DoAttack_prev = Combat.DoAttack
	function Combat:DoAttack(target_override, weapon, ...)
		-- Check if weapon requires ammo
		if weapon and weapon.HasAmmo then
			if weapon:HasAmmo(inst) then
				-- Owner has ammo.
				return DoAttack_prev(self, target_override, weapon, ...)
			end
			-- Owner has no ammo. Cancel attack
			return
		end
		
		return DoAttack_prev(self, target_override, weapon, ...)
	end
end)

 

and in the weapon prefab

Spoiler

local function onattack_robewand(inst, owner, target)
	owner.components.inventory:ConsumeByName("berries", 1)
end

--The following section is placed in the 'local function fn()', below the 'if not TheWorld.ismastersim then'

	inst.HasAmmo = function(inst, owner)
		if owner and owner.components.inventory and owner.components.inventory:Has("berries", 1) then
			return true
		end
		return false
	end

 

 

Link to comment
Share on other sites

I don't 100% know if this code would work, but hopefully it does :)! I would put this code in your "check" thing :)

local inventory = inst.components.inventory
if inventory and inventory:Has("berries", 1) and inst.ignore_other_ammo == nil then
	inst.ignore_other_ammo = true
	inventory:ConsumeByName("berries", 1)
	inst:DoTaskInTime(.1, function() inst.ignore_other_ammo = nil end)
elseif inventory and inventory:Has("OTHER AMMO PREFAB", 1) and inst.ignore_other_ammo == nil then
	inst.ignore_other_ammo = true
	inventory:ConsumeByName("OTHER AMMO PREFAB", 1)
	inst:DoTaskInTime(.1, function() inst.ignore_other_ammo = nil end)
else
	inst.sg:GoToState("idle")
end

So, for every ammo you want to use you will have to put a elseif to check if you have that ammo in your inventory & "ignore_other_ammo" is nil.

How I think this code will work is it checks your inventory to see if you have a ammo item if you do, it will set value that all the other elseif for other ammos do not work for .1 sec & if your character doesn't have any of the ammos they will just idle, which will cancel anything you were doing.

Hopefully it works or gives you some ideas :D!!

Link to comment
Share on other sites

Thank you, your code worked! for the most part, I changed "local inventory = inst.components.inventory" to "local inventory = owner.components.inventory" since it didnt seem to do anything as an inst.

I also changed the "inst.sg:GoToState("idle")" to owner since it was causing crashes as is, though it doesn't seem to do anything now.

Also, I set one of the acceptable forms of ammo as berries_juicy, but even when i have them in the inventory it doesn't detect or use them.

Here's the code currently for reference

Spoiler

local function onattack_robewand(inst, owner, target)
	-- owner.components.inventory:ConsumeByName("berries", 1) --left this here in case I need to do a rollback
	local inventory = owner.components.inventory
	if inventory and inventory:Has("petals", 1) and inst.ignore_other_ammo == nil then
		inst.ignore_other_ammo = true
		inventory:ConsumeByName("petals", 1)
		inst:DoTaskInTime(.1, function() inst.ignore_other_ammo = nil end)
	elseif inventory and inventory:Has("berries", 1) and inst.ignore_other_ammo == nil then
		inst.ignore_other_ammo = true
		inventory:ConsumeByName("berries", 1)
		inst:DoTaskInTime(.1, function() inst.ignore_other_ammo = nil end)
	elseif inventory and inventory:Has("berries_juicy", 1) and inst.ignore_other_ammo == nil then
		inst.ignore_other_ammo = true
		inventory:ConsumeByName("berries_juicy", 1)
		inst:DoTaskInTime(.1, function() inst.ignore_other_ammo = nil end)
	else
		owner.sg:GoToState("idle")
	end
end

EDIT: Actually I set the GoToState back to inst and it doesn't crash, don't know what's going on there it was crashing earlier, it still doesn't stop the animation though.

EDIT2: No wait, I was right the first time, it crashes when it checks for the GoToState

Spoiler

[00:02:25]: [string "../mods/robe/scripts/prefabs/robewand.lua"]:30: attempt to index field 'sg' (a nil value)
LUA ERROR stack traceback:
    ../mods/robe/scripts/prefabs/robewand.lua:30 in (field) onattack (Lua) <14-32>
    scripts/components/weapon.lua:69 in (method) OnAttack (Lua) <67-75>
    scripts/components/combat.lua:837 in () ? (Lua) <781-855>
    =(tail call):-1 in ()  (tail) <-1--1>
    scripts/actions.lua:586 in (field) fn (Lua) <585-588>
    scripts/bufferedaction.lua:24 in (method) Do (Lua) <20-34>
    scripts/entityscript.lua:1269 in (method) PerformBufferedAction (Lua) <1261-1279>
    scripts/stategraphs/SGwilson.lua:4278 in (field) fn (Lua) <4275-4281>
    scripts/stategraph.lua:568 in (method) UpdateState (Lua) <536-580>
    scripts/stategraph.lua:607 in (method) Update (Lua) <599-627>
    scripts/stategraph.lua:125 in (method) Update (Lua) <109-148>
    scripts/update.lua:218 in () ? (Lua) <149-228>

 

Edited by Eevelion
Link to comment
Share on other sites

To be more clear, I wanted it to remove the animation for the projectile, having the character swing and nothing coming out when they do doesn't sound too strange, I tried messing around a little to see what works and this is what I got

Spoiler

local function onattack_robewand(inst, owner, target)
	-- owner.components.inventory:ConsumeByName("berries", 1)
	local inventory = owner.components.inventory
	if inventory and inventory:Has("petals", 1) and inst.ignore_other_ammo == nil then
		inst.ignore_other_ammo = true
		inventory:ConsumeByName("petals", 1)
		inst.components.weapon:SetProjectile("ice_projectile")
		inst:DoTaskInTime(.1, function() inst.ignore_other_ammo = nil end)
	elseif inventory and inventory:Has("berries", 1) and inst.ignore_other_ammo == nil then
		inst.ignore_other_ammo = true
		inventory:ConsumeByName("berries", 1)
		inst.components.weapon:SetProjectile("fire_projectile")
		inst:DoTaskInTime(.1, function() inst.ignore_other_ammo = nil end)
	elseif inventory and inventory:Has("berries_juicy", 1) and inst.ignore_other_ammo == nil then
		inst.ignore_other_ammo = true
		inventory:ConsumeByName("berries_juicy", 1)
		inst.components.weapon:SetProjectile("ice_projectile")
		inst:DoTaskInTime(.1, function() inst.ignore_other_ammo = nil end)
	else
		inst.components.weapon:SetProjectile(nil)
		owner.sg:GoToState("idle")
	end
end

 

it can switch from ice_projectile on the petal check to the fire_projectile on the berries check (it still fails to use the juicy berry check) but if it fails to check for ammo it still uses the projectile from whichever ammo it had last. Could the nil entry not be cleaning out the SetProjectile right? and if so, what can I do to fix it?

Link to comment
Share on other sites

Here's a code which hopefully it doesn't consume multiple ammos at once & I put a print thing in the juicy berries go in a world with no  caves & in console see if that "Hello" pops up, if it does then your juicy berries code does work if it doesn't I don't really know what's wrong since it seems fine to me.

Spoiler

local function onattack_robewand(inst, owner, target)
	local inventory = owner.components.inventory
	if inventory and inventory:Has("petals", 1) then
		inventory:ConsumeByName("petals", 1)
		inst.components.weapon:SetProjectile("ice_projectile")
		return
	elseif inventory and inventory:Has("berries", 1) then
		inventory:ConsumeByName("berries", 1)
		inst.components.weapon:SetProjectile("fire_projectile")
		return
	elseif inventory and inventory:Has("berries_juicy", 1) then
		print("Hello")
		inventory:ConsumeByName("berries_juicy", 1)
		inst.components.weapon:SetProjectile("ice_projectile")
		return
	else
		inst.components.weapon:SetProjectile(nil)
		owner.sg:GoToState("idle")
	end
end

 

Also, isn't it possible to do something like " projectile:Remove() " or something to delete the projectile when you don't want it?

Edited by SuperDavid
Link to comment
Share on other sites

So, I realized there were a ton of mistakes I made, including stuff like a component with an indent it didn't need, and that I never updated the part that the code in the modmain checks for ammo, that now looks like this

Spoiler

    inst.HasAmmo = function(inst, owner)
		if owner and owner.components.inventory and owner.components.inventory:Has("petals", 1) then
			print("HasAmmoPetalsTest")
			return true
		elseif owner and owner.components.inventory and owner.components.inventory:Has("berries", 1) then
			print("HasAmmoBerryTest")
			return true
		elseif owner and owner.components.inventory and owner.components.inventory:Has("berries_juicy", 1) then
			print("HasAmmoJuicyBerryTest")
			return true
		else
			print("HasAmmoFailedTest")
			return false
		end
	end

 

As you can see I've been using a lot of print functions to check when and if the calls are used correctly

which comes to my main problem at the moment, this is the part of my modmain that controls cancelling attacks when the weapon doesn't have any ammo

Spoiler

AddComponentPostInit("combat", function(Combat, inst)
	local DoAttack_prev = Combat.DoAttack
	function Combat:DoAttack(target_override, weapon, ...)
		-- Check if weapon requires ammo
		if weapon and weapon.HasAmmo then
			if weapon:HasAmmo(inst) then
				-- Owner has ammo.
				print("CombatCheckSuccess")
				return DoAttack_prev(self, target_override, weapon, ...)
			else
				print("CombatCheckFail")
				-- Owner has no ammo. Cancel attack
				return
			end
		end
		
		print("AmmoCheckNotNeeded")
		return DoAttack_prev(self, target_override, weapon, ...)
	end
end)

 

for some reason though, even if it does one of the CombatChecks it still runs the AmmoCheckNotNeeded, I'm guessing the way it's set up isn't structured right, but I have no clue how to get it right, I've tried nesting the AmmoCheckNotNeeded part in an else on the same level as the "if weapon and weapon:HasAmmo then" part but it still seems to run it

 

Also, I'm going to include the rar for my mod in case it helps at all

 

robe.rar

Edited by Eevelion
Link to comment
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
 Share

×
  • Create New...