Jump to content

Trouble with Modified Action String Behaviour


Recommended Posts

So I modified spellcaster to support a "specific target" function that I need for my staff, which works fine.

But when I went to try and change the string, which I thought should be here;

GLOBAL.ACTIONS.CASTSPELL.strfn = function(act)
    return act.invobject ~= nil
        and act.invobject.components.spellcaster ~= nil
        and (act.invobject.components.spellcaster.specifictargetfn == nil or
             act.invobject.components.spellcaster.specifictargetfn(act.target))
        and act.invobject.components.spellcaster.actiontype
        or nil
end

but apparently, even if i change the whole block of code to "return nil" it still shows "Cast Spell"

So where am I suppose to put this change? I tried looking into how the telestaff does it, or in the spellcaster component, but I didn't notice anything of help.

Thanks.

Link to comment
Share on other sites

5 minutes ago, Muche said:

Have you added strings for actiontypes into GLOBAL.STRINGS.ACTIONS.CASTSPELL table?

no, but I'm trying to hide the option to "cast" if the target prefab doesnt meet the requirements.

Am I messing with the wrong code?

Link to comment
Share on other sites

IIRC, stringutil.lua|getmodifiedstring() returns generic string (in this case STRINGS.ACTIONS.CASTSPELL.GENERIC="Cast Spell") if it can't find one provided by strfn function in the appropriate table.

Maybe you can try to overwrite/inject before spellcaster component action so it doesn't insert ACTIONS.CASTSPELL into actions table if it doesn't meet the requirements?

Edited by Muche
Link to comment
Share on other sites

29 minutes ago, Muche said:

IIRC, stringutil.lua|getmodifiedstring() returns generic string (in this case STRINGS.ACTIONS.CASTSPELL.GENERIC="Cast Spell") if it can't find one provided by strfn function in the appropriate table.

Maybe you can try to overwrite/inject before spellcaster component action so it doesn't insert ACTIONS.CASTSPELL into actions table if it doesn't meet the requirements?

I don't really understand what you suggested I do, but it came to mind now to search for the tag that telestaff uses and it did lead me to "componentactions.lua"

but i'm not sure how i can modify it without editing base files:

spellcaster = function(inst, doer, target, actions, right)
            if right then
                if inst:HasTag("castontargets") then
                    table.insert(actions, ACTIONS.CASTSPELL)
                else
                    local castonrecipes = inst:HasTag("castonrecipes")
                    local castonlocomotors = inst:HasTag("castonlocomotors")
                    if (castonrecipes or castonlocomotors) and
                        (not castonrecipes or AllRecipes[target.prefab] ~= nil) and
                        (not castonlocomotors or target:HasTag("locomotor")) then
                        table.insert(actions, ACTIONS.CASTSPELL)
                    end
                end
            end
        end,

for now i'm gonna try messing with the spellcaster code to remove the tag if the player cant cast it.

Do you happen to know how to add more of these:

"spell = oncancast"

in a ComponentPostInit?

I dont think I can actually do that since the targetcheckfn is dynamic

Edited by Aquaterion
Link to comment
Share on other sites

There doesn't seem to be an easy way to change a base component action. Adding a custom one with higher priority could solve that:

local actCastSpell2 = AddAction("CASTSPELL2", "My Cast Spell", function(act)
	-- copied from actions.lua|CASTSPELL, do something custom?
	local staff = act.invobject or act.doer.components.inventory:GetEquippedItem(GLOBAL.EQUIPSLOTS.HANDS)

	if staff and staff.components.spellcaster and staff.components.spellcaster:CanCast(act.doer, act.target, act.pos) then
		staff.components.spellcaster:CastSpell(act.target, act.pos)
		return true
	end
end)
actCastSpell2.priority = 0 -- higher than GLOBAL.ACTIONS.CASTSPELL
actCastSpell2.distance = 20
actCastSpell2.mount_valid = true

AddComponentAction("EQUIPPED", "spellcaster", function(inst, doer, target, actions, right)
	if right then
		if inst.components.spellcaster ~= nil 
--			component spellcaster is not available on clients, need to rewrite it using tags:
--			and (act.invobject.components.spellcaster.specifictargetfn == nil or
--				act.invobject.components.spellcaster.specifictargetfn(act.target)
--			)
		then
			table.insert(actions, actCastSpell2)
		end
	end
end)

-- handlers copied from SGwilson & SGwilson_client
AddStategraphActionHandler("wilson", GLOBAL.ActionHandler(actCastSpell2,
	function(inst, action)
		return action.invobject.components.spellcaster.castingstate or "castspell"
end))
AddStategraphActionHandler("wilson_client", GLOBAL.ActionHandler(actCastSpell2, "castspell"))

The component action check needs some work to be done, as components are not available to clients, which the component actions run on.
Also, the tags castontargets, castonrecipes and castonlocomotors have to be not present when the custom action is not added too, so it doesn't show up again. They can be present if the custom action is added, since that will take precedence due to higher priority.

 

Link to comment
Share on other sites

1 hour ago, Muche said:

-snip-

I'm not sure how i can add tags to a dynamic check?

 

Btw, the specificfn thing happens to also be saved on the staff, does that help?

Well I just tested it in multiplayer and it didn't work D:

and inst.staffdata.specificfn(target)

gahhhh why does clientside have to be so hardd

Edited by Aquaterion
Link to comment
Share on other sites

