Serpens

  • Content Count

    1379
  • Joined

  • Last visited

 Content Type 

Profiles

Forums

Downloads

Klei Bug Tracker

Game Updates

Hot Lava Bug Reporter

Posts posted by Serpens


  1. Regarding starting worldjumping within caves:
    I'm no shards expert and it is too long ago to remember all consequences... Not sure if some code is only possible at master server or what the reason was for only adding worldjump component to the master server...

    So if you are no expert on it as well, I would suggest to try workarounds. So either give the player in caves an item/recipe that they need to enable on the master server. Or use some  slave to master communication code, so the Gateway in caves notifies the master server to start the worldjump (in this case you would need GEMAPI mod, to make sure the jumpdata from players in caves is saved)

    • Like 1

  2. Thanks, you should REALLY read all of my text in the tutorial linked above. I think nealry everything (except jumping within cave) can be done this way, without copying modifing anything of my code.
    For example the last post in this tutorial describes how you can save and load your personal "jumpdata". So you could save and load the equip slots this way, regardless of my code elsewhere.
    And the first post in that thread describes how you can create predefined worlds the player will hop one after the other (just like in adventure mod).

    Quote

    where each time you world jump, worldgen components of previous mods will be disabled, and the worldgen of a new mod will be enabled

    do you already know how to do that? I'm not aware of a way to achieve this, without copy/pasting alot of code from that mod.


  3. Hi :)
    (link to my mod 
    https://steamcommunity.com/sharedfiles/filedetails/?id=756229217
    link to tutorial how to add your own stuff https://forums.kleientertainment.com/forums/topic/111396-tutorial-adventure-add-your-own-worlds/ )

    so I think it is best if you first describe exactly what your mod should do. I still don't understand it 100%.

    From what I think I understand so far:
    a) You want the ancient gateway work like a teleportato, so worldjumping to a new world. -> in my adventure mod you can also jump with maxwells door in the first world, so the teleportato or the parts are not mandatory. It should be possible to replace it easily with the ancient gateway.
    b) Since this gateway is within caves (non master) and my mod currently only allows worldjumping on master shard (forest) we need an adjustment here, right? -> I can add this to my "API" to allow this.

    Open questions:
    1) What kind of worlds should be generated when worldjumping with the gateway?
    1.1) Should the previous world be destroyed and a new world generated, like my mod currently does? Or do you want to create new worlds that exist next to each other (more shards), which would be beyond my knowledge?
    1.2) And how should the worlds look like? Are they just a random world/based on game settings ? Or are they predefined worlds, like in my adventure mod?
    2) at the mod page you mentioned "health caps" no clue what you mean by this: Max health for mobs? Or for players? And what will these caps do or are they a requirement?
    3) What do you mean by "body slot" and those equipment slots? For what purpose do you "take them into account"? As requirement of jumping to the next world is possible? Or are you talking about transfering items to the next world?

    Please tell me more about your plans. Only then I can decide if I add the needed changes on my side to the API (see tutorial above) or if they are by far too much and we need another solution.

     


  4. Feature Request:
    Could Klei please add a way for mods to change existing settings? For example add more season-length options to the season settings.
    Currently this is not possible without again alot of work and overwriting game functions, while it would be so damn easy for Klei to add this option.
    I could write you the few lines code you need to add, to also allow replacing/merging existing settings.

    edit:
    was not too complicated, but only thanks to upvaluehacker. Following code in modworldgenmain.lua:

    -- ## Add more options to existing game settings.
    -- this is an example to add 3 and 6 days to all season lengths. Requires upvaluehacker (google for it if you dont have it)
    local UpvalueHacker = GLOBAL.require("upvaluehacker")
    local customize = GLOBAL.require("map/customize")
    local WSO = require("worldsettings_overrides")
    local RefreshWorldTabs = UpvalueHacker.GetUpvalue(customize.RemoveCustomizeGroup, "RefreshWorldTabs")
    local customize_descriptions = UpvalueHacker.GetUpvalue(customize.GetDescription, "descriptions")
    	local new_seasonlengths = {{ text = "3 days", data = "3__daysseason", pos=1 },{ text = "6 days", data = "6__daysseason", pos=2 }}
    for _,length in ipairs(new_seasonlengths) do
        table.insert(customize_descriptions.season_length_descriptions,length.pos,length)
    end
    UpvalueHacker.SetUpvalue(customize.GetDescription, customize_descriptions, "descriptions")
    RefreshWorldTabs() -- refresh to display new settings
    	local seasons = {"autumn","summer","spring","winter"}
    for i,season in ipairs(seasons) do
        local old_fn = WSO.Post[season]
        WSO.Post[season] = function(difficulty,...)
            if string.find(difficulty,"__") then
                local diff_split = difficulty:split("__") -- this way we det the desired length number from aboves data string
                GLOBAL.TheWorld:PushEvent("ms_setseasonlength", {season = season, length = diff_split[1]})
            elseif old_fn~=nil then
                return old_fn(difficulty,...)
            end
        end
    end
    ---------------------------

  5. The final solution to the question:
    How does a mod change Pre/Post settings on world-generation, without forcing them on every load is the following code.

    We (penguin0616 and I) spent over 50 hours for this, while it would be a "one liner" for Klei to simply add modsupport, so mods can easily change them... And my teleportato mod is still not completely made compatible to that QoL update, guess it will take several more hours.. now you see why I thought about abandoning my mods...)
    In modworldgenmain.lua:

    local overrides_forest = {dropeverythingondespawn = "always"} -- put your Pre/Post settings you would like to load here
    local overrides_cave = {dropeverythingondespawn = "always"}
    
    local WSO = require("worldsettings_overrides")
    local savedata = nil
    local new_game = true
    local function GetSaveData()
        if new_game then
            if savedata then
                return savedata
            end
            local i = 1
            local stack_i = 3 -- we increment this in case another mod is overwriting a function, to make sure getlocal still works.
            while true do
                if stack_i>20 then --give up
                    return nil
                end
                local name, value = GLOBAL.debug.getlocal(stack_i, i) -- Level 1 is where we are now, Level 2 is our Pre function calling this, level 3 is gamelogic.lua:362 or gamelogic.lua:367 depending on the savedata.
                if name then
                    if name == "savedata" then
                        if value.meta.SERP_MOD_HAS_OVERRIDEN then -- only do it once per world
                            new_game = false -- do not try it again, it is no new game, so no need to find savedata again.
                            return nil
                        end
                        savedata = value
                        savedata.meta.SERP_MOD_HAS_OVERRIDEN = true
                        return savedata
                    end
                    i = i+1
                else
                    stack_i = stack_i+1
                    i = 1
                end
            end
        end
    end
    for i,PrePost in ipairs({"Pre","Post"}) do
        for name, fn in pairs(WSO[PrePost]) do
            if overrides_forest[name] or overrides_cave[name] then
                local old_fn = WSO[PrePost][name]
                WSO[PrePost][name] = function(difficulty,...)
                    if new_game then
                        if not savedata then -- only when it is a new game
                            GetSaveData()
                        end
                        if savedata then
                            if savedata.map.prefab=="forest"  then
                                if overrides_forest[name] then
                                    savedata.map.topology.overrides[name] = overrides_forest[name]
                                    difficulty = overrides_forest[name]
                                end
                            elseif savedata.map.prefab=="cave" then
                                if overrides_cave[name] then
                                    savedata.map.topology.overrides[name] = overrides_cave[name]
                                    difficulty = overrides_cave[name]
                                end
                            end
                        end
                    end
                    if old_fn then
                        return old_fn(difficulty,...)
                    end
                end
            end
        end
    end


     

    • Like 1

  6. On 3/23/2021 at 7:39 PM, zarklord_klei said:

    For world settings, simply create a function in worldsettings_overrides.lua's Pre or Post table(depending on what the setting affects) with the same name as the setting, that function will get called with either "default" or the option selected by the user, for world generation it's more complicated, I would suggest looking at forest_map.lua to see ways to do that.

    This is still the correct way to do this for world generation options.

    I would recommend modifying the functions in WorldSettings_Overrides.Pre/Post to either run custom logic, or fake a different setting, like this:

    
    local WSO = require("worldsettings_overrides")
    local _day = WSO.Post.day
    
    function WSO.Post.day(difficulty)
    	if some_check_for_adventure_mode then
    		--force a setting
    		difficulty = "onlynight"
    	end
    	_day(difficulty)
    end
    --or
    function WSO.Post.day(difficulty)
    	if some_check_for_adventure_mode then
    		--completely custom setting.
    		TheWorld:PushEvent("ms_setseasonsegmodifier", day = 0.2, dusk = 1.5, night = 1.3)
    		return
    	end
    	_day(difficulty)
    end

     

    thank you. but none of them is a good solution, making it incompatible to other mods that want to change seasons...

    My mod should change the season once when starting the world the first time. After that, all other mods should be able to do whatever they want (eg. an item which is able to change season length or whatever).
    So I guess I will do a PostInit for the world, then call WorldSettings_Overrides.Post["autumn"]("noseason") or similar (or directly the PushEvent) and try to remember, that I called it once, to not call it again on next loading.
    Still no good solution, but better.

    edit:
    ok, only calling this does not work, because the change is not saved O.ô really mod-unfriendly (because it means an item can not easily change season length or so midgame).
    Will see if I can instead override the setting... ... but this also won't help -.- because it seems when loading a game, it will always take the settings chosen in the worldscreen.
    All in all I come to the conclusion, that your code is indeed the only viable way to achieve my goal, but it still makes it incompatible to other mods doing the same...

    edit2:
    Ah no, it would be better if my mod could change the settings on the worldscreen after world was generated. This way the game would save the setting and the users are able to change them, if they don't like them.
    But how to do this!?

    edit3:
    Solution:
    because of steam apiv2 I forgot to look at your (Zarklord) worldsettings mod (because I did not find the new location of mods). But I will now do it, like you did it there, simply changing eg. TheWorld.topology.overrides.day and then reload the world once per game. This will change the settings on the worldscreen and if the user is unhappy with it, they can simply change it to whatever they want.


    Modding after that QoL update:

    Spoiler

    All in all the changes of this update regrading modding are horrible, especially APIv2 from steam. I really consider, after so many years, to stop modding DST and abandon my mods...

    edit:
    Especially because there are no notes about how to deal with many of these changes. Ok, there was never a modding documentation for DST, but when you do so drastic changes, I still expected some kind of introduction and how to deal with it. I'm no expert modder, but I'm a good one and can read/understand the lua game code to find out many things myself. But I really struggled to fix my mods and steam APIv2 makes it even more complicated.

    edit2:
    don't get me wrong, I like your work Zarklord very much! You are an important addition to the DST dev team and pushing important improvements. And it looks like you are the only dev that is coding and watching the forum at the same time, maybe DST can afford another dev to help in the forum, at least to communicate big game changes and their effect on mods.



     

    • Sad 1

  7. maybe look instead at the worldgeneration code from stuff that spawns in the ocean? Then generate a setpiece there with a boat and a spawnpoint on it. And look at the wilderness-mode code, to see if it is possible to spawn your character there, instead of the portal (if not you can teleport him after game start to the boat)

    it is still quite difficult, so I won't help you more. But you may tkae a look at some of my mods which add/change setpieces:
    https://steamcommunity.com/sharedfiles/filedetails/?id=758921911

    https://steamcommunity.com/sharedfiles/filedetails/?id=756229217

    but you will also find some functions like "SpawnPrefabAtLandPlotNearInst" here, maybe you can rewrite them to your use:
    https://github.com/Serpens66/DST-Teleportato-Mod/blob/master/workshop-756229217/scripts/tele_helpers.lua

    • Thanks 1

  8. thanks. @zarklord_klei Could you please add this anywhere in your tutorial from this update? https://forums.kleientertainment.com/forums/topic/127319-world-options-and-mod-changes/
    I mean it is a little bit important for modders to know where mods are stored. And it is important to be able to unpack this new "xyz_legacy.bin" folder. (edit just like a zip file)
    I first thought you are hiding your own mods, when I did not found them after subscribing, sorry for that.
    But still, everyone needs to be able to unpack and read mods, also from others.

    And how should modders proceed now? Still store their unpacked mods in the previous folder? Or store it somewhere save, because they might get deleted? Any guidance about this change would be helpful.

     

    • Like 1

  9. use my mod as example:
    https://github.com/Serpens66/DST-Evolving-Maxwell-Shadows/blob/master/modmain.lua

    search for the line "AddComponentPostInit("finiteuses",function(self) -- unlimited weapons/tools" to change the duration only for your prefab. My code changes it for everything with "finiteuses" so also weapons. If you dont wan to change weapons you need to add an if condition to catch all weapons.
    Depending on the modsettings the duration can be unlimted, but it can also be higher/less than default.

    And for faster work (less needed hits), you can do in your postinit of your character this:
    inst:AddComponent(
    "workmultiplier")
    inst.components.workmultiplier:AddMultiplier(GLOBAL.ACTIONS.CHOP, multiplier, "your_unique_naming")
     

    • Thanks 1

  10. you just need to make sure that all players have the same version.
    So either send them the files directly (without steam) or make sure all of them are your steam friends, so they can download the hidden mod (edit: thought "hidden" already means "friends only").
    And make sure that the version on your PC matches the version you uploaded to steam/gave your friends.


  11. ok thanks. I still don't get why my code version does not work (when remobing the replica), but this container stuff is really specific and really terrible , so I will just accept the new solution :)

    One thing you mentioned elsewhere, but you should add it here to your tutorial:
    The name of the containerparams, so currently above "my_container_params", needs to match the prefab name you want them to use on.
    That means if you want to add 3 things with container, while all of them should use the same size of container, you still need to add all three of them to the containerparams.

    And you dont need that big code if you just want to use and exisiting size/typ of container.
    You can also do:

    containers.params.my_prefab = deepcopy(containers.params.chester)

    to use the same like chester.

    edit:
    deepcopy is important, because otherwise you would also change chesters container, when you change your new one, since they would be linked)

    • Like 1

  12. thanks, but is the new code complete?
    I guess at least a line like: inst.components.container:WidgetSetup("my_container_params") is missing in the prefab function, isn't it?

    And of course, even if overwriting the widgetsetup is now deprecated, please still correct it. The return of the old_widgetsetup is missing data:

    return old_widgetsetup(container, prefab,data) -- do not miss data, important!

    Now some more questions:
    1) Why was the replica.WidgetSeutp call previously necessary? And how did this change? I see no call of this replica in containers, where is it done, why wasn't it done before?
    2) If I only wan't the same widget like eg. chester for my anything. Then many mods try "inst.components.container:WidgetSetup("chester")". And I wonder, did this work before? Does this still work? Because when I made my shadowmaxwell mod, I remember that this caused problems and I had to copy paste the params of chester... In addition, if this "chester" line now works, why is the replica WidgetSetup still needed? It will crash on clients on opening, if it is not there.


  13. 1 hour ago, penguin0616 said:

    It wasn't in the changelog.

    image.png.d126d3211128a10bec3e21077e01814e.png

    thank you. and can you provide examples which code simnplifies know to what with help of this?

    Is it for mod-containers? I currentlyknow 2 ways to add mod containers, while both of them are really too big hassle:
    1) Override containers widgetsetup for your specific prefab, return old function for others.
    2) Don't override, but pass your params to the widgetsetup call as data. current problem: you need to copy paste the params,if you want the same like already existing -> I guess this is the place where the containers.params kicks in? So now you can do sth like:
    inst.components.container:WidgetSetup("shadowBMduelist",containers.params.chester) ? But unfortunately, all those mods which are using the first attempt, always forget to return "data" in their "old_widgetsetup" call, which makes all mods using the second method incompatibel to them =/

     


  14. @zarklord_kleimore and more mods suffer from this and it is related to the latest QoL update, before everything worked.
    But it is also quite likely, that the code from those mods is not the best. But the reason for this is like mentioned above, that there is no game code you could copy for your own containers and there is no tutorial. So everyone is using that code, regardless if it is good or not. ( I mean, why do mods at all need that line "inst.replica.container:WidgetSetup("chester")" while the original files never use this...)

    So I would suggest that you check if there was a bug introduced and/or if you can improve the way modders add container widgets (or simply use exisiting ones).


  15. @CarlZalph

    I get the same crash for Chester Family mod.
    I currently try to help fixing it, but I have absolutely no clue what the problem is. It was "introduced" with the latest game update.

    It is happending when a new chester is first time spawning in (only for clients)

    In general all "custom container" code for mods is horrible, right?
    There is no tutorial for it and there is also no code from the game you could copy. There are only mods you coudl copy from, but many many many of them have buggy code, like overwriting the WidgetSetup (which I can't beleive that this is the intended way).
    And then the problems with replica for clients. really terrible.

    The line leading to the crash seems to be a line like this in prefab file:

        if not GLOBAL.TheWorld.ismastersim then
            inst:DoTaskInTime(0, function(inst)  
                inst.replica.container:WidgetSetup("chester")             
            end)
            return inst
        end

    You wont find such code in normal chester or in treasurechet or any container, but in most mods which add containers, even if they are the same like chester, because otherwise it crashed for clients.
    But now this line is causing crashes, although sometimes only in specific circumstances... not easy...


  16. On 2/22/2021 at 8:47 PM, penguin0616 said:

    It's going to be very happy modding :angel:

    Especially since "containers" now returns the params table. No more replacement hell for them :>

    can you elaborate on this? found nothing abouts this in changelog and no "params" in any container file I searched.


  17. 1 hour ago, zarklord_klei said:

    This should be resolved now 

     

    I will take a look at this on monday.

    thank you very much.
    For easier work, I will summarize how I used to change genration-settings (this is an "easy" example, copy pasted and merged from my scripts, so may not be functional, but shows what I did previously).

    in modworldgenmain :

    local function IsModLoaded(modname) -- there is no TheNet on world generation, so we use this to find out about gem api
        return GLOBAL.KnownModIndex:IsModEnabled(modname) or GLOBAL.KnownModIndex:IsModForceEnabled(modname)
    end
    local GEMAPIActive = IsModLoaded("workshop-1378549454")
    
    AddLevelPreInitAny(function(tasksetdata)
        local _overrides = nil
        if GEMAPIActive then -- to prevent gem api to revert our changes in rare cases
            _overrides = GLOBAL.shallowcopy(tasksetdata.overrides)
        end
        if tasksetdata.location == "forest" then
            tasksetdata.tasks = {"Tentacle-Blocked Spider Swamp"}
            tasksetdata.numoptionaltasks = 0
            tasksetdata.optionaltasks = {}
            tasksetdata.set_pieces = {}
            tasksetdata.required_setpieces = {}
            table.insert(tasksetdata.required_setpieces,adventureportal)
            tasksetdata.numrandom_set_pieces = 0
            tasksetdata.random_set_pieces = {}
            tasksetdata.required_prefabs = {"spawnpoint_master","adventure_portal"}
            tasksetdata.ocean_prefill_setpieces = {} -- delete any ocean stuff
            tasksetdata.ocean_population = {} -- delete any ocean stuff
            
            if tasksetdata.overrides==nil then
                tasksetdata.overrides = {}
            end
            tasksetdata.overrides.world_size  =  "small"
            tasksetdata.overrides.wormhole_prefab = "wormhole"
            tasksetdata.overrides.layout_mode = "LinkNodesByKeys"
            tasksetdata.overrides.deerclops  =  (GetModConfigData("difficulty")==0 and "never") or (GetModConfigData("difficulty")==1 and "never") or (GetModConfigData("difficulty")==2 and "rare") or (GetModConfigData("difficulty")==3 and "default") or "never"
            tasksetdata.overrides.dragonfly  =  "never"
            tasksetdata.overrides.bearger  =  (GetModConfigData("difficulty")==0 and "never") or (GetModConfigData("difficulty")==1 and "never") or (GetModConfigData("difficulty")==2 and "rare") or (GetModConfigData("difficulty")==3 and "default") or "never"
            tasksetdata.overrides.goosemoose  =  (GetModConfigData("difficulty")==0 and "never") or (GetModConfigData("difficulty")==1 and "never") or (GetModConfigData("difficulty")==2 and "rare") or (GetModConfigData("difficulty")==3 and "default") or "never"
            tasksetdata.overrides.antlion = "never"
            tasksetdata.overrides.season_start  =  "autumn"
            tasksetdata.overrides.day = "noday"
            tasksetdata.overrides.autumn = "veryshortseason"
            tasksetdata.overrides.winter = "veryshortseason"
            tasksetdata.overrides.spring = "veryshortseason"
            tasksetdata.overrides.summer = "veryshortseason"
            tasksetdata.overrides.keep_disconnected_tiles = true
            tasksetdata.overrides.no_joining_islands = true
            tasksetdata.overrides["has_ocean"] = false
        end
        if GEMAPIActive and _overrides~=nil then -- to prevent gem api to revert our changes in rare cases
            local overrides_to_block = {}
            for k, v in pairs(tasksetdata.overrides) do
                --original blockoverrides updateanyways are 3 things that could be in this table that arent actually overrides.
                if k ~= "original" and k ~= "blockoverrides" and k ~= "updateanyways" and _overrides[k] ~= v then
                    table.insert(overrides_to_block, k)
                end
            end
        	GLOBAL.gemrun("overridesblocker", tasksetdata.overrides, modname, overrides_to_block)
        end
    end)

    All new "world_settings", so the ones you could change after the world was generated, are no longer affected by this code. So no way to change season lenghts, or day length and stuff like this with this code.

    And I got "overrides index nil" errors (after recent update), so I had to add these lines:

            if tasksetdata.overrides==nil then
                tasksetdata.overrides = {}
            end

    and I noticed, that AddLevelPreInitAny seems to be called twice now, one time with overrides not being nil and a second time after the world was generated with it being nil. Not 100% sure though.


    Anyway, when you gave me the code to "prevent gem api to revert my changes", you already said that you did not expect anyone to change generation-settings like this. So maybe this also means and especially meanwhile after this QoL update, that there is a much better way to achieve this? Which is it?