[PRPL] Custom module/unit settings manager

Started by kajacx, December 01, 2017, 07:06:54 PM

Previous topic - Next topic

kajacx

A long time ago, in a forum post far far away, there was a discussion about custom modules having settable options like normal modules have to (for example, turing them on/off, setting target priority, etc.). Since that is a good idea, I have made some PRPL that will allow mapmakers to register some customizable options for their custom ship modules or units with just a "few" lines of code.

How it works: (for the end player)

  • Player can show/or hide the custom options. When shown, custom options will show next to a ship/unit that has custom options set when approaching it with mouse.
  • The player can left click on a unit/ship or press C when hovering over it to "lock" the selection on that unit/ship.
  • With the selection locked, the player can edit the settings.
  • Right click will unselect the ship/unit. A visual indicator is shown on the selected unit/ship.

Screenshots



[close]

How to set it up: (for the mapmakers)
Once you get it up and running, adding new options is very easy. Here is a full list:

  • Download the CustomModuleManager.prpl and ShowButton.prpl scriptrs and place each of them in a separate PRPL Core.
  • Download the TextBind.prpl script and just put it in your scripts folder, it will be added automaticly by another script.
  • Download the square64.png white square image and put it into "Custom0pp" slot, and targeting.png into "Custom0_256" slot.
  • (The image slot names are always set in an input variable, so you can easily change them to another slot if the default ones are occupied.)
  • Add the 3 functions from CustomModuleManager.api.prpl to your script. They are defined at the end of CustomModuleManager.prpl as well for convenience.
  • Register your ship/unit ID and custom settings in a once block at the start of your scripts (examples later).
  • Read the current index of the value of a setting for your ship/unit (examples later). This is the value settable by player.
  • Optional: override the setting value from code (examples later).

So, without furder ado, let's see some examples of how to register, read and manually set your settings:

Example 1: Simple yes/no option


once
    #create the option and color lists
    CreateList ->options
    CreateList ->colors

    <-options "On" AppendToList #Index 0
    <-colors 0 AppendToList #R
    <-colors 255 AppendToList #G
    <-colors 0 AppendToList #B

    <-options "Off" AppendToList #Index 1
    <-colors 255 AppendToList #R
    <-colors 0 AppendToList #G
    <-colors 0 AppendToList #B
   
    Self "Active" <-options <-colors @CmmApiRegisterModuleAndShip
endonce

"Is active:" @IsActive Trace2
28 Delay

:IsActive
    Self "Active" @CmmApiGetShipModuleOption not
    # returns index (0="On", 1="Off") so we need to negate
   
# ---- The 3 functions from the API ----
:CmmApiSetShipModuleOption # [ sid name int - ]
    ->cassmo_value
    concat "ModuleOption" swap concat #varname
    -1 "IsCustomModuleManager" 1 GetCoresWithVar 0 do max loop asint swap #uid varname
    "CustomModuleManager.prpl" swap #uid scriptName varname
    <-cassmo_value SetScriptVar # -

:CmmApiGetShipModuleOption # [ sid name - int ]
    concat "ModuleOption" swap concat #varname
    -1 "IsCustomModuleManager" 1 GetCoresWithVar 0 do max loop asint swap #uid varname
    "CustomModuleManager.prpl" swap #uid scriptName varname
    GetScriptVar #selected option