26 minutes ago, Aquaterion said:

I'm not sure how i can add tags to a dynamic check?

You need to have those checks in the server, and it updates tags accordingly.

What I'm gathering here is that you want to have a item with spellcaster component say the "Cast Spell" only on certain targets?

Then give the prefabs you want to be targets a inst:AddTag("aqua_spellcastable") with AddPrefabPostInit and then check for that tag on the AddComponentAction.

If your specifictargetfn does a complex check, and you want the client to do it, then you will need to send many tags, or make a replica with many netvars, so the client can make the check themselves.

 

By the way

GLOBAL.ACTIONS.CASTSPELL.strfn = function(act)
    return act.invobject ~= nil
        and act.invobject.components.spellcaster ~= nil
        and (act.invobject.components.spellcaster.specifictargetfn == nil or
             act.invobject.components.spellcaster.specifictargetfn(act.target))
        and act.invobject.components.spellcaster.actiontype
        or nil
end

always returns nil for clients, and even the current code for it returns nil.

Which is why the staff_tornado has "Use Gadget" for hosts, and "Cast Spell" for clients.

If it returned an empty string, you would have a little mouse icon followed by nothing.

Link to comment
Share on other sites

What does that dynamic check do? Maybe it can be reimplemented on the client side if it uses only prefab names? Or tags could be emloyed?

Then there is the less option of always displaying the action availability prompt, only for the action to fizzle when used because the server (which has all information available) will deem the requirements not fulfilled.

Alternatively, there is always the option of creating own replicable component that will supply required information through attached classified.

Link to comment
Share on other sites

2 minutes ago, Muche said:

What does that dynamic check do? Maybe it can be reimplemented on the client side if it uses only prefab names? Or tags could be emloyed?

Then there is the less option of always displaying the action availability prompt, only for the action to fizzle when used because the server (which has all information available) will deem the requirements not fulfilled.

Alternatively, there is always the option of creating own replicable component that will supply required information through attached classified.

I tried doing tags like "targetonly_"..target(while also simplifing the specific feature to just prefabs) but everything stopped working and at that moment I remembered I had the inst.staffdata and I wanted to try that instead.

I'll try using it again.

10 minutes ago, DarkXero said:

Then give the prefabs you want to be targets a inst:AddTag("aqua_spellcastable") with AddPrefabPostInit and then check for that tag on the AddComponentAction.

 

If it doesn't work I'll try this method.

Link to comment
Share on other sites

Anddd for some reason this doesn't work for clients yet again.. Shockerr:

inst:HasTag("targetonly_"..target.prefab)

The staff definitely has that tag "targetonly_campfire" yet when I hover over a campfire nothing shows up.. I'm about to give up on this clientside bullsh** and just make the caster say "Invalid Target" or something..

 

As I was about to try DarkXero's method, and thinking how to implement it into Muche's component action, I saw

if inst.components.spellcaster ~= nil 

and at that moment I remembed you said COMPONENTS DONT WORK THERE, so finallyyyyy I removed that and its working for specific prefabs(I woulda noticed sooner but you added that line yourself), however this makes it uncompatible with mod items.

 

does the target have components for the client in the component action?

Edited by Aquaterion
Link to comment
Share on other sites

8 minutes ago, DarkXero said:

No, just replicas and tags.

would it be healthy for the game if I added every component name as a tag for EVERY prefab?

 

idk im tired I cant think anymore

Edited by Aquaterion
Link to comment
Share on other sites

4 minutes ago, DarkXero said:

But why? What do you want to check?

if the target has that component? but I think i'm just gonna give up on the clientside part.. I'd rather keep full serverside functionality.

If for example you want to make a staff that idk takes sanity from the target, I wanted the "Cast Spell" to only show up on targets with sanity

Edited by Aquaterion
Link to comment
Share on other sites

3 minutes ago, Aquaterion said:

would it be healthy for the game if I added every component name as a tag for EVERY prefab?

 

idk im tired I cant think anymore

There is a hard limit of 31 tags for an entity, so that could cause problems.

How many differentiable targets are there? I mean, does the target need all, let's say 5, different tags for each component the target has, or is just one tag enough (indicating it's a valid target for the item, i.e. this entity has at least one of 5 components)?

Link to comment
Share on other sites

2 minutes ago, Aquaterion said:

if the target has that component? but I think i'm just gonna give up on the clientside part.. I'd rather keep full serverside functionality.

Well, I don't know why would you want to know every single component a prefab has.

But yes, you can make a AddPrefabPostInitAny that parses all components and attaches a tag if it has it.

2 minutes ago, Muche said:

There is a hard limit of 31 tags for an entity, so that could cause problems.

Instead of using tags, he could parse through all components, get their names, concatenate them into a single string, and then give the prefab a net_string holding it. Then the client only needs to read the string.

Link to comment
Share on other sites

4 minutes ago, DarkXero said:

Well, I don't know why would you want to know every single component a prefab has.

But yes, you can make a AddPrefabPostInitAny that parses all components and attaches a tag if it has it.

Instead of using tags, he could parse through all components, get their names, concatenate them into a single string, and then give the prefab a net_string holding it. Then the client only needs to read the string.

Why is it everything I try to mod in this game end up being super complicated.. darn you clientside.. maybe I should just go to DS.. Anyways. I'm sticking with server-side only.

netvars scare me so that made me give up even more lol

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