RedHairedHero Posted July 19, 2023 Share Posted July 19, 2023 (edited) Hello, I’ve hit wall after wall attempting to do this and could use some help. I’m attempting to convert Woodie’s idols into reusable skills with a cooldown indicator. The three closest items I can think of would be Wigfrids Battle Songs, Wanda’s Pocket Watch, or Wickerbottoms books. I’d prefer not to allow Woodie to use their exclusive items though which is another hurdle to overcome. Of the three I would like Wigfrids Battle Songs, but I’m clueless when it comes to widgets. Any suggestions, starting points, or insight would be greatly appreciated as this whole process has given me a headache. Edited July 26, 2023 by RedHairedHero Link to comment https://forums.kleientertainment.com/forums/topic/149592-solved-create-an-inventory-item-with-a-cooldown-indicator/ Share on other sites More sharing options...
RedHairedHero Posted July 20, 2023 Author Share Posted July 20, 2023 I've managed to convert the wereitems into books with a recharge indicator when it is used. There's three issues I'm currently running into, I'll list them based on the priority I'd like to resolve them in. 1.) I currently have Discharge triggering inside the OnForceTransform function with a set time. Ideally I would like to have this trigger when Woodie reverts back to being human. I've used a couple listeners such as werenessdelta and transform_player to some success, but the main issue is I can't seem to get Discharge to work in any other function other than the OnForceTransform one. I'm guessing it has to do with inst, but I'm uncertain. 2.) The second issue is now that Woodie has the reader component he can read any books. I'm wondering if there's a way to prevent him from reading anything other than his own books. 3.) The last issue is minor, but annoying. When Woodie reads a book a message pops up above his head stating "only_used_by_waxwell_and_wicker". Idk why this is triggering since it seems to be tied to ANNOUNCE_TOOMANYBIRDS and ANNOUNCE_WAYTOOMANYBIRDS inside the speech_wilson file. Any help would be greatly appreciated. wereitems.lua Link to comment https://forums.kleientertainment.com/forums/topic/149592-solved-create-an-inventory-item-with-a-cooldown-indicator/#findComment-1652169 Share on other sites More sharing options...
Merkyrrie Posted July 24, 2023 Share Posted July 24, 2023 For 3, as its very simple to solve, when Maxwell or Wicker reads a book a specific string is called from their speech file for them to say. In the speech file this is set to "only_used_by_waxwell_and_wicker" for all books for everyone except those two as they're the only ones who need those lines. The chosen line when someone reads the book is pulled from the books definition which it looks like your "book" doesn't actually have, so its likely using GENERIC instead as a backup to prevent crashing. You could fix this by changing that part of your speech file or adding a definition to your book but I think you're actually far better off scrapping using the book/reader components here as they run you into more trouble than the pre-built code is worth. That would also solve your second problem. For 1, I'd need to see how you've been trying to code the discharge function into those other options to know what would be going wrong there. I'm not sure this needs to be any more complicated than using a barebones custom component and action, though. Its a little harder to get into and understand doing custom components and actions but its far, far more flexible than trying to bend the reader and book components to fit your needs. Especially when you most likely don't need most of what said components even have to offer. Link to comment https://forums.kleientertainment.com/forums/topic/149592-solved-create-an-inventory-item-with-a-cooldown-indicator/#findComment-1654030 Share on other sites More sharing options...
RedHairedHero Posted July 25, 2023 Author Share Posted July 25, 2023 2 hours ago, Merkyrrie said: For 3, as its very simple to solve, when Maxwell or Wicker reads a book a specific string is called from their speech file for them to say. In the speech file this is set to "only_used_by_waxwell_and_wicker" for all books for everyone except those two as they're the only ones who need those lines. The chosen line when someone reads the book is pulled from the books definition which it looks like your "book" doesn't actually have, so its likely using GENERIC instead as a backup to prevent crashing. You could fix this by changing that part of your speech file or adding a definition to your book but I think you're actually far better off scrapping using the book/reader components here as they run you into more trouble than the pre-built code is worth. That would also solve your second problem. For 1, I'd need to see how you've been trying to code the discharge function into those other options to know what would be going wrong there. I'm not sure this needs to be any more complicated than using a barebones custom component and action, though. Its a little harder to get into and understand doing custom components and actions but its far, far more flexible than trying to bend the reader and book components to fit your needs. Especially when you most likely don't need most of what said components even have to offer. I'm not well versed in coding which is why I typically aim to use pre defined code in the game to accomplish things. I can understand creating components and actions would be more flexible, but because I'm not the best at coding I wouldn't know how to create them all. I attempted to create my own components based on the reader and book components and I know I would have to add an action handler after that as well I think, but I'm not sure how much down that rabbit hole I need to go to create what I need. I had gone back into the wereitem to resolve the issue I had regarding the speech. I added a peruse function and added a talker to overwrite the GENERIC backup, but it still doesn't appear to do so. I added the caller inst.components.book.onperuse = OnPerUse and called the function... local function OnPerUse(inst, reader) reader.components.talker:Say("...") end but it just defaults back to "only_used_by_waxwell_and_wicker". As for the discharge function this is what I had. I attempted to get the GrandOwner of the item (which always returns nil, not sure why) Added a listener (this is used by Lucy under the sentientaxe component) local owner = inst.components.inventoryitem:GetGrandOwner() if owner ~= nil then inst:ListenForEvent("stopwereplayer", Discharge, owner) end and then called the function local function Discharge(inst) inst.components.rechargeable:Discharge(30) --cooldown end Obviously if I can't determine the owner it won't trigger so not sure what to do to get around that. Link to comment https://forums.kleientertainment.com/forums/topic/149592-solved-create-an-inventory-item-with-a-cooldown-indicator/#findComment-1654090 Share on other sites More sharing options...
Merkyrrie Posted July 25, 2023 Share Posted July 25, 2023 1 hour ago, RedHairedHero said: I'm not well versed in coding which is why I typically aim to use pre defined code in the game to accomplish things. I can understand creating components and actions would be more flexible, but because I'm not the best at coding I wouldn't know how to create them all. I attempted to create my own components based on the reader and book components and I know I would have to add an action handler after that as well I think, but I'm not sure how much down that rabbit hole I need to go to create what I need. That's understandable, and usually a good starting point. In this case though, since the reader component is necessary for book while also being tied to allowing the use of all books its hard to use while avoiding conflicts with wicker. Possible maybe, but not entirely worth it. I could help you understand how to do a simple component and action though, at their core they aren't as complicated as they are daunting. The component itself would just need the constructor and a couple functions. Actions are a little more tricky but once you understand them they actually open up a lot of possibilities with modding. I'll leave whether you want to do that or keep working with reader/book up to you, though. 1 hour ago, RedHairedHero said: .I had gone back into the wereitem to resolve the issue I had regarding the speech. I added a peruse function and added a talker to overwrite the GENERIC backup, but it still doesn't appear to do so. I added the caller inst.components.book.onperuse = OnPerUse and called the function... local function OnPerUse(inst, reader) reader.components.talker:Say("...") end but it just defaults back to "only_used_by_waxwell_and_wicker". Correct me if I'm wrong, but I believe onperuse is used exclusively for Wurt being able to read books and gain sanity, so its function would only be called from her reading or others who have the tag/variable "aspiring_bookworm" which then prevents them from actually reading the book for its effect. This likely isn't actually going off in that case. 1 hour ago, RedHairedHero said: As for the discharge function this is what I had. I attempted to get the GrandOwner of the item (which always returns nil, not sure why) Added a listener (this is used by Lucy under the sentientaxe component) local owner = inst.components.inventoryitem:GetGrandOwner() if owner ~= nil then inst:ListenForEvent("stopwereplayer", Discharge, owner) end At what point are you defining owner? What function would this be in? Link to comment https://forums.kleientertainment.com/forums/topic/149592-solved-create-an-inventory-item-with-a-cooldown-indicator/#findComment-1654110 Share on other sites More sharing options...
RedHairedHero Posted July 25, 2023 Author Share Posted July 25, 2023 11 hours ago, Merkyrrie said: That's understandable, and usually a good starting point. In this case though, since the reader component is necessary for book while also being tied to allowing the use of all books its hard to use while avoiding conflicts with wicker. Possible maybe, but not entirely worth it. I could help you understand how to do a simple component and action though, at their core they aren't as complicated as they are daunting. The component itself would just need the constructor and a couple functions. Actions are a little more tricky but once you understand them they actually open up a lot of possibilities with modding. I'll leave whether you want to do that or keep working with reader/book up to you, though. Correct me if I'm wrong, but I believe onperuse is used exclusively for Wurt being able to read books and gain sanity, so its function would only be called from her reading or others who have the tag/variable "aspiring_bookworm" which then prevents them from actually reading the book for its effect. This likely isn't actually going off in that case. At what point are you defining owner? What function would this be in? I think you are right about the onperuse, I didn't see another way to overwrite the speech from the item itself, but if you could help with the component action setup that should help solve the first two issues like you mentioned before. I've attached the latest wereitem prefab. I have owner being called inside the main MakeWereItem function. Granted it is inside of an if statement, but I don't think that would have any impact on pulling the owner. That starts on line 87. wereitems.lua Link to comment https://forums.kleientertainment.com/forums/topic/149592-solved-create-an-inventory-item-with-a-cooldown-indicator/#findComment-1654306 Share on other sites More sharing options...
Merkyrrie Posted July 25, 2023 Share Posted July 25, 2023 2 hours ago, RedHairedHero said: I've attached the latest wereitem prefab. I have owner being called inside the main MakeWereItem function. Granted it is inside of an if statement, but I don't think that would have any impact on pulling the owner. That starts on line 87 The reason owner is always nil then is because its setting who its owner is before the prefab is actually initialized into game, so its GrandOwner isn't actually set to you yet. Doing a DoTaskInTime could resolve this, but I don't actually think this is where you'd want to be setting its owner (If you give the item to another Woodie, who would also be able to use it, it would still have you as its owner.) Might be better to try and have it set and reset its owner at proper times, such as when it is taken out of and put in an inventory. However, it might not need to remember its owner in the first place, you can set the listener to the person who used the item when the item is used instead. For making the component, start with something like this as the most barebones base of a component : local Component_Name = Class(function(self, inst) inst = self.inst end) return Component_Name Component_Name can be anything you want that isn't already used by the game, but for the sake of compatibility with other mods it should try and be as unique as possible to your mod. In the component's file Component_Name should be capitalized and both the "local Component_Name" and "return Component_Name" need to be exactly the same. Note that everywhere else you'll be calling this component will have its name in full undercase. The file's name should be all lowercase and the same as Component_Name. The first part, "local Component_Name = Class" is the component's constructor. The variables you put inside will mostly not be local variables, they will be accessible almost everywhere in your code through this component. These variables will also be set to whatever you put them as in the constructor when the prefab with the component is first initialized and whenever the game loads in afterwards, making what you set there the item's defaults. Keeping them changed if another part of code changes that variable would require an OnSave and OnLoad but its unlikely you'll need that for now. Besides an extra function this may actually be all the component needs for the item to work, depending on how much you need the item to do/have information it can access. It would be here you could do cooldowns and such if the rechargeable component didn't already exist. Next is the harder part, setting up the action in Modmain.lua The Component Action part of this post can kind of explain it, but I'll do my best as I go here too. -- First you need an AddAction, which functions as the definition of the action that the next part will use -- component_name is the same as your component's name. -- act.doer refers to the one doing the action, act.invobject refers to the item itself. Kind of self-explanatory -- component_name:DoAction in the return is calling the function you'll be putting in your component. the DoAction part is the function's name, this can be anything you want as long as it matches the name of the function you put in your component AddAction("ACTION_NAME", "Action Name", function(act) if act.doer~=nil and act.doer:HasTag("charactertag") and act.invobject ~= nil and act.invobject.components.component_name then return act.invobject.components.component_name:DoAction(act.invobject, act.doer) end end) -- Then an AddComponentAction which will insert the action into the actions table. -- Note that this is running Client-side, and can only use information that Clients have. This is a bit more complicated but as a baseline assume it doesn't know anything about normal components. Use tags and replicas instead. "charactertag" can be replaced with any tag only Woodie has. This is the same for "charactertag" in AddAction -- "INVENTORY" is the type of action this will be. See the linked post for more information on this, but for your action you'll likely want INVENTORY -- The part of the if statement with doer.replica.rider isn't entirely needed, but it should stop the action from being able to be used when the player is on a beefalo. AddComponentAction("INVENTORY", "component_name", function(inst, doer, actions, right) if doer:HasTag("charactertag") and not (doer.replica.rider ~= nil and doer.replica.rider:IsRiding()) then table.insert(actions, GLOBAL.ACTIONS.ACTION_NAME) end end) -- Lastly the action needs a "State" it will put the player in when the action is done. For now just think of it as the animation your character will do -- "wilson" and "wilson_client" needs to stay the same, as its the default stategraph. -- "dolongaction" is the state/animation that will be played. There's a few options here, I don't know what animation you'd prefer it to be AddStategraphActionHandler("wilson", GLOBAL.ActionHandler(GLOBAL.ACTIONS.ACTION_NAME, "dolongaction")) AddStategraphActionHandler("wilson_client", GLOBAL.ActionHandler(GLOBAL.ACTIONS.ACTION_NAME, "dolongaction")) "ACTION_NAME" in AddAction and everywhere else should be all caps and needs to be unique, similar to how the component's name needs to be unique. "Action Name" does not need to be unique, nor the same as ACTION_NAME. It will be the text that's displayed when you hover over the item to do the action. Now back in the component file, add a function for the action to call -- Below the Class constructor, but above the return -- Note the syntax of this is different then other functions, as its a global one tied to this component rather than a local one. Component_Name should have caps here too. -- DoAction should be the same as what was put in the AddAction. The function paramaters (inst, doer) will be related to the paramaters given there too. -- There should be a return true at the end of the function to tell the game the action was done successfully. If you were to add an if statement that would check something then make the action *not* happen you can make the function end there with a return false that will tell the game the action did not happen successfully, playing the action failure animation and quotes in-game function Component_Name:DoAction(inst, doer) -- Everything you want the action to do Here return true end This pretty much wraps up the whole base setup for this kind of thing. You'd obviously add the component to your prefab as you would other components with inst:AddComponent I would suggest then doing relevant functions in the component file from there, those can be local functions unless you want to be able to call them elsewhere too. The DoAction function can also be where you'd put a ListenForEvent for wereness running out, as that's when the item would want to start listening for its cooldown. Then in the function the ListenForEvent goes to you can put a RemoveEventListener so its not constantly listening for that. That's a lot at once so I'll be happy to answer as many questions as you have for this. Link to comment https://forums.kleientertainment.com/forums/topic/149592-solved-create-an-inventory-item-with-a-cooldown-indicator/#findComment-1654368 Share on other sites More sharing options...
RedHairedHero Posted July 26, 2023 Author Share Posted July 26, 2023 Okay so I have found a better workaround than making the item a book, it's now a simplebook. This resolved the speech issue and the reader component issue I had. So with that resolved I would like to focus on the rechargeable issue I have. So I need to somehow tell the wereitem that Woodie has reverted back from his Wereform. Once that happens I can trigger the wereitem's rechargeable cooldown. I keep trying different listeners, but I can't seem to get any to trigger properly. I've uploaded the updated version of the code. wereitems.lua Link to comment https://forums.kleientertainment.com/forums/topic/149592-solved-create-an-inventory-item-with-a-cooldown-indicator/#findComment-1654438 Share on other sites More sharing options...
Merkyrrie Posted July 26, 2023 Share Posted July 26, 2023 3 minutes ago, RedHairedHero said: Okay so I have found a better workaround than making the item a book, it's now a simplebook. This resolved the speech issue and the reader component issue I had. So with that resolved I would like to focus on the rechargeable issue I have. So I need to somehow tell the wereitem that Woodie has reverted back from his Wereform. Once that happens I can trigger the wereitem's rechargeable cooldown. I keep trying different listeners, but I can't seem to get any to trigger properly. I've uploaded the updated version of the code. Its actually awkward to try and use any listenforevent pushed to Woodie for this because the function you give it loses all access to the item (inst) since it wouldn't be pushed as a parameter, and there's no particularly clean way to fix that as far as I'm aware. There are some hacky solutions, though. You can temporarily store the item's prefab as a variable attached to the player and then use that in the function for inst. This can be done in a component but also just directly to the player prefab (which I don't like doing much but it should work) local function OnStopWereness(player) if player.tempidol == nil then return end inst = player.tempidol inst.components.rechargeable:Discharge(10) inst:RemoveEventCallback("stopwereplayer", OnStopWereness, player) player.tempidol = nil end local function OnForceTransform(inst, reader) --Triggers when transforming into wereform if reader:HasTag("werehuman") then reader.tempidol = inst inst:ListenForEvent("stopwereplayer", OnStopWereness, reader) reader.components.wereness:SetWereMode(inst.were_mode) reader.components.wereness:SetPercent(1, true) reader.components.wereness:StartDraining() else if not reader:HasTag("mime") then reader.components.talker:Say("I have no clue what this is...") end end end 1 Link to comment https://forums.kleientertainment.com/forums/topic/149592-solved-create-an-inventory-item-with-a-cooldown-indicator/#findComment-1654449 Share on other sites More sharing options...
RedHairedHero Posted July 26, 2023 Author Share Posted July 26, 2023 12 hours ago, Merkyrrie said: Its actually awkward to try and use any listenforevent pushed to Woodie for this because the function you give it loses all access to the item (inst) since it wouldn't be pushed as a parameter, and there's no particularly clean way to fix that as far as I'm aware. There are some hacky solutions, though. You can temporarily store the item's prefab as a variable attached to the player and then use that in the function for inst. This can be done in a component but also just directly to the player prefab (which I don't like doing much but it should work) local function OnStopWereness(player) if player.tempidol == nil then return end inst = player.tempidol inst.components.rechargeable:Discharge(10) inst:RemoveEventCallback("stopwereplayer", OnStopWereness, player) player.tempidol = nil end local function OnForceTransform(inst, reader) --Triggers when transforming into wereform if reader:HasTag("werehuman") then reader.tempidol = inst inst:ListenForEvent("stopwereplayer", OnStopWereness, reader) reader.components.wereness:SetWereMode(inst.were_mode) reader.components.wereness:SetPercent(1, true) reader.components.wereness:StartDraining() else if not reader:HasTag("mime") then reader.components.talker:Say("I have no clue what this is...") end end end Thank you, inst had to be added as a parameter to OnStopWereness, but besides that it works great. I'll work more on my coding skills to see if I can do this the less hacked together approach with components, but I'm glad it works. Thanks again I'll definitely be referencing this thread going forward you were very helpful. 1 Link to comment https://forums.kleientertainment.com/forums/topic/149592-solved-create-an-inventory-item-with-a-cooldown-indicator/#findComment-1654587 Share on other sites More sharing options...
Merkyrrie Posted July 26, 2023 Share Posted July 26, 2023 3 hours ago, RedHairedHero said: Thank you, inst had to be added as a parameter to OnStopWereness, but besides that it works great. I'll work more on my coding skills to see if I can do this the less hacked together approach with components, but I'm glad it works. Thanks again I'll definitely be referencing this thread going forward you were very helpful. Oh yeah, I've been away from coding for a little bit so I guess adding a 'local' before inst slipped my mind there. And no problem! Good luck with your future modding endeavors Link to comment https://forums.kleientertainment.com/forums/topic/149592-solved-create-an-inventory-item-with-a-cooldown-indicator/#findComment-1654632 Share on other sites More sharing options...
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now