:CmmApiRegisterModuleAndShip # [ sid name list list - ]
    ->carmas_colors
    ->carmas_options
    ->carmas_name
    ->carmas_sid
   
    "IsCustomModuleManager" 1 GetCoresWithVar 0 do ->carmas_manager loop
   
    #set the options and colors for that module
    <-carmas_manager "CustomModuleManager.prpl" "ModuleOptions" <-carmas_name concat <-carmas_options SetScriptVar
    <-carmas_manager "CustomModuleManager.prpl" "ModuleColors" <-carmas_name concat <-carmas_colors SetScriptVar
   
    #create a module list for this ship if not yet set
    <-carmas_manager "CustomModuleManager.prpl" "ModuleShip" <-carmas_sid concat GetScriptVar ->carmas_moduleList
    <-carmas_moduleList GetType "LIST" neq if
        CreateList ->carmas_moduleList
        <-carmas_manager "CustomModuleManager.prpl" "ModuleShip" <-carmas_sid concat <-carmas_moduleList SetScriptVar
    endif
   
    #register the module to this ship, if not already registered
    0 #contains
    <-carmas_moduleList GetListCount 0 do
        <-carmas_moduleList[I] <-carmas_name eq or
    loop not if
        <-carmas_moduleList <-carmas_name AppendToList
    endif
   
    #initilize the option to 0
    <-carmas_manager "CustomModuleManager.prpl" "ModuleOption" <-carmas_sid concat <-carmas_name concat 0 SetScriptVar


As you can see, you need 4 things to register a custom option setting: the id of the ship/unit, the name of the setting, the list of the values and the list of colors for those values.

For reading the value, just specify the ID of the unit you wish to read and the name of the setting. I recommend writing a more friendly read function, like in the example. The default read function returns the index of the selected option. The first option (with index 0) is always default.
[close]

Example 2: More settings, manual set


once
    #register fire button
    CreateList ->fireOptions
    CreateList ->fireColors

    <-fireOptions "Fire" AppendToList
    <-fireColors 255 AppendToList #R
    <-fireColors 128 AppendToList #G
    <-fireColors 0 AppendToList #B

    <-fireOptions "Fire" AppendToList #the text can be the same, it's the index that matters
    <-fireColors 255 AppendToList #R
    <-fireColors 128 AppendToList #G
    <-fireColors 0 AppendToList #B
   
    Self "Action" <-fireOptions <-fireColors @CmmApiRegisterModuleAndShip
   
    #register target
    CreateList ->targetOptions
    CreateList ->targetColors

    <-targetOptions "Particles" AppendToList
    <-targetColors 255 AppendToList #R
    <-targetColors 0 AppendToList #G
    <-targetColors 64 AppendToList #B

    <-targetOptions "Ships" AppendToList
    <-targetColors 255 AppendToList #R
    <-targetColors 64 AppendToList #G
    <-targetColors 0 AppendToList #B
   
    <-targetOptions "Units" AppendToList
    <-targetColors 196 AppendToList #R
    <-targetColors 0 AppendToList #G
    <-targetColors 16 AppendToList #B
   
    Self "Target" <-targetOptions <-targetColors @CmmApiRegisterModuleAndShip
endonce

@IsFiring if
    "Firing at" @GetTarget Trace2
endif

:IsFiring
    Self "Action" @CmmApiGetShipModuleOption ->firing
    Self "Action" 0 @CmmApiSetShipModuleOption
    <-firing

:GetTarget
    <-targetOptions[Self "Target" @CmmApiGetShipModuleOption]

#I ommited the 3 API functions in other to save space, it's the same code anyway


Here we see 2 settings on one unit (see the 3rd screenshot). The idea is to first select a target, and then click the "Fire" button. Once the "Action" setting is set to the second "Fire" option (index 1), the script will fire at the "Target" (represented by just tracing "Firing at X"). This is a neat trict to create a clickable button rather than a settable option.
[close]

Example 3: A custom module


#showing only relevant portions of the code
once
    # ...

    CreateList ->convertorOptions
    CreateList ->convertorColors
   
    <-convertorOptions "Nearest" AppendToList
    <-convertorColors 0 AppendToList
    <-convertorColors 255 AppendToList
    <-convertorColors 0 AppendToList
   
    <-convertorOptions "Farest" AppendToList
    <-convertorColors 0 AppendToList
    <-convertorColors 255 AppendToList
    <-convertorColors 0 AppendToList
   
    <-convertorOptions "Random" AppendToList
    <-convertorColors 255 AppendToList
    <-convertorColors 196 AppendToList
    <-convertorColors 0 AppendToList
   
    <-convertorOptions "Off" AppendToList
    <-convertorColors 255 AppendToList
    <-convertorColors 0 AppendToList
    <-convertorColors 0 AppendToList
   
    <-sid "Convertor" <-convertorOptions <-convertorColors @CmmApiRegisterModuleAndShip
