• Content count

  • Joined

  • Last visited

 Content Type 




Klei Bug Tracker

Game Updates

Hot Lava Bug Reporter

Everything posted by Clopen

  1. Thank you, Zarklord! =D I've been kind of stuck with things and you really don't want to behold the hideous, unholy, unethical workaround I was making in order to circumvent the problem... I'm gonna try incorporating everything you said here and in the other thread in a bit and see if I can make it work. I think I have the necessary knowledge for it now, thanks to you, so hopefully I'll be able to manage. =)
  2. So this is what happened so far: I created a replicable component. Added it with AddReplicableComponent(). The game gave me some syntax errors in my _replica file so I know it loads it. Added it as a component in a player character's master_postinit(). Tried to access either the component or its replica in common_postinit(), both were nil. Moved the component to be added in common_postinit(), client now crashes silently, the word "error" does not show up in the log file for either the server or the client, but the client crashes due to a break in its C++ code and right before it there is a print warning that the replica already exists even though it was nil just before I added it. Right now I have this in common_postinit(inst): if not (inst.components.transformer or inst.replica.transformer) then print("Adding in common_postinit") inst:AddComponent("transformer") end if TheWorld.ismastersim then local tf = inst.components.transformer print("Server: tf = ", tostring(tf)) else local tf = inst.replica.transformer print("Client: tf = ", tostring(tf)) end And I have this in master_postinit(inst): if not (inst.components.transformer or inst.replica.transformer) then print("Adding in master_postinit") inst:AddComponent("transformer") end And the client log show this at the very end (with no other debug prints of mine anywhere): [00:01:02]: Adding in common_postinit [00:01:02]: Client: tf = nil [00:01:02]: Movement prediction enabled [00:01:02]: Craft Pot ~~~ component loaded with no data [00:01:02]: replica transformer already exists! scripts/entityreplica.lua:77 in (method) ReplicateEntity (Lua) <74-84> [00:01:02]: Registering duplicate lua network variable 3646546233 in entity tee[100201] [00:01:02]: Break at: ..\source\simlib\Entity.cpp(347) : So I have two questions: Why can't I access a replica if it already exists? Actually, also why does it exist already if I haven't added it yet? And where do I put client-side initialization code for a player character? Thanks in advance. =)
  3. I'm somewhat new to modding DST, and I haven't dealt much with host-client communications yet since I haven't needed to, but I do now so I've been looking at examples and searching through the forums but the way components and their replicas should be made is still not clear to me. I examined Kzisor's leveler component and its companion exp widget, as well as some standard game components and random things I found in the mods I have installed, but still. Some use classifieds, some use netvars, some reference the replica in the main component, some don't...I could really use a basic rundown of when you use replicable components as opposed to a regular component added in common_postinit(); when you should use player_classified (and what it even is to begin with); when to use netvars (are the instead of the classified thing?) - and if possible, seeing an example of a simple "Hello world" replicable component would be really great. Thanks in advance. =) P.S. If you're interested in the functional context here, I'm trying to create a "transformer" component to manage all character transformations in my mod. It's basically a state machine with forms and triggers that activate the transitions between them. I'd like to give it an AddTransformation() function that lets you define the transitions as well as the trigger, and, for example, I'd like key press to be one of the options. I really want to encapsulate the entirety of the functionality into the component and its replica. Ideally, the user would simply write something such as transformer:AddTransformation("justme", "superme", transformer.Triggers.OnKey(SOME_KEY)) where transformer is the component or its replica and it will simply work. For that end I made registered an RPC call for all transformations that passes along some transformation ID. Now I need to have the code that performs the transformation to be on the server and the code that listens for a key and invokes the RPC to be on the client, but I don't know what to put where, and how to raise an event on the client when the transformation is done. Thus I come seeking the wisdom of the forum elders.
  4. Version 1.0


    Description This contains a little tool called CaseReplace that I whipped up because I got tired of using search & replace with different letter cases when renaming characters for Don't Starve. It runs a search & replace process, replacing one name with another in all the relevant files while preserving case (so, for example, "oldname" will become "newname" while "OLDNAME" will become "NEWNAME"). It also renames all the files and directories accordingly (this can be turned on or off). I bundled a batch file named RenameDSCharacter with it which runs the script using the parameters you would normally need in order to make the replacements for a standard Don't Starve character (it basically searches everywhere except zip files). IMPORTANT NOTE This tool performs a simple search & replace. It doesn't know that you only want to change text when it's referring to a character name. Therefore the name you replace needs to be unique and only appear when as the name of that character! If your character is named "e" and you run this tool to replace "e" with "a" you'll most certainly break your mod and nothing will work. Also, this tool has no undo option (replacing "a" with "e" won't undo it), so it's highly recommended you back your files up before using it. I have tested it but not rigorously, so it's best to make a backup before running this. I will not be held responsible for the loss of your work or data as a result of using this tool. Usage You need Python 3 to use them, so if you don't have it and you want to use this tool, go get it. I was using Python 3.6.6 when I made it but it's probably OK to use any version of Python 3. To use it, first extract it into your mod folder. Now you have two options: Double-click the file called RenameDSCharacter.bat and when prompted enter the name of the character you want to replace followed by the new name you want it to have (it asks you for those two; just follow the instructions). Run RenameDSCharacter.bat from command prompt and give it the current name and the new name as parameters (in that order). If you really want to, or if there's a problem making it get to the right files or something, you can also use the CaseReplace script directly. To do that, go to the folder in command prompt and type "python" (no quotes) followed by the parameters you want. You can run "python --help" to see a list of parameters Enjoy your modding. =)
  5. Actually, one more question... common_postinit() is called for an entity on both the server and client, right? And we basically look at TheWorld.ismastersim in order to determine whether we're on the client or the server - but how do we know if the player is remote or not? I have keyboard events I want to register to, and these need to be on the client only, but if someone is playing in a world without caves and they're the host then the value of TheWorld.ismastersim would still be true, no? So how do I know whether to register the key handler or not?
  6. First of all, thank you very much, Zarklord; what you wrote is really helpful. =) Ok, so I think I understand the concept of replicas (though I have a question pertaining to them which I'll write in a moment), and I think I understand more about classifieds now, but I didn't understand part of your explanation. I searched for "SetInLimbo" in the DST code (and my random collection of mods) and I only found it in entityscript.lua, in the functions RemoveFromScene() and ReturnToScene(). So regarding that: Is there ever a reason to use it directly? I looked for uses of RemoveFromScene() in the game code and found several, one of which is in inventoryitem.lua, inside OnPutInInventory(). If I understand what you said correctly, this means that when an item is picked up, its entity is removed from all clients. However, I seem to remember reading at some point that all the items in all the containers everywhere are stored in memory for all the clients at all times. How do these two fit together - or, alternatively, is my memory and/or the information inaccurate? Now, about this: I don't really get it. Let's try using concrete values, if that's OK. Say inst is some carrot and entity is Woodie. So if I invoke inst.Network:SetClassifiedTarget(entity), it will cause that entity, Woodie, to spawn only for the player playing that Woodie? I don't understand the meaning of that sentence. Does it need to be run before Woodie is spawned? If not, what happens if it's spawned already? Would Woodie disappear from everybody's clients except his own? I have similar comprehension issues with the out-of-limbo case, but clearing the above up will probably clear those up too. Now for a more specific question: How do I send information from the server to a specific client? Let's assume I have that client's player entity and/or I'm inside the scope of an RPC handler that was invoked by that client. I have a clear idea of what data I need to have where (server / client, who needs it, who doesn't) and what messages I want to send, when and between whom. I think I can implement all of the above with replicas, but if I set a netvar for a replica, won't it be sent to all the clients? In some cases that's just wasted network data since only one player will do something other than ignore it. Once again, I truly appreciate your assistance here, so thank you very much.
  7. This is more of a consultation than a direct how-to question. I implemented a feature that temporarily adds a tag to a specific target (can be anything that can do combat), and now I'd like to give this effect some visual representation, but I don't know what. I tried tinting the target red (by setting AnimState:SetMultColour(1, 0.5, 0.5, 1)), and while it worked, I'm not sure it's a good idea - A. because it might not be too noticeable, especially for reddish entities; B. because I think it might clash with other places setting color multipliers; and C... pretty sure I had a C when I started typing this sentence but I forgot it already. O_o I thought maybe to put a ring on the ground around it, but I'm not sure how to do that and it would also need to be scaled based on the target's size. Also I think if I did that then I'd want the back of the ring to be hidden by the character, another something I don't know how to do. Another idea is an aura, but I think just adding a light source would make it difficult to see exactly which entity is the target when there are several around. Also it has gameplay ramifications at night which I'd rather not have. Would be easy to abuse. So any feasible ideas on how to mark the target (and if possible an ideas on how to implement them) would be appreciated. If it helps, it's possible that only specific players will be able to see this marking (I already know how to implement that part). Thanks in advance. =)
  8. I see. Well, alright then. I'll give him a modified version of Wilson's stategraph. Thank you both. =)
  9. I made a new character and I'd like to override a specific state in its stategraph, but nobody else's (namely, I want to change its funnyidle animation to one of the emotes). I managed to override it for everyone,but I don't know how to override it just for him. I'd rather not copy Wilson's stategraph to a new file, make the modification and then assign that stategraph to him, since that would require updating when Wilson's stategraph changes. Thanks in advance. =)
  10. Well, sure, I can do that, and yeah, it would work (thanks for the suggestion), but it doesn't feel like the right approach to me. What if I want to add other customization? Do I change Wilson's stategraph and test prefabs for everything? Every character has its own stategraph; there has to be a way to alter a specific state for a specific character.
  11. Oh, you're right. How lovely. =) Thanks. ^_^
  12. DST has a module called "util" in its scripts bundle which contains various scripting paraphernalia that one may find useful. I'd like to use the weighted_random_choice() function it has, but when I use require "util" to import the code (currently to my modmain file but I plan on moving it out of there shortly) and then use it, I get an error for invoking a nil value. I know I can copy the function, of course but since it's already there and various game scripts use it, I figure there should be a way to import it into a mod environment as well - or am I wrong? Assistance would be appreciated, like always. =)
  13. Yeah, I did the same. I actually found it by looking at Inventory:Equip(). The first line there checks if the equippable item is restricted. Thanks a lot for all your help. I'm pretty sure I'll need it again (e.g. for making only specific entities attackable based on tags, a feature I'm not even close to starting work on yet and for which I'll start a separate topic if I need to). I'm making a pretty big mod with lots of new things and my knowledge of modding DST (and Lua in general, but that's less of a problem) is limited, so I'm bound to run into problems I'll need help with.
  14. I'd like to find a way to make a character refuse to equip certain items (based on tags). I've found examples of this in other mods and posts but in all of them the item is equipped, checked and then unequipped if necessary. I got that working, and if I must then I'll keep that, but I would rather have the character refuse to equip it in the first place. I.e.: When right-clicking the equipment (armor) in the inventory, it will not be equipped at all and the character will say something about it - as opposed to what happens now, where it gets equipped, unequipped and then the characters says something about it. When the equipment is picked up and the character's equip slot is empty, it should go into the inventory without being equipped - as opposed to what happens now, where... (same as above) When equipping would cause a currently-equipped item to be unequipped, I'd like for the currently equipped item to remain equipped. In other words, as I said, the character should refuse to equip it, with the behavior you'd expect when that happens. Similarly, rather than using tags, I'd like to make specific items usable only by specific characters. I'm guessing that'll be similar though. Any ideas or leads would be appreciated. =) Edit: Problem solved; see the bottom part of my own reply below.
  15. Yeah, I saw the GiveItem() function. If you'd told me I had to override it to add something in the middle I'd have chosen to stay with what I have. -_- Regarding your second idea: Using OnPickup() seems like a good idea if I don't want them to be able to have the item at all - which may actually be what I decide eventually, but for now I just want the character to be unable to equip the item, but still have the ability to carry it. I do like your first idea though, of overriding CanTakeItemInSlot(), so I went ahead and tried it, but I've come across a big problem: it seems that CanTakeItemInSlot() is invoked for every inventory slot except equipment slots. I had my injected code accept/deny the item without any regard for the slot parameter and as a result the character is unwilling to put the item in his inventory, but he's willing to equip it. Here's the code, for reference: local function heavy_armor_check(item, slot, owner) print("[Mod] given owner = " .. tostring(owner) .. " , item owner = " .. tostring(item.owner)) print("[Mod] owner.prefab = " .. tostring(owner.prefab) .. " , item.prefab = " .. tostring(item.prefab)) print("[Mod] slot = " .. tostring(slot)) if item == nil or owner == nil then return true end if item:HasTag("heavyarmor") and not owner:HasTag("canequipheavy") then if owner.components.talker then owner.components.talker:Say(GetString(owner, "ARMORTOOHEAVY")) end return false else return true end end local function heavy_armor_check_wrapper(inventory) local oldCanTakeItemInSlot = inventory.CanTakeItemInSlot inventory.CanTakeItemInSlot = function(inv, item, slot) return heavy_armor_check(item, slot, inv.inst) and oldCanTakeItemInSlot(inv, item, slot) end end AddComponentPostInit("inventory", heavy_armor_check_wrapper) If I pick up the tagged item from the ground with the equip slot empty, the character equips the item silently and no "[Mod]" print occurs at all. If I pick it up when I already have something equipped, the character says the ARMORTOOHEAVY line and the item is picked up to my cursor. Same thing happens if I try to place it in the inventory or right-click to unequip it from its equip slot, yet I can place it in the equip slot without problem. However! I have found a different, very simple solution which, in consistence with my recent "how the hell did I miss this" streak, I really should have noticed before. The equippable component has a "restrictedtag" property which only allows character with the specified tag to equip that item, which is precisely what I want. All I had to do was add this to my armor prefab's initialization function: inst.components.equippable.restrictedtag = "canequipheavy" And now only characters who are tagged with "canequipheavy" can equip it. Other characters don't even have "Equip" as a right-click action for it anymore; they can only examine it. Now, since my "heavy armor" thing is a component and not a specific prefab, I just need to add that line to my component's postinit function to apply it to every prefab that has that component and I'm done. Thanks for your help though, Ultroman; it did expose me to some useful things. P.S. The "restrictedtag" property is used in the game for the inactive Bernie.
  16. [Tutorial] Using Extended Sample Character Template

    "[00:04:46]: [string "scripts/util.lua"]:550: Could not find an asset matching images/avatars/avatar_phelps.tex in any of the search paths." You're missing that file. Check that you have an image called "avatar_phelps.png" in images\avatars and make sure you let the autocompiler run its course. You should be able to see it say that it's processing that file, I think. As a general tip, whenever you encounter a crash, search the log file for the first time the word "error" appears and look around there. The line I just copied appears one line above the first time "error" shows in the file.
  17. Thanks everyone. When I saw that the timing was off I decided to just let the periodic task run whenever it wants to as long as it's fast enough (no problem there) and do the logic of progress and ending using starting time, total duration and current time, which is pretty much what @CarlZalph said. I'll manage the rest around that.
  18. I have two different components that I activate one after the other. One uses DoTaskInTime to schedule a task for 30 seconds from now. The other uses DoPeriodicTask to schedule a task to run every 0.1 seconds over a total of 30 seconds. For some reason, the second task finishes all its iterations in 20 seconds instead of 30 like the first one. The periodic task increases a number value in every iteration, from 0 to 1, in increments of 0.1 / 30. When it reaches 1 it stops. This should take 300 iterations, so with an interval of 0.1 it should take 30 seconds in total. Even if the calculations in my code are wrong, it doesn't change the fact that the periodic task occurs too quickly. I put debug printouts right before starting the periodic task, printing the interval, and in the tick function printing the progress. The server log shows 15 prints per second, 1.5 times faster than it should be (which corresponds to the total time being 2/3 of what it should be). If I increase the interval to 0.5 the total time for the periodic task increases to about 27 seconds. I really have no idea what's going on here and how to fix it. Here's the code for the component (not much here, really): -- Fake component for enabling usage of the recharge -- item slot animation from The Forge local Rechargeable = Class(function(self, inst) self.inst = inst self.percent = 1 self.recharge_time = 0 self.interval = 0 end) local function do_recharge(inst, self) self:SetPercent(math.min(1, self:GetPercent() + self.interval / self:GetRechargeTime())) print("[Mod] Percentage: " .. tostring(self:GetPercent())) if self:GetPercent() == 1 then self:StopRecharging() end end function Rechargeable:GetPercent() return self.percent end function Rechargeable:GetRechargeTime() return self.recharge_time end function Rechargeable:SetPercent(percent) self.percent = percent self.inst:PushEvent("rechargechange", { percent = self.percent }) end function Rechargeable:SetInterval(interval) self.interval = interval end function Rechargeable:SetRechargeTime(recharge_time) self.recharge_time = recharge_time self.inst:PushEvent("rechargetimechange", { t = self.recharge_time }) end function Rechargeable:StartRecharging(starting_percentage) if starting_percentage then self:SetPercent(starting_percentage) end print("[Mod] Starting percentage: " .. tostring(self:GetPercent()) .. " ; Inteval: " .. tostring(self.interval)) if self.recharge_task == nil then self.recharge_task = self.inst:DoPeriodicTask(self.interval, do_recharge, nil, self) end end function Rechargeable:StopRecharging() if self.recharge_task ~= nil then self.recharge_task:Cancel() self.recharge_task = nil end end return Rechargeable And here is the relevant debug output: Like I said, 15 prints per second. What's the problem here and how do I make DoPeriodicTask work like I expect? I would have suspected server load but it's working faster than it should. Thanks in advance.
  19. I have some code that updates the durability of an armor (repairs it) in a timed task. It works, but the durability percentage only updates if I unequip it or equip it again. How do I get the item slot to refresh its state? ... Well, actually, I did get this to work by piggybacking on the "fueled" component's event: inst:PushEvent("percentusedchange", { percent = inst.components.armor:GetPercent() }) But that looks like an underhanded trick that only works since the ItemTile widget calls the same SetPercent function for both and doesn't look very closely at the inventory item that pushed the event. I'd be happier if I could just call a "refresh inventory bar" function or something of the same ilk. Thanks in advance. =)
  20. Ugh, I don't know what's wrong with me lately. >_< I went over all the code in armor.lua searching for a function to modify the armor durability and somehow I missed that one. I only saw TakeDamage. Thanks. =\
  21. Oh, you're right. I just automatically assumed it was local because it's a "class" function. Thanks. =)
  22. I'm trying to modify damage taken before armor checks and in a non-multiplicative manner. Specifically, I'm making a "heavy armor" component which grants a fixed damage reduction that should go before everything else. For example, if the damage reduction is 10 then when a character wearing this armor is attacked, the damage should be reduced by 10 and then the entire combat mechanism should do its job. After the damage reduction takes place I want the normal armor damage reduction, so I can't make the armor component do this work. I also don't want any of the absorbed damage to affect the equipped armors and wear them down. Ideally I would wrap the function Inventory:ApplyDamage with my mechanism and alter the damage before the inventory does its work, but I can't do that since it's a local function. I looked at combat.lua to see if I could interject in the process somehow. I found Combat:GetAttacked and Combat:CalcDamage, but I didn't see any way for me to do what I want. The effects should occur after the damage calculation is performed but before the inventory applies the damage to the equipped armor. Any ideas on how I can do this? Of course, I could always replace inventory.lua or combat.lua, but that seems extremely intrusive and I imagine it would require constant updating with the game.
  23. [Tutorial] Using Extended Sample Character Template

    I made a small utility script for renaming characters easily. It conserves case, so it should take care of both "esctemplate" and "ESCTEMPLATE" replacements at once. It also changes filenames accordingly. You can find it here, and a post I wrote with more details on how to use it here. Hope this helps someone. =)
  24. In the two (well, one and a half) mods I've made so far, I was coding for functionality only ("just make it work.") I was on a deadline and I didn't know the environment (or Lua) very well, and I did get everything working, but the result was very messy code. I ended up with 1647 lines of code in my modmain file. Now I'm about to start working on a new mod, something bigger and possibly with other people too, so this style of work is not a good approach - and I don't like it either, coming from a background of coding with a strong emphasis on design, style and conventions. So what I'm looking for here is some advice from veteran Don't Starve script writers. What are some good practices and rules of thumb for coding in Lua for Don't Starve Together? Some design patterns would also be nice (for example, I always try to wrap an existing function with my extra code rather than replace it with a copy of the original that has my additions in it.) I know there's an option for importing modules which can help reduce the size of modmain and organize the code better, but I think that when I tried using it I had some trouble accessing the GLOBAL variable (my memory's kind of fuzzy on that part). Thanks in advance. =)
  25. I made a small utility script for renaming characters easily. It conserves case, so it should take care of both "esctemplate" and "ESCTEMPLATE" replacements at once. It also changes filenames accordingly. You can find it here, and a post I wrote with more details on how to use it here. The script itself is written in Python, so it should work on Mac systems too (untested), but there's a batch file I added to facilitate using it more easily, and that won't work on Macs (at least, not without conversion or some program to run batch files on Mac, if such a thing exists). Still, the batch file is very simple, and the explanations I wrote should make it clear how to use the script without relying on the batch file, so it should still be of use. If someone wants to write a Mac equivalent of the batch file I'll gladly add it to the archive for download. Hopes this helps someone. =)