Modding Help Tool that paints objects

Discussion in 'Starbound Modding' started by mastercookie, Feb 14, 2017.

  1. Charlatan

    Charlatan Parsec Taste Tester

    Ya thats true, but its not just a whitelist. If you look into the data of dyemod you'll see it edits every dye-able object separately.

    However, as you said, its indeed just a matter of time to do the same for a 1.0+ version too.
     
  2. mastercookie

    mastercookie Existential Complex

    yep, u're right, it adds retainObjectParametersInItem to everything.
    funny thing is when i was making my mod compatible with dyemod, i just added a whitelist, it seemed to work fine.

    anyways as i mentioned i may help with batch editing, i just need one example.
     
  3. mastercookie

    mastercookie Existential Complex

    isnt it a bit ridiculous that we can terraform a whole planet (for god knows what reason), but cant dye a simple table?

    anyways, i checked how Nostalgic Greenery works, it adds new colours as frames, which works but im not sure about few things.
    first, does it work when object has more frames than 1?
    second, unfortunately it wont work with interactable objects (thats why theres special locking tool in the mod)

    i tried making an object so it changes its colour when clicked on, using very basic script:

    function onInteraction(args)
    if animator.animationState("switchState") == "1" then
    animator.setAnimationState("switchState", "2")

    elseif animator.animationState("switchState") == "2" then
    animator.setAnimationState("switchState", "3")

    and so on
    it kinda works, but doesnt save upon restarting the game and i cant figure out why (retainObjectParametersInItem doesnt work apparently)

    so i have to ask Charlatan can u make a tool (or a script rather) that simply switches states of an object on interaction?

    i tried doing that using NG_Special_DecorationDisplacer.lua as basis, but clearly am too dumb for this :(
     
    Charlatan likes this.
  4. Charlatan

    Charlatan Parsec Taste Tester

    In terms of frames you got two directions - x and y, meaning in the picture file, you can go downwards and sideways as much as you want.
    In fact, with enough work you can make repeated frames, as many frames as you wish as long as you define them correctly in the .frames file

    Your object needs to "remember" the state it should have, this is done by the "storage" variable.

    What you made only tells the object to change, in your current state of the game.
    You need to, for example, add this:

    Code:
    if storage.animationstate == "1" then
       animator.animationState("switchState") = "1"
    end
    if you want to do it more efficiently, without causing unnecessary load for the players, you do:
    Code:
      if storage.animstate == 1 then
         if animator.animationState("animstate") ~= "1" then
           animator.setAnimationState("animstate", "1")
       end
    end
    ^ this checks if the current state is up to date, and only changes it if it becomes different. The previous script keeps checking and updating the state every-time the script updates (you define how often it updates in the .object or .config file)

    In both cases, your script needs to know the variable first, so in the "INIT" part you should define it like:
    Code:
    if storage.animstate == nil then storage.animstate = 0
    The "nil" in this means, IF your object is completely new, and the player just placed it, it ofc has no variable like that, and if the script checks it it would return "nil" or, basically, nothing. So it's better to force it to be 0, or whatever value you want, else Starbound will have errors.

    That's wrong, I have objects which could both be colored and be interact-able and animated, I just dont have any such objects in the released mod yet.
    I only added the locking tool, so that people don't need to have all the flowers and things flashing up when trying to interact :/
     
    Last edited: Mar 13, 2017
    mastercookie likes this.
  5. mastercookie

    mastercookie Existential Complex

    but if interacting with an object opens up a menu, how do u interact with it? (lets say its a chair and u want to sit on it)
     
    Charlatan likes this.
  6. Charlatan

    Charlatan Parsec Taste Tester

    Hehe, good point, in that case there really is just one "interact" option - and I would just make my "special tool", or, the "Decoration Displacer" open up the color menu if you right-click.
    I might do so anyway.
     
  7. mastercookie

    mastercookie Existential Complex

    this brings us to where it all started :)
    could u pls make such tool (as a separate mod) so it can dye vanilla objects (with or without menu, id personally be okay with just clicking an object until the right colour shows up)

    to be honest i really want castlevania mod to be dyeable again
     
    Charlatan likes this.
  8. Charlatan

    Charlatan Parsec Taste Tester

    I'd love to make that, but you have to know first that this would mean to patch all the vanilla objects and be really hard to handle in terms of compatibility :0
     
    mastercookie likes this.
  9. bk3k

    bk3k Oxygen Tank

    I haven't really followed the conversation well, but IIRC if the old/pre-release objects had the colors in their PNG and frames files, would not the easiest solution be to replace these files with their pre-release equivalents? I have lots of old assets going back to Enraged Koala.

    I guess you'd need old animation files too? I haven't looked deep at this. They may have left the old files and just used <color> (which defaults to "default"). It might be as simple as changing the <color> tag then.

    You're correct that patching all the objects = a lot of work. But what wouldn't be is (if necessary) also replacing the objects themselves with their pre-release equivalents by lazily copying entire folders. Might create problems on a handful of things(techstations for one can be deleted from the batch). That does leave you with new objects to consider, but that list is far smaller.

    I suppose a more competent solution involves a script(Powershell, Python, etc) that makes a big difference file comparing old assets and new. So you could note any important object changes you need to avoid. Then a specific mass-string swap(easily accomplished in Notepad++ etc) to change
    Code:
    .png:default
    to
    Code:
    .png:<color>
    And I think such a string swap means you could probably start with the current vanilla's .object files(obviously a better idea) but the older build's .png, .frames, and maybe .animation files.

    If you did mod in such a nuclear manner, I'd also set "priority" in _metadata very low(maybe just one number higher than the game asset's own) to assure it loads first and everyone's patches get applied.

    And I wonder(I actually don't know) if it could be as simple as
    Code:
    local aim = activeItem.ownerAimPosition()
    world.objectQuery(aim, aim, {
      boundMode = "position",
      callScript = "animator.setGlobalTag",
      callScriptArgs = "color", "red"
    })
    
    etc.

    Some interesting questions are -
    1. Can you call this on objects that aren't expressly scripted? Because actually that's attempting to call an engine function rather than one which belongs to the object.
    2. Scripted or not, can you call the base engine's functions in this way? If so, will the function have the correct context(the specific object)?

    I don't have the source code so I guess trying it is the only way to know.

    edit:
    Another thing is you could (if necessary) make a script(Powershell, Python, etc) that creates patches for all the objects. Those patches could all be identical if they do this.
    Code:
    [
      [
        {
          "op": "test",
          "path": "/scripts",
          "inverse" : true
        },
        {
          "op": "add",
          "path": "/scripts",
          "value": []
        }
      ],
      [
        {
          "op": "add",
          "path": "/scripts/-",
          "value": ["/scripts/myScript.lua"]
        }
      ]
    ]
     
    Last edited: Mar 15, 2017
    mastercookie and Charlatan like this.
  10. Charlatan

    Charlatan Parsec Taste Tester

    Yes they did, and new animation files would be necessary anyway - the old ones were colorable without using .animation files at all.
    One could, however, do this without really going through making some kind of batch script - the majority of changes could actually be handled by Notepad++'s "find replace in all open files"

    Basically:
    .frames => Identical for all non-animated, regular decorations, except for the x,y dimensions of the image.
    .animation => can be 100% identical
    .lua => can be 100% identical
    Pre-1.0 could do this as stated above. The objects took <color> from a custom color table parameter in the .object file, instead of animation and lua.
    That, I wouldnt know. :/
    Unfortunately, you won't get away with this alone.
    Yes, setGlobalTag, in the way you taught it to me, works perfectly fine, but its practical effect is, in the end, exactly the same as simply using the typical combination of .frames .animation and .lua files.

    Here's why: the setGlobalTag will only work as long as the object is loaded. Port back to your ship, restart the game, and <color> will be back to "default", instead of "red"

    What I did:
    My paint gun sends an world.sendEntityMessage to the object, which sets storage.animstate to a number. in Update, the game sets the animator.setAnimationState to the message it received - with an IF NOT condition attached, so that the game doesnt keep executing it, and only does so in the next update after you recolored. Unless we find a way to make it work with the .object parameter as it did before 1.0, we *will* need a script to tell the object to accept its new color, else it will have the wrong colors for other players and when you restart.

    Alternative Solution: Using setGlobalTag they way you told me earlier, and using custom parameter in the .object, but that seems to still need scripting for 1. config.getParameter 2. animator.setGlobalTag

    My scripting is still kinda clunky, and I only just started improving to use less if-not-else chains and less variables, reducing them down to arrays, tables, etc. What I currently wonder about is, if the <color> tag in the .object file can't simply somehow be replaced to point simply at said custom parameter. I'll try that soon.


    EDIT:
    Interestingly, simply setting the custom parameter "color" in my .object files works - at the first placement.
    object.setConfigParameter(String key, Json value) won't do a thing, though.

    EDIT2:
    I finally found the most simple solution and the only thing we're missing: A way to force the game to "reload" an object.

    Simply setting "color" as a custom parameter and changing it requires only an extremely simple script - but the game won't reload the object with the right color unless you restart / warp away.
     
    Last edited: Mar 16, 2017
    mastercookie likes this.
  11. bk3k

    bk3k Oxygen Tank

    One thing I do know - I am going to make a series of object-modifying stations. Probably to start with they'd be specific, eventually cutting the number down to have many options. Thanks to the work I did for ISE4, I have a firmer idea of how to do this(and an established code basis). I can take an object, and spit out an object with altered parameters. Not as convenient as a paint tool, but almost anything is possible this way. Because that object has different defaults, it won't revert. It also won't stack with the unaltered variety.

    Have you tried combining what you did with config.setParameter? I don't know if that would have any better lasting effect.

    Also have you ever used
    Code:
    "retainObjectParametersInItem" : true,
    Try doing both. I think it would retain color. Even after removal/replacement.
     
    mastercookie and Charlatan like this.
  12. Charlatan

    Charlatan Parsec Taste Tester

    Yes, same result as what I currently have though. All it does is take the Parameter from the object, instead of storing it in it's script.

    Same thing, the only thing that changes is, if you pick the object up then place it as an item again, it'll use the new color. The problem remains, that the object needs to be re-loaded somehow for the new color to show.

    I wanted to try make a script which destroys the object and immediately uses world.placeObject with the JSON parameter set to the new color.
    However, that doesn't work simultaneously, between the object.smash and world.placeObject I need a delay, but since I dont know how coroutines work, I can't get util.wait to work.

    Code:
    function init()
        message.setHandler("swapcolor", swapcolor)
    end
    
    function update()
    end
    
    function swapcolor()
    
        local objectName= world.entityName(entity.id())
        local objectPosition= world.entityPosition(entity.id())
      
        local pos1 = (objectPosition[1]+2)
        local pos2 = (objectPosition[2])
      
        world.breakObject(entity.id(), true)
        world.placeObject(objectName, {pos1,pos2}, 1, { color = "pink" } )
    end
    Using this on one of the Crystal Rocks I made, creates a new rock of the same type 2 tiles beside it, with the correct color.

    But I can't use it on the same position, for that I need to have a delay. Preferably without adding a timer in the update() function.... that way, we'd only be back at every object constantly running script lines when updating.
     
    Last edited: Mar 16, 2017
    mastercookie likes this.
  13. bk3k

    bk3k Oxygen Tank

    I was crafting code that would give you the delay... until I realized this was code used in the object. I don't think you can hope to run code from an object that doesn't exist anymore. I realized that when I saw entity.id()

    If it was ran from the tool... yes you could remove an object and replace it next update tic, or after a short timer. Let me think about how you get the name of the object using world functions. The rest I know what to do.
     
    mastercookie and Charlatan like this.
  14. Charlatan

    Charlatan Parsec Taste Tester

    Thanks, but neither of us should bother with this tbh. - This way, we'll only be back to constant updating, this time simply with a timer.

    Any way to "reload" the object would be the maximum efficient solution - even the script would only have to run one instant when the message is received.
     
    mastercookie likes this.
  15. bk3k

    bk3k Oxygen Tank

    Try a combination of everything.
    1. config.setParameter()
    2. "retainObjectParametersInItem" : true,
    3. animator.setAnimationState()
    That last step should make it re-load. I think.

    You can even use
    Code:
    local state = animator.animationState("objectState")
      --replace "objectState" with whatever is valid for that particular object
    animator.setAnimationState("objectState", state, true)
    
    The purpose there is to get the current state, and then tell it to "change" to that same state aka reload it.

    edit:
    That might only work with objects that have defined parts and states for those parts. Probably only with .animation files...
     
    mastercookie and Charlatan like this.
  16. Charlatan

    Charlatan Parsec Taste Tester

    Just tested and can confirm.
    Can also confirm that. My function successfully started a timer, but that's it, any function that should follow is gone, because the script vanishes together with the object.

    With that, the method to truly emulate the pre1.0 coloring is impossible unless the aforementioned method to "reload" an object is found.
    Instead of going that way, I'll go and optimize my current solution here:


    EDIT:

    With my own reading of the guide on the LUA homepage and the stuff you @bk3k and @Errors4l posted in the mod section, I think I managed to make my first actually efficient .lua script. The old one was about 6 times as long :oops:

    Code:
    colorTable = {"default", "yellow", "orange", "red", "pink", "violet", "blue", "sky", "green", "forest", "copper", "black", "white" }
    
    function init()  
       if storage.color == nil then storage.color = colorTable[1] end
       message.setHandler("colorizeObject", function(_, _, colorIndex)
         storage.color = colorTable[colorIndex]
      end)
    end
    
    function update()
       if storage.color ~= nil and storage.color ~= animator.animationState("color") then
         animator.setAnimationState("color", storage.color)
         animator.playSound("switch")
       end
    end
    
     
    Last edited: Mar 16, 2017
    mastercookie and bk3k like this.
  17. mastercookie

    mastercookie Existential Complex

    bk3k likes this.
  18. bk3k

    bk3k Oxygen Tank

    mastercookie likes this.
  19. mastercookie

    mastercookie Existential Complex

    nah... u see, i already painted the gun. this one > bubblegun.png
     
  20. Yanazake

    Yanazake Space Kumquat

    So close, yet so far~
     

Share This Page