Author Topic: [Lib Day 2.0] Mod Framework  (Read 874 times)

Offline The Bigfoot

[Lib Day 2.0] Mod Framework
« on: March 15, 2016, 08:29:48 pm »
Mod Framework for LD

This is in constant development (as is the other one) and is aimed at helping mods interact with the game in new ways whilst maintaining compatibility between them. It will try to minimize any difference to the actual game (except in usability and bug-fixes) and should not be noticeable in most cases.

If there is something you would like to see in it, let me know and I will do my best to add it.

To Modders: You are welcome to use any and all code, but PLEASE direct people to download from the live link and if you add code to your mod separately be aware it may cause conflict problems, as this will be frequently patched and keeping an old version in a mod will likely conflict with other mods/newer versions of this.

Backwards compatibility will be maintained as best as able
Drop the file into the Sunrider\game folder

Current Features (2)

Modifiable Choices at start

Choices can now be added in by mods and inserted wherever you want in the choice screen.
You can hide choices at will and declare they must be picked or not, either by variable or more complex functions.
The screen auto-adjusts its size to fit the number of choices


The Syntax is different for a title block and a choice block. You can use any number of choices but they must share the same variable they change to update correctly. You must define your variable name BEFORE you define your choice blocks.

Title Block:

Code: [Select]
[1,"Title","tooltip","eval string"]
The 1 tells the game it is a title block
Title and tooltip are obvious
An eval string is a string of conditions that must return eval(string) == True to show the block
It can be as simple as "True" to always show or it can be "previous_choice_variable == True" or even a function