endonce

    # ...
    @GetConfig 3 neq and (<-cooldown 0 lte) if # is not disabled and cooldown ready
    # ...
            @Fire if
                 0 ->energy # reset energy
            endif
    # ...
    endif
    # ...


:Fire # [ - bool ]
    @SelectParticle ->pid
    <-pid -1 eq if #particle not found
        0 return
    endif
   
    #delete old particle
    <-pid GetParticlePosition ->py ->px
    <-pid GetParticleMotion ->my ->mx
    <-pid 1 DestroyParticle
   
    #create new particle
    <-px <-py 0 0 0 <-Enemy CreateParticle ->pid
    <-pid <-mx neg <-my neg SetParticleMotion
   
    1 #return success

:SelectParticle
    @GetConfig ->config
    CurrentCoords <-Range 0 <-Enemy not GetParticlesInRange ->particles
    <-particles GetListCount eq0 if
        -1 return #nothing found
    endif
   
    <-config 2 eq if #random
        <-particles[0 <-particles GetListCount RandInt] return #return random particle
    endif
   
    <-config 0 eq if #nearest
        1000000.0 ->distance
        -1 ->pid
        <-particles GetListCount 0 do
            CurrentPixelCoords <-particles[I] GetParticlePosition Distance ->dist
            <-dist <-distance lt if
                <-dist ->distance
                <-particles[I] ->pid
            endif
        loop
        <-pid return
    endif
   
    <-config 1 eq if #farest
        -1.0 ->distance
        -1 ->pid
        <-particles GetListCount 0 do
            CurrentPixelCoords <-particles[I] GetParticlePosition Distance ->dist
            <-dist <-distance gt if
                <-dist ->distance
                <-particles[I] ->pid
            endif
        loop
        <-pid return
    endif
   
    "Invalid config:" <-config Trace2
    -1 #return
   
:GetConfig
    <-sid "Convertor" @CmmApiGetShipModuleOption


Here I have a custom module that converts enemy particles to friendly. The "Fire" function will attempt to find a particle and convert it, returning true if it found and converted a particle, false otherwise. Note that the function is only called only if the module isn't disabled to begin with.

Also note how the custom module passes the ID of the ship into the registry and when reading the value, not the ID of the custom module itself.
[close]

By now you should be able to add some custom settings to your modules or units as well. Here are some final remarks:

  • The association "Setting name"-"values/colors" is global, so you can't have "Mode:On/Off" on one unit and "Mode:Full/Partial/Off" on another. Rename the option name in such case, for example " Mode " (with spaces) will do, and it will be undisguisable by the player.
  • The individual values of the settings are local to the ships/units they were registered to. For example, one units can have "Mode:On" and another "Mode:Off" without a problem, but both must have the same pool of avaliable values for the setting "Mode" (in this case, On/Off).
  • The setting for custom modules should be registered to the ship the custom module is on. If you want to have 2 custom modules with separate settings, both modules need to register under a different setting name (for example "Convertor1", "Convertor2", "Convertor3" or "Convertor top", "Convertor mid", "Convertor bot"). I recommend setting a different "position" input variable in the Master script (zz_adder) for the different modules.
  • Both CustomModuleManager.prpl and ShowButton.prpl should be alone on a separate core, since both of them will move the core they are in around somewhere.
  • A lot of the variables or functions are named "Ship" this or "Module" that. This is because this was originally for custom modules only, but at the very end I decided to include units as well and was lazy to rename everything, so don't worry, everything works with units just fine.

That's about it, I'm looking forward to some maps with configurable units on the Exchange.
Why do work yourself, when you can write a program that will do the work for you.