Midnight_Monday Posted June 13, 2017 Share Posted June 13, 2017 (edited) Hiya! I am looking to create a character that moves at half speed in the rain, but can also move at normal speed if they are holding an umbrella. I have written up an example piece of code which will be below: -- Checks if an umbrella is equipped local umbrella_equipped = false for k,v in pairs (inst.components.inventory.equipslots) do if v.components.dapperness then if v.components.dapperness.mitigates_rain then umbrella_equipped = true end end end -- Slows down the character if it is raining and there isn't an umbrella equipped if TheWorld.state.israining() = true and umbrella_equipped = false then inst.components.locomotor:SetExternalSpeedMultiplier(inst, "character_speed_mod", 0.5) else inst.components.locomotor:SetExternalSpeedMultiplier(inst, "character_speed_mod", 1) end (I removed the character's name as this is a personal mod) I am very new to coding so I am not sure what it is I am doing wrong. A lot of my coding knowledge comes from snooping around in the game's files so I apologize if this makes you cringe. The example code below gives me the following error (once again the name has been removed for personal reasons) Please Note: I am using Don't Starve Together [[Also an optional thing that isn't really a priority is if you are offering help, could you also try making the character get progressively slower the more damp they become?]] EDIT: I am aware of the existance of but it does not work with Don't Starve Together Edited June 13, 2017 by Midnight_Monday Link to comment Share on other sites More sharing options...
Midnight_Monday Posted June 13, 2017 Author Share Posted June 13, 2017 If you need a more in-depth look at my coding, I have included the .lua file. character.lua Link to comment Share on other sites More sharing options...
MorickClive Posted June 13, 2017 Share Posted June 13, 2017 (edited) it's complaining about this line of code: if TheWorld.state.israining() = true and mitigates_rain = false then When comparing conditional checks you need to ensure you use the "==" comparison statement, It's an easy enough thing to overlook but remember when you state "=" you are assigning a value to a variable, if you are using ==, >=, <=, ~= then you are using a conditional check. -- Ternary function: Handy tip for conditional checking for variable results. If you ever test with print() on a "true" or "false" statement then be aware that print() will unlikely be able to print it out the boolean result, ternary functions can help you here. Anything after the "AND" statement is what happens when the condition is successfully triggered, this can include returning a result based on the condition or even triggering a specific function. (pseudo code) : -- logic examples var = (statement) AND "True string or value" OR "False string or value". print( (statement) AND "True string or value" OR "False string or value" ) -- code examples print ( true AND "It's true" OR nil) -- will print: "It's true" in the DST console my_int = 1 my_bool = my_int < 1 AND true OR false -- value of my_bool would be set -- false unless my_int reflects the initial condition differently Edited June 13, 2017 by MorickClive Link to comment Share on other sites More sharing options...
alainmcd Posted June 13, 2017 Share Posted June 13, 2017 Change if TheWorld.state.israining() = true and umbrella_equipped = false then to if TheWorld.state.israining and not umbrella_equipped then Checking every tenth of a second seems overkill, you could probably tune it to every second or two. Link to comment Share on other sites More sharing options...
Midnight_Monday Posted June 13, 2017 Author Share Posted June 13, 2017 I want to thank the both of you, I could make my character slow down in the rain like I wanted! I also was able to understand coding a bit better, so thank you for that as well. If it's not too much to ask, I could you help me figure out why the character's speed does not return to normal when an umbrella is equipped? I know it's just me being a doof but I would prefer the help of someone experienced then going through hours of trial and error by myself. Not much has changed in the code but if you need another look then here you go: -- Slows character in the rain local function slowinrain(inst) --this bit will let you move freely with an umbrella local mitigates_rain = false for k,v in pairs (inst.components.inventory.equipslots) do if v.components.dapperness then if v.components.dapperness.mitigates_rain then mitigates_rain = true end end end --this will check if it is raining and either set your speed to normal if it is not, or reduce it to half if it is raining if TheWorld.state.israining == true and mitigates_rain == false then inst.components.locomotor:SetExternalSpeedMultiplier(inst, "character_speed_mod", 0.5) else inst.components.locomotor:SetExternalSpeedMultiplier(inst, "character_speed_mod", 1) end end I feel like I'm going to be frequent on these forums now... Link to comment Share on other sites More sharing options...
alainmcd Posted June 13, 2017 Share Posted June 13, 2017 dapperness is probably not what you are looking for and I don't find any reference to mitigates_rain in the game code, so that probably isn't it either. You can either check if the player is wearing an item that has rain protection (this includes a lot of headgear, Raincoat, Umbrella, etc.): local mitigates_rain = false for k,v in pairs (inst.components.inventory.equipslots) do if v:HasTag("waterproofer") then mitigates_rain = true end end or check whether it's an "umbrella" (Umbrella, Pretty Parasol and Eyebrella): local mitigates_rain = false for k,v in pairs (inst.components.inventory.equipslots) do if v:HasTag("umbrella") then mitigates_rain = true end end or check directly if it's the specific item you want, in this case an Umbrella or a Pretty Parasol: local mitigates_rain = false for k,v in pairs (inst.components.inventory.equipslots) do if v.prefab == "umbrella" or v.prefab == "grass_umbrella" then mitigates_rain = true end end All code untested, but hopefully at least enough to nudge you in the right direction. Link to comment Share on other sites More sharing options...
Midnight_Monday Posted June 14, 2017 Author Share Posted June 14, 2017 Thank you so very much, this now completely works as planned! This has been very helpful to me in terms of coding, but there are a few more things planned for this character that I don't know how to do for sure. You see I would like for the character to be able to heal themselves using sewing kits (and nothing else, I also want healing salves and even food if possible to do nothing health-wise) and I believe I did it correctly but i'm not sure. Here is the code if you need it: --character.lua: local assets = { Asset("SCRIPT", "scripts/prefabs/player_common.lua"), Asset("SCRIPT", "scripts/components/sewinghealing.lua"), Asset( "ANIM", "anim/character.zip" ), Asset( "SOUNDPACKAGE", "sound/character.fev" ), Asset( "SOUND", "sound/character.fsb" ), } --Sewinghealing.lua: local SewingHealer = Class(function(self, inst) self.inst = inst self.health = TUNING.HEALING_LARGE end) function SewingHealer:SetHealthAmount(health) self.health = health end function SewingHealer:CollectInventoryActions(doer, actions) if doer.components.health and doer.prefab == "character" then table.insert(actions, ACTIONS.SEWINGHEAL) end end function SewingHealer:Heal(target) if target.components.health then target.components.health:DoDelta(self.health,false,self.inst.prefab) if self.inst.components.stackable and self.inst.components.stackable.stacksize > 1 then self.inst.components.stackable:Get():Remove() else self.inst:Remove() end return true end end return SewingHealer --Modmain.lua: --Makes Character able to heal herself with Sewing Kits AddPrefabPostInit("sewing_kit", function(inst) inst:AddComponent("sewinghealing") inst:AddTag("irreplaceable") end) local SEWINGHEAL = GLOBAL.Action() SEWINGHEAL.fn = function(act) if act.invobject and act.invobject.components.sewinghealing then local target = act.target or act.doer return act.invobject.components.sewinghealing:Heal(target) end end SEWINGHEAL.str = "Sew-heal" SEWINGHEAL.id = "SEWINGHEAL" AddAction(SEWINGHEAL) AddStategraphActionHandler("wilson", GLOBAL.ActionHandler(SEWINGHEAL, "dolongaction")) And by extension the character should also start off with the ability to create a Sewing Kit without an Alchemy Engine (I also wanted to say sorry for my huge noobish-ness, I might be way in over my head with this mod but if I can get this done I'll only have 2 things left to create!) Link to comment Share on other sites More sharing options...
alainmcd Posted June 14, 2017 Share Posted June 14, 2017 2 hours ago, Midnight_Monday said: You see I would like for the character to be able to heal themselves using sewing kits (and nothing else, I also want healing salves and even food if possible to do nothing health-wise) and I believe I did it correctly but i'm not sure. Try it out! I don't see why it wouldn't work. Minor correction: AddPrefabPostInit("sewing_kit", function(inst) inst:AddTag("irreplaceable") if not GLOBAL.TheWorld.ismastersim then return end inst:AddComponent("sewinghealing") end) Most components are only defined and executed server-side, but tags should be added to clients too. I'm not sure why you'd want Sewing Kits to be irreplaceable, I'm guessing maybe you intend to later change it to a character-exclusive item? Not that it makes a great difference, though. If you want your character to not be healed by healing items, you can try adding something like this to your modmain.lua: local function healerpostinit(self) local OldHeal = self.Heal self.Heal = function(self, target) if target.prefab ~= "yourcharacter" then return OldHeal(self, target) end end end AddComponentPostInit("healer", healerpostinit) And similarly for edibles: local function ediblepostinit(self) local OldGetHealth = self.GetHealth self.GetHealth = function(self, eater) local health = OldGetHealth(self, eater) return eater.prefab == "yourcharacter" and health > 0 and 0 or health end end AddComponentPostInit("edible", ediblepostinit) This will make your character receive health penalties from food, but gain no bonuses. If you want your character to completely ignore health deltas from food items, use the Eater component's SetAbsorptionModifiers in your character's master_postinit: inst.components.eater:SetAbsorptionModifiers(0, 1, 1) Usual disclaimer: all code untested, I'm not responsible if your PC threatens to burn your house, or marries or refuses to marry your cat. Link to comment Share on other sites More sharing options...
Midnight_Monday Posted June 14, 2017 Author Share Posted June 14, 2017 (edited) The code works just as it should for disabling alternative sources of health gain, but now my computer is on a honeymoon with a cat I didn't even know I had. In all seriousness, I have yet another dilemma (I'm sorry for using you as a solution to all of my inadequacies) when I try and heal the character using a sewing kit the option just doesn't appear. Like how when you are using Healing Salves you can see an option for right-clicking to heal yourself with, however this option doesn't appear on the Sewing Kit and only has "Examine" EDIT: I also attempted copying the script in the character.lua as well as the modmain.lua but it gave me an error that the coding wasn't valid to be in the character.lua file. Edited June 14, 2017 by Midnight_Monday Link to comment Share on other sites More sharing options...
alainmcd Posted June 14, 2017 Share Posted June 14, 2017 Add this towards the end of your modmain.lua: AddComponentAction("USEITEM", "sewinghealing", function(inst, doer, target, actions, right) if doer.prefab == "yourcharacter" and target.replica.health ~= nil and target.replica.health:CanHeal() then table.insert(actions, SEWINGHEAL) end end) AddComponentAction("INVENTORY", "sewinghealing", function(inst, doer, actions, right) if doer.prefab == "yourcharacter" then table.insert(actions, SEWINGHEAL) end end) Note: I don't have any experience with adding new actions and again, untested code, so this is mostly guesswork from briefly looking at other mods. Explanation on what's going on/what I expect to happen: AddComponentAction does what it says (see data/scripts/componentactions.lua - you are actually calling AddComponentAction from data/scripts/modutil.lua but it just calls the one from componentactions.lua along with the modname). Its first argument is the actiontype: "USEITEM" lets you use the item directly on something, "INVENTORY" lets you use it from your inventory, usually on yourself (again, see componentactions.lua). The second argument is the component that carries your action. The last argument should be a function to determine whether the action can be performed. As an example, I've added a reasonable restriction: you only want your character to be able to heal with a Sewing Kit, and you only want it to be used on things that can be healed. CanHeal is used to avoid healing entities with health that shouldn't be healed normally (like walls). Notice that I'm using target.replica.health instead of target.components.health here: this is because, as noted before, components are usually handled by the server only. Replicas are handled client-side and contain only enough information for the client to work on its own. This is so clients can see whether the action can be performed. If the player chooses to perform an action, this is sent to the server where it is actually executed. Good luck! Link to comment Share on other sites More sharing options...
kaachiel Posted June 15, 2017 Share Posted June 15, 2017 @Midnight_Monday Hi! Thanks for sharing your code (for walking slower in the rain) but I can't seem to make it work, I don't see any difference for my character. How did you get it to work? Can you help me? local function slowinrain(inst) --this bit will let you move freely with an umbrella local mitigates_rain = false for k,v in pairs (inst.components.inventory.equipslots) do if v:HasTag("waterproofer") then mitigates_rain = true end end --this will check if it is raining and either set your speed to normal if it is not, or reduce it to half if it is raining if TheWorld.state.israining == true and mitigates_rain == false then inst.components.locomotor:SetExternalSpeedMultiplier(inst, "character_speed_mod", 0.25) else inst.components.locomotor:SetExternalSpeedMultiplier(inst, "character_speed_mod", 1) end end Link to comment Share on other sites More sharing options...
Midnight_Monday Posted June 16, 2017 Author Share Posted June 16, 2017 (edited) @kaachiel It's really not anything that you did wrong, it's just something that wasn't in the code to begin with. You see in the master_postinit section of your character.lua you have to add a line of coding in somewhere below the hunger rate option. Here is an example -- This initializes for the server only. Components are added here. local master_postinit = function(inst) -- choose which sounds this character will play inst.soundsname = "wilson" -- Stats inst.components.health:SetMaxHealth(200) inst.components.hunger:SetMax(150) inst.components.sanity:SetMax(150) -- Damage multiplier (optional) inst.components.combat.damagemultiplier = 1 -- Hunger rate (optional) inst.components.hunger.hungerrate = 1 * TUNING.WILSON_HUNGER_RATE inst:DoPeriodicTask(1/10, function() slowinrain(inst) end) inst.OnLoad = onload inst.OnNewSpawn = onload end . The code you are missing is: inst:DoPeriodicTask(1/10, function() slowinrain(inst) end) That will check every 1/10th of a second for the slowinrain function. (Checks if it's raining, and if they have an umbrella equipped) On 6/13/2017 at 0:43 PM, alainmcd said: Checking every tenth of a second seems overkill, you could probably tune it to every second or two. but as alainmcd said, you can adjust this number to whatever you think fits. Running constant checks in the background might slow the game down for anybody not playing as a host, but this all relies on the person's internet in general. But it is something to keep in mind. Edited June 16, 2017 by Midnight_Monday Link to comment Share on other sites More sharing options...
alainmcd Posted June 16, 2017 Share Posted June 16, 2017 @Midnight_Monday, I just saw you asked about knowing how to craft Sewing Kits from the get-go (and I ignored you - sorry!). Try this in your master_postinit: inst.components.builder:UnlockRecipe("sewing_kit") Sometimes the game doesn't like performing certain tasks during initialisation because not everything is properly set up yet. The usual workaround is to tell the game to perform the task in the next frame. Try this instead if the game crashes: inst:DoTaskInTime(0, function(inst) inst.components.builder:UnlockRecipe("sewing_kit") end) Usual disclaimer: untested. What could go wrong? Btw, did you get your new actions to work properly? Link to comment Share on other sites More sharing options...
Midnight_Monday Posted June 17, 2017 Author Share Posted June 17, 2017 (edited) Don't worry it's fine, everything is working exactly as it should now thanks to you! You've been a big help, now there's one final thing to add to my character mod before they can finally be put to rest. I am trying to add a custom (and character specific) crafting tab, but i'm not sure how to go about it. From looking at Wickerbottom's files I know that you have to give them a tag to gain access to the tab, and I've established prefabs (of course not in the code as it would cause a game crash) I'm using the mod Camp Cuisine as reference, but I don't want to do anything too major based off of that mod as the version I have is from DS and not DST. I'm not looking to add anything fancy into this tab, in fact everything my character should be able to craft will just be weaker versions of pre-existing items in the game, if you need specifics. A Walking Cane [50% less durability than a normal walking cane] A Craftable Touch Stone [Naturally, very expensive.] A Lazy Forager [But a hand-held version if possible] A Pig Helmet [25% less durability than a normal helmet] A Lantern [50% less durability than a normal lantern] A Blow Dart [ 1 time use, and half the strength.] A Stronger Blow Dart [ 1 time use, and a little less than the same strength as a normal blow dart] A Slot Machine [has a higher chance of giving you better items the more health you sacrifice] The Crafting Tab's name is going to be Powerup's and I have recipes in mind for each item, but they might require fine tuning. If there is anything you need me to elaborate on just ask, all Art of course is going to be handled by me, it's the one thing I know how to do. Edited June 17, 2017 by Midnight_Monday Link to comment Share on other sites More sharing options...
alainmcd Posted June 17, 2017 Share Posted June 17, 2017 So... Spoiler local Ingredient = GLOBAL.Ingredient local myrecipetab = AddRecipeTab( "MYRECIPETAB", --tab name 999, --sort: force to last pos "images/hub/mytab_atlas.xml", --atlas: place your atlas here "mytab_icon.tex", --icon: place it next to your atlas "mycharacter", --owner_tag ,--unused --crafting_station: only used by special crafting stations like sculpting ) AddRecipe( "weakcane", --name {Ingredient("gold", 3), Ingredient("twigs", 5)}, --ingredients myrecipetab, --tab: neat trick from SageOfLegend's Wynn mod GLOBAL.TECH.NONE, --level: can always be crafted nil, --placer nil, --min_spacing nil, --nounlock nil, --numtogive: defaults to 1 if nil nil, --builder_tag: not needed since the tab is already character-restricted "images/inventoryimages/weakcane.xml", --atlas , --image: leave blank and place your "weakcane.tex" next to its atlas , --testfn: not needed ) AddRecipe( "weakdart", --name {Ingredient("gold", 3), Ingredient("feather_canary", 5)}, --ingredients myrecipetab, --tab: neat trick from SageOfLegend's Wynn mod GLOBAL.TECH.MAGIC_TWO, --level: magic required nil, --placer nil, --min_spacing true, --nounlock: can't be learned if nounlock is set to true 3, --numtogive: defaults to 1 if nil nil, --builder_tag: not needed since the tab is already character-restricted "images/inventoryimages/weakdart.xml", --atlas , --image: leave blank and place your "weakdart.tex" next to its atlas , --testfn: not needed ) AddRecipe( "resurrectionstone", --name {Ingredient(GLOBAL.CHARACTER_INGREDIENT.HEALTH, 50), Ingredient(GLOBAL.CHARACTER_INGREDIENT.SANITY, 100), Ingredient("rocks"), 100}, --ingredients: special ingredients CHARACTER_INGREDIENTS myrecipetab, --tab: neat trick from SageOfLegend's Wynn mod GLOBAL.TECH.MAGIC_THREE, --level: magic required "resurrectionstone_placer", --placer: you need to create a placer 6.4, --min_spacing: defaults to 3.2 if nil nil, --nounlock nil, --numtogive nil, --builder_tag: not needed since the tab is already character-restricted "images/inventoryimages/resurrectionstone.xml", --atlas , --image: leave blank and place your "resurrectionstone.tex" next to its atlas , --testfn: not needed ) Lots of placeholder-y code and more illustrative than game-intented, but most of it should be understandable. The recipe tab is fairly straightforward, only to note that you don't need to declare a variable for it, but it makes things much simpler when adding recibes to the tab, as commented. As for recipes: name: The recipe's name must be the same as the prefab to craft. ingredients: As a rule, recipes only require three different ingredient types. I'm not sure if this is a hard limit. special ingredients: There are four CHARACTER_INGREDIENT's, namely HEALTH, MAX_HEALTH, SANITY and MAX_SANITY. All of them must be used in multiples of 5. HEALTH and SANITY apply a simple stat penalty, whereas MAX_HEALTH and MAX_SANITY place a cap on the stat (Maxwell's minions, Resurrection Statues in single player). I strongly recommend you avoid the MAX_ special ingredients, since you'd then want to add somewhere else some code to remove those caps. tab: as noted, use the local variable to your tab. placer: A prefab to act as a placer for structures. More below. min_spacing: Craftable structures can't be placed within a certain radius of any other structure or mob. Default value is 3.2. For reference, a floor tile's length is 4 units. nounlock: A character won't learn the recipe if set to true. numtogive: 1 if nil. builder_tag: Restrict the recipe to characters with a certain tag. The whole tab is already restricted, so no need. atlas: A regular atlas file. Your image HAS to be named prefabtocraft.tex and be placed alongside its atlas. testfn: Don't use. Placer: You need a prefab to act as a placer for your structure. Since the Resurrection Stone doesn't have a placer prefab, you need to create one. You can probably get away with something extremely simple: create a prefabfile (e.g. resurrectionstone_placer.lua in scripts/prefabs) with this code: return MakePlacer("resurrectionstone_placer", "resurrectionstone", "resurrectionstone", "idle_activate") And, hopefully, it'll work. I looked at resurrectionstatue.lua, which does have a placer. The only mention of it is the very last line, it doesn't reference any assets. The MakePlacer function is defined in scripts/prefabutil.lua and seems clear enough. So that should take care of itself. And... that's it? If it works, obviously. Link to comment 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