Choice Block:
Code: [Select]
[2,"Title", "tooltip, "eval string", ("variablename",Value)]
This is similar to the title block but it starts with a 2 and has an extra field
The extra field tells the block what variable to associate with, the variable name should be a string and the value can be anything.

Inserting into the list:

To actually implement them in the list you need to do:

Code: [Select]
setoptions.append([ code ])
For each of the blocks.


To make an answer compulsory then you can add the variable name into Optionsvars as a string

Code: [Select]
If one of the variables listed in Optionsvars == None, the confirm button will not show.

If you have a choice that depends on another variable you can add a function to check the validity of the choice into Optionfuncs:

Code: [Select]
init 2 python:   
    def validate_spire():
        global his_pactspire, his_capturetraffickers
        Confirm = True
        if his_pactspire == False and his_capturetraffickers == None:
            Confirm = False
        return Confirm


This returns True if the combination is valid, and False if it isn't. This works the same way as the Optionsvars but you should put the function in without quotes. If any function returns False, the confirm button will not show.

Inserting Choices:

As the choices are chronologically organized, there is a function that lets you insert a choice wherever you want. To do this, put all your blocks in one list and use the insert_test function like:

Code: [Select]
init 2 python:   
    VariableName = None # define your variable name
    insert_test = ([Title],[Choice1],[Choice2])
    options_insert(Block Title,insert_test)

This will insert your choices before the Block you called with the title.

Example Code:

Basic Options
(From modified game file)

Code: [Select]
init 2 python
    setoptions.append([1,"Flag","After the fall of Cera, which flag did you suggest flying?","True"])

    setoptions.append([2,"Cera","You told Ava the Sunrider would always fly Cera's flag.","True",("his_ceraflag",True)])

    setoptions.append([2,"Pirate","You told Ava being a pirate ship wouldn't be bad.","True",("his_ceraflag",False)])

Here, the first block is the title, hence the 1 at the start. The showing text is Flag and the Tooltip is "After the fall of Cera, which flag did you suggest flying?". Because it has an eval string of "True" it always shows.

The second and third blocks are choice blocks that both modify his_ceraflag setting it to either True or False. They are again both visible because of "True"

Because the variable is put in Optionsvars then you cannot confirm without picking a choice

To make the choice only appear if another option has been chosen, like if you saved pirates, the Eval strings should be "Variable == x" where x is the defined choice.

Possible Options code:
(From altered game code)

Code: [Select]
init 2 python:
    setoptions.append([1,"Beach Time 1","Who did you talk with at the beach?","True"])

    setoptions.append([2,"Asaga","","his_beach2 != 1 and his_beach3 != 1",("his_beach1",1)])
    setoptions.append([2,"Chigara","","his_beach2 != 2 and his_beach3 != 2",("his_beach1",2)])
    setoptions.append([2,"Ava","","his_beach2 != 3 and his_beach3 != 3",("his_beach1",3)])
    setoptions.append([2,"Icari and Kryska","","his_beach2!= 4 and his_beach3!= 4",("his_beach1",4)])
    setoptions.append([2,"Claude","","his_beach2 != 5 and his_beach3 != 5",("his_beach1",5)])
    setoptions.append([2,"Sola","","his_beach2 != 6 and his_beach3 != 6",("his_beach1",6)])

    def validate_beach_decision():
        if his_beach1 == his_beach2 or his_beach1 == his_beach3 or his_beach2 == his_beach3:
            return False
        return True

This code is a bit more complex, it uses the same code as the previous, but the eval string in the choices, checks that the same pick hasn't been made in the other 2 selections, it then hides that choice option.

This works better than the original code, and validate_beach_decision is no longer needed but it is a good example of using a function to hide the confirm button if there is a problem. The function returns false if any choices have been made twice.

Insert Choice Example:

Code: [Select]
init 2 python:
    beardtest = None
    insert_test = ([1, "Do you like beards?","The Answer is always YES","True"],[2, "Yes", "Yes", "True", ("beardtest",True)],[2, "No", "Liar", "True", ("beardtest",True)])
    options_insert("Ava's sacrifice",insert_test)

This code inserts a Title and 2choice set into the choice list.
It appears above Ava's Sacrifice because the options_insert looks for a matching string in title blocks and when it finds it, puts it one slot ahead.

This means it is easy to slot a choice into the list wherever you want.

Dynamic Ship Map Buttons:

Buttons can now be added to the ship map by mods. This works very similarly to MoA framework's buttons but uses a different list and has extra features:

You can use buttons to go either to a label (like regular character buttons) or you can now set buttons to trigger functions (like research and the store)
Button graphics can change dynamically according to variables (for example Ava's eye patch only appears if legion_destroyed == True)
You can add your own buttons using custom images and they will display in any of the ship locations you pick.

Name and location codes
The name code can be:
"ava" for Ava
"asa" for Asaga
"chi" for Chigara
"ica" for Icari
"cla" for Claude
"sol" for Sola
"kry" for Kryska
"pro" for Progress
"gal" for Galaxy Map

The location code can be:


 To display a button with a label jump, you add a list to talk_buttons.
Code: [Select]
talk_buttons.append(["eval string", "name code", "location", "label name"])
The eval string tells the game if it should display the button. If it returns True then it is displayed. This CAN overwrite story defined labels so unless there is good reason, you should always include Btest("name code") in the eval string. This returns False if that character's location is not None and prevents the button being activated.

If all eval conditions are True, then the named button will show up at the location and clicking it will jump you to the named label.

The talk_buttons should only be added to in init, because this means if the mod is removed, talk_buttons will not remember the new button.

In the label you jump to, you MUST set "namecode"_location = None and "namecode"_event = None or it will still show on the ship map when you return.


Code: [Select]
init 2 python:
    talk_buttons.append([("Btest("asa") and seen_mod_label == False", "asa", "lab", "mod_asagaquest"])

label mod_asagaquest:
    $ asa_location == None
    $ asa_event == None
    asa "This is an awesome quest."
    jump Wherever
This will make asaga appear in the lab if seen_mod_label is False. When she is clicked, you jump to the mod_asagaquest label and her _location and _event variables are reset.

Event Button:
An event button calls a function or triggers an effect, rather than jumping to a label. The syntax is broadly the same, but you don't set the event.
The event is fixed with the button and to create a new event, you need to make a new button, which will be covered in a moment.

To display the event button you do:

Code: [Select]
event_buttons.append(["eval string", "name", "location"]]
The Eval string and the location is the same as previously.
Name is one you define when you make the button, or it can be "res" for research and "cal" for store to move them to your location.


Both these new buttons work by setting "namecode"_location and "namecode"_event to make the button appear and know what to do with it. You can always manually do it, in a label by asa_location = "bridge" if you know you want it immediately after a certain scene, the lists just place it automatically when you tell them to.

Creating a new Button:
It is now possible to make your own button to display on the ship map, by defining it in a new list. After it's been defined then it will show up at the location you want. PLEASE only define it in an init block as otherwise if a mod is removed it will cause errors.

To make your button, you need to add a list to buttonlist or eventlist:
Code: [Select]
# Talk Button:
buttonlist.append(["name code","unhovered image", "hovered image"])

# Event button:
eventlist.append(["name code", "unovered image", "hovered image", function])
The name code is identical to seen previously, it is what the map looks for when trying to display a button. If it thinks it should display it, then it places the unhovered image button which transforms when it is moused over.
For event buttons, the function is triggered when it is clicked rather than the label jump. The function can have no non-default arguments.

If you want to make a button change its displayed image dynamically, then instead of a string containing the file name, you should put a function. It will automatically be run and the result will be used.

To make the image glow when moused over, the hovered image should be tr_hoverglow(hoverimage). Other transforms will also be shown if entered.

Example useing pre-defined buttons:

Basic: Asaga
Code: [Select]
init 2 python:

The hoveredimage is a transform whilst the image is the file in sunrider/game/UI (which is actually hidden inside an archive)

Dynamic: Ava, Change sprite if Legion is destroyed

Code: [Select]
init 2 python:

    def ava_img():
        if legion_destroyed:
            return "UI/ava_button_eyepatch.png"
        return "UI/ava_button.png"
    def ava_img2():
        if legion_destroyed:
            return tr_hoverglow("UI/ava_button_eyepatch.png")
        return tr_hoverglow("UI/ava_button.png")


The functions return the image string depending on what the legion_destroyed variable is, thus changing the button image

Upcoming Features:

Will be filled (but as I haven't actually had a chance to finish 2.0 yet, will take some time)
If you have any requests then please post them and I will see what I can do.

If there are any problems with the code, then please let me know and I will do my best to fix it.
« Last Edit: March 17, 2016, 09:28:25 pm by The Bigfoot »
I make spaceships! -
Massive Modding tutorial here:
New Award: John Titor [Sep 17, 2015, 10:16:07 PM]:   BigFoot is the official Evil Genius fro mteh forum

Offline Douman

Re: [Lib Day 2.0] Mod Framework
« Reply #1 on: March 18, 2016, 05:22:47 am »
Store it on github for someone else to help you out?