Show Posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.


Topics - The Bigfoot

Pages: [1] 2
1
Mods / [LibDay:2.02] First Arrival and Mask of Arcadius Port
« on: April 19, 2016, 07:47:04 pm »
First Arrival and Mask of Arcadius
in
Liberation Day


This is a total port of First Arrival and Mask of Arcadius into LibDay, adding 21 battles and letting you play through the entirety of the three games seamlessly in a single playthrough, or start at the begining of any of the games.

The mod uses mostly the original images for the FA/MoA parts, the sprites are the same for example (as it is far beyond me to draw beach sprites) but wherever it could be done, the upgraded backgrounds are used.


Balance:

There is a difference in balance, especially with the new way CMD works in battles and stacking buffs. The Sunrider also starts with an extra two missiles and as money is not used to upgrade, then ships will upgrade slightly faster (~10%) because intel is not affected by repair costs.

To balance that, repair costs in LD are much steeper than in FA/MoA and it is possible you will go into negitive funds, especially in harder dificaultys. This means it's slightly harder to get consumables like rockets. Later in the game it is expected you will have too much money to spend, because again it is not being used up in research.


Modding:

It is easy to incorperate MoA story mods into the game with a little rewireing. The MoA missions set missionMoA*_completed and starting at any start point, sets these to True or False as needed.

You can set choices for the MoA questionire in the same way as the LD questionare but useing setoptions_MoA instead.

There are MoA versions of all ships as some LD ships are tougher than MoA ships, the rest are synonims and can be used interchangeably. The new versions have a _MoA suffix.


Bonus:

You can choose either to reset the Sunrider and upgrades for LibDay or you can cheat and keep the upgrades/ships you used in the previous parts by changeing line 18 of MoA in LD.rpy to False

Installation:
Extract the MoA folder from Here

From the mask of arcadius game folder, drop these folders into the Liberation Day/Game/MoA folder:
  • Background
  • Battle UI
  • cg
  • Character
  • gameplay
  • Map
  • Menu
  • Music
  • sound
  • Space
  • Transitions

Unfortunatly they are too big to googledrive

Edit: Bugs Fixed (22-4-16)

2
Mods / [LibDay:2.02] Movement and Wreckage mods
« on: March 28, 2016, 11:26:35 am »
Movement Directions Mod
and
Ships Leave Wreckage Mod

Movement Directions Mod:

This mod introduces the revolutionary idea of *Turning*
When you move the Sunrider if it moves "backwards" then the label changes!
This same mysterious magic happens when a weapon fires.

Ships leave Wreckage Mod:

All ships (bar ryders and specials) create cover that is colour-coded to its faction (Player, Union, Alliance, Pirate, PACT).
Depending on the ship, the cover has a set chance to block, a variable HP and when made from large ships, damages ships when it is destroyed by weapons. It also degrades over time with the image changing to match.

Cover Stats:

Cover is different depending on the ship but categories are roughly similar. Cover HP is currently 10% of the Max hp.

Frigate: Timer: 3, Chance to block: 10%
Cruiser: Timer: 4-6, Chance to block: 15%
Destroyer: Timer: 4, Chance to block: 13%, Detonates if destroyed (200dmg)
Station: Timer: 6-12, Chance to block: 40-60%, Detonates if destroyed. (200dmg)
Carrier: Timer: 5, Chance to block: 30%
Battleship: Timer: 7, Chance to block: 40%, Detonates if destroyed. (200dmg)
Assault Carrier: Timer : 5, Chance to block: 40%, Detonates if destroyed. (200dmg)

Cover starts off looking like the far left, over time it fades until it is destroyed.

Installation:
To install, just drop the files into the LibDay game folder and put the ModFramework file from https://drive.google.com/file/d/0B0D6n1uinVVzelctTDUwM3VPR2c/view in there as well.

3
Mods / [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
Installation:
Drop the file into the Sunrider\game folder
https://drive.google.com/open?id=0B0D6n1uinVVzelctTDUwM3VPR2c

Current Features (2)

Modifiable Choices at start

Spoiler
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

Use:

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.

Validating:

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

Code: [Select]
Optionsvars.append("variablename")
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

    Optionfuncs.append(validate_spire)

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)])

    Optionsvars.append("his_ceraflag")
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

    Optionsfuncs.append(validate_beach_decision)
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:

Spoiler
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:
"captainsloft"
"hallway"
"sickbay"
"messhall"
"bridge"
"engineering"
"lab"
"hangar"

Use:
Basic:

 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.

Example:

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.

Alternatively:

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:
    buttonlist.append(["asa","UI/asa_button.png",tr_hoverglow("UI/asa_button.png")])

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")

    buttonlist.append(["ava",ava_img,ava_img2])

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.

4
Mods / [7.2] Shambler Hunting!
« on: January 14, 2016, 05:15:22 pm »
Woolly Shambler Hunting!
Moon Recon


This is a mod to provide catharsis to the forum Mafia players but other people will be able to play and enjoy it, it does have a 'sensible' storyline, but be aware it is quite tongue-in-cheek.

None of it is meant to be taken seriously..

Features:

This mod mod is set before/shortly after Ongess and is balanced for that time, it would be worth doing any sidemisions first.

A unique mission, fight in the atmosphere and above it... at the same time
Grumpy dialogue about the cold
Special upgrades for every ship depending on how well you do
A morally dubious (but very cathartic) hidden weapon
Alternate Shambler sprite for a Christmas feel

Hints:

If the mission is hard, buy an Alliance Cruiser or two, the extra flak will help.
How to get the weapon
To get the weapon, you need to keep the shamblers and then within 3-6 turns in battles an event will happen. You need to keep some shamblers until you get the event or it won't fire.

Picture Time!

Pictures
Actually these would all be spoilers so you just have to make do with a smiley face.
:D

Installation:
Unpack the Zip file into the sunrider/game folder
Download the ModFramework.rpy and put it in the sunrider/game folder
https://drive.google.com/open?id=0B0D6n1uinVVzTVJ0YzdfM2ZrU2M

It will now trigger when you talk to Kryska after the battle for Farport.

Important things:
This mod is not currently compatible with Astral's mod (but I will work on that at some point)

SPOILER: How the Weapon works:
The Shambler rocket is immune to flak but also doesn't reduce it. When it hits the ship it applies a debuff called Rampaging Shambler, The % is the chance that it will be caught next turn. Every turn it either applies a debuff or deals heavy damage to the host ship.

When shot at Ryders it disables them or outright kills them.

Does not work against Legion or Nightmares. The ammo can be replaced from the Store.

To change the sprite, open the Shamble folder and change the names around

Although I have run this mod 20+ times, given the huge amount of custom code, let me know if something goes wrong so I can fix it

When you try and shoot the REDACTED they don't glow, this was previously intentional, but now I don't have the faintest idea how it was done. If someone can work it out, please let me know, its driving me mad!

The info screen in the battle map now shows damage AFTER buff rather than damage without buffs

Thanks to the Cheathopper for bug-testing the mod!

Quote out of context:

aw nuts. this is worse than the time I got turned into slippers or a bathrobe... WoollyShambler

Yes it is.

Updated 27/1/16: Minor bug fixes

5
Mods / [7.2] Mod Framework/Tools
« on: December 14, 2015, 11:10:23 am »
Mod Framework RPY
For Modders

This is a file intended as an enabler for mods and adds new ways to interact with the game and new functionality whilst maintaining compatibility. It is in a constant state of development but backwards compatibility should be total unless a large bug needs to be patched out.

Anyone is free to use it as they want, but please link either to the googledrive link or here so the most updated version is always available so mods do not need to be constantly updated as it changes.



Features
Function Points
These are points you can script across the entire game. These are triggered at certain events and can combo fantastically. You could do some of these things in battles you code specially, but this lets you do much more, and widens their scope across the entire game.

Weapon Functions
Use:

Allows functions to trigger on weapon fire, These can be set to trigger on hit, miss, either and can modify damage and accuracy. This lets you modify damage and accuracy or add extra effects like modifiers, cumulative damage or pretty much anything you can imagine.

If you wanted to make it only trigger on a certain ship's name you can put a function to check target.name and if it's not a valid target you can set accuracy to 0 to make it auto-miss, or do something completely different.

When the functions are put in weapon.functions they trigger only for that weapon, but if put in ship.weapon_functions they trigger for every weapon that ship uses.

Example Code:

Example
Code: [Select]
# Weapon functions are a tuple of (check,function)
# check values are: 0 - Miss only, 1 - Hit only, 2 - Hit or miss, 3 - Modify damage, 4 - Modify accuracy
# With 3 and 4, the output is an integer that INCREASES the final amount by that much

# A functions arguments must be:
# (weapon, user, target, total_damage, hit_count, accuracy, damageid)

# It is best to attach a function to the weapon or ship base class and use a variable to turn the function on, otherwise it can get removed/overwritten easily.

# Make Liberty laser a targeting laser:
init 20 python:
    def Targetlaser(weapon, user, target, total_damage, hit_count, accuracy, damageid):
        if targetenabled == True:
            if target.modifiers['evasion'][0] > -20:
                target.modifiers['evasion'] = [-20,1]
        return

# Attach Targetlaser to LibertyLaser and tell it to trigger when it hits the target   
    LibertyLaser.functions.append(1,Targetlaser)


#Make the Paladin have increased damage when it attacks an enemy ship if its next to one of the Sunrider's crew. (Code from simplified Shambler hunting reward)

#adjacent_to_core is covered later in this post

    def loyaltydmg(weapon, user, target, total_damage, hit_count, accuracy, damageid):
        return_variable = 0       
        if kry_gift:
            if paladin.adjacent_to_core():
                damage = total_damage*0.1

        return return_variable # If the function just passes, it adds 0 to the damage, otherwise 10% of initial damage

    Paladin.weapon_funcs.append((3,loyaltydmg))

    # You could also set an effect to trigger on target death by something like this:
    def function(stuff):
        target.deathfuncs.append((0 or damageid, function))
        return

Multi-turn effects
This is code from the Shambler Hunting mod, used to work the secret weapon. It triggers a function on hit and sets a flag on the target. We then use gated_player_actions to check ships for that flag on the start of the player turn. If its found, it runs the function to set a new effect and check for shambler survival.

Code: [Select]
init 20 python:

# Function to fire with the weapon, it calls another function

    def inflictshamblers(self, parent, target, damage, hits, accuracy, damageid):
        # First do a ship-check
        if target.stype == "Ryder": # Doesn't attach Rampage, just disables
            if target.name != "Nightmare": # doesn't work on 'Boss' ships
                target.en = 10
                target.max_en = 10
                if target.hp >= 1:
                    target.modifiers['Ryder Disabled'] = [100,0]
                    target.hp -= 200
                    if target.hp < 1:
                        target.destroy(sunrider)
                return # If its a Ryder target, it just wrecks it and returns out.
       
            target.modifiers["Rampaging Shambler"] = [5,0] # Use this as a flag for S_
            shamblereffect(target)
            return

    # shamblereffect(target) triggers a random effect on the target shot at

    Shamblerocket.functions.append((1,inflictshamblers))

    # This is put in gated_player_actions to run at the start of each turn. It checks for the

    def S_debuf_handler(target = False, Effect = False):
        for ship in BM.ships:
            if "Rampaging Shambler" in ship.modifiers:
                if ship.modifiers['Rampaging Shambler'][0] != 0:
                    check = ship.modifiers["Rampaging Shambler"]
                    check[0] += 10
                    if renpy.random.random()*100 <= check[0]:
                        ship.modifiers.pop("Rampaging Shambler",None)# = [0, 0]
                        show_message("The Shambler on a "+ ship.name +" has been caught")
                    shamblereffect(ship)
        return

    init_gated_player_actions.append(S_debuf_handler)


You can do something similar to trigger constant effects every turn, damage or otherwise as well as more normal things like making a weapon be more accurate or do more each time it's shot at a target in one turn.


Functions trigger on movement
Use:

This lets you trigger functions when moving, for example switching ship directions or detecting if you are next to an enemy or ally (this is done automatically). The function can be triggered before moveing, before counter attacks and after counterattacks determined by its check value.

Ideas are dealing damage if moving through a certain location on the map, or moving past enemy ships.

This can be used for Auras as the trigger on moving portion.

The Check is 0 = before move, 1 = before counter step, 2 is after counter step
The arguments must be: (ship, new_location, total_move_cost)

Example Code:
Display text when moved next to a core ship
Code: [Select]

# From Shambler mod, again a cut down part of Kryska's bonus
init 20 python:
    Ptext = "10% Teamwork bonus active."
    def loyalty_show(ship,new_location,total_move_cost):
        if adjacent_to_core(paladin):
            if not Ptext in paladin.mod:
                paladin.mod.append(Ptext)
        else:
            if Ptext in paladin.mod:
                paladin.mod.remove(Ptext)
    return

    Paladin.move_funcs.append((2,loyalty_show))
Aura code
This was used for the destroyer aura for Drath's Dreadnought, it is put in every ship's move functions and checks which mode to use when the ship moves. If its a Dreadnought then it applies it to other ships in the new location, if its not then it checks if it moves into the aura and if so, applies it.

Can also be applied to Covers.

MHL is a list of all hexes, generated by the framework.

Code: [Select]
init 20 python:
    def dread_aura_move(ship,new_location,total_move_cost):
        effect_triggered = False
        # This is split into two parts, if it is a Dreadnought then it applys/removes when it moves.
        # Else it checks where it moves for the aura and takes its effects
        # If the ship has moved out of range of the aura, it looses the disintegrate tag
        # Get aura hexes, aurahexes2 has all disintegration hexes, aurahexes is only a specific ship
       
        aurahexes2 = []
        for item in BM.ships:
            if item.name == "Dreadnought":
                for hex in MHL:
                    if get_distance(hex,new_location) <= item.aura_size: aurahexes2.append(hex)
        # The aura_size is an ship attribute unique to auras, automatically added in the framework

        if ship.name == "Dreadnought":
            aurahexes = []
            for hex in MHL:
                if get_distance(hex,new_location) <= ship.aura_size: aurahexes.append(hex)
                   
            # check all ships to see if they are in the disintegration aura, this ignores other Dreadnoughts
            # If in aura: if it has no tag, give it one set to false.
            # If tag == False, do 250 dmg and set to True
            for prey in BM.ships[:]:
                if prey.location in aurahexes:
                    if prey.name != "Dreadnought":
                        if not hasattr(prey, "Disintegrate"):
                            prey.Disintegrate = False
                        if prey.Disintegrate == False:
                            effect_triggered = True
                            prey.Disintegrate = True
                            prey.hp -= 250
                            if prey.hp < 1:
                                prey.destroy(prey)
                               
                # if outside aura, check to see if its in any other disintegrate auras.
                # If not, set tag to False
                # Uncomment this to allow repeated aura dmg when moving in-out often

                #if prey.location not in aurahexes:
                    #if prey.location not in aurahexes2:
                        #prey.Disintegrate = False

            if effect_triggered == True:
                show_message("The Dreadnought's Disintegration aura has damaged ships.")

        # If moveing ship is NOT a dreadnought
        else:
            #if new_location not in aurahexes2: # Uncomment this to allow repeated effect
            #    ship.Disintegrate = False
            if new_location in aurahexes2:
                if not hasattr(ship, "Disintegrate"):
                    ship.Disintegrate = False
                if ship.Disintegrate == False:
                    ship.Disintegrate = True
                    ship.hp -= 250
                    if ship.hp < 1: ship.destroy(ship)
        return

    # storeships is generated at init 15 and is how ship attributes can be added without modifying Battleship

    for ship in storeships:
        ship.move_funcs.append((1,dread_aura_move))
   


Death Triggers
Use:

This lets you assign functions to trigger on a ship's death.

You can either add them to the ship classes or on the ship directly, you can even use weapon functions to attach them.

You can use this to trigger shrapnel explosions, disable other ships, spawn wreckage and numerous other effects

The syntax is (check,function), if check is 0, it always fires. If its anything else then it checks the damageid of the last attack and if its the killing blow, it triggers.

It's arguments must be (self,attacker)

Example Code:
Wreckage code from Salvage Mod
Code: [Select]

This function makes a cover, and then takes information from its trigger ship to set certain attributes. Using storeships, you can set the function to every ship class including mod defined ships.

init 10 python:
    # cover_make makes the cover, sets its timer, label and some other stats used later
    def covermake(ship, attacker):
        #Ryders get obliterated, skip them
        if ship.stype == "Ryder":
            return

        # The cover attributes are pulled from ship, the destroyed ship
        create_cover(ship.location)
        c = BM.covers[-1]
        c.hp = int(ship.max_hp*(float(renpy.random.random()))) # Less than starting hp
        c.destroy = coverdestroy # New death code
        c.source = ship.stype
        c.timer = store.wreckstats[ship.stype][0]
        c.explode = store.wreckstats[ship.stype][1]
        c.cover_chance = store.wreckstats[ship.stype][2]
        c.faction = ship.faction
       
        # Apply a colourshifted image for the battle map, the labels are shifted by the timeing function
           
        if c.faction == 'PACT': # Use red tinted cover
            c.label = wreckimg[3]
            c.Degrade = wreckimg[4]
            c.Degrade2 = wreckimg[5]
           
        elif c.faction == 'Pirate': # Use green tinted cover
            c.label = wreckimg[6]
            c.Degrade = wreckimg[7]
            c.Degrade2 = wreckimg[8]
           
        else: # Use alliance tinted cover
            c.label = wreckimg[0]
            c.Degrade = wreckimg[1]
            c.Degrade2 = wreckimg[2]
       
    # Add this function to all ship's deathfunc list
init 16 python:
    for ship in storeships:
        ship.deathfuncs.append((0,covermake))
Assigning on death triggers
If you want a weapon to hit the target and cause an effect on death you can assign it by useing a weapon func to trigger on hit (check 1 or 2)  and getting it to assign the function.

If you want the function to trigger only on the kill-blow, you can use it to overwrite other functions, for example if you disintegrate the ship so it doesn't leave wreckage by setting ship.deathfuncs = []

Code: [Select]
init python:
    def expload(self, attacker):
        for ship in BM.ships:
            if get_distance(ship.location,self.location) < 3:
                ship.hp -= 200
                if ship.hp < 1:
                    ship.destroy(self)
        return
       
#This will assign the expload function to trigger on ship death

    def applyfunc(self, parent, target, dmg, hit_count, accuracy, damageid):
        if not (0,deathmessage) in target.deathfuncs:
            target.deathfuncs.append((0, expload))
        return

#This one triggers if the LAST attack on the ship is the one that kills it. damageid is passed by weapon functions.

    def applyfunc(self, parent, target, dmg, hit_count, accuracy, damageid):
        target.deathfuncs.append((damageid, expload))

Battle Start functions
Use:

This is a list run at the start of a battle, includeing if you need to restart a battle.

Syntax: (check, function)
The tuple should be put in the init_start_funcs list,  this stops functions being lost.  The check can either be the same as the mission or can be "Any".

If it matches BM.mission then the function is fired.

Example Code:
Function run on every mission
This code is taken from the Phoenix restoration mod and run at every battle to reset it.
Code: [Select]
init python:
    def setphoenix():
        global player_ships, upgrade_ships, phoenix, Attached, Boosterdead

        #if not dead at start of mission, set up booster for attached
        if booster in player_ships: # if not dead, attach phoenix to booster. set vars in case.
            if phoenix2 in player_ships: player_ships.remove(phoenix2)
            if phoenix2 in BM.ships: BM.ships.remove(phoenix2)
            if not phoenix2 in upgrade_ships: upgrade_ships.append(phoenix2)
            if not booster in player_ships: player_ships.append(booster)
            if not booster in BM.ships: BM.ships.append(booster)
            if booster in upgrade_ships: upgrade_ships.remove(booster)
            if not booster.mercenary: booster.mercenary = True
            for item in booster.weapons[:]:
                item.energy_use = item.energy_use2
            store.phoenix = booster
            store.Attached = True
            for item in booster.weapons:
                item.energy_use = item.energy_use2
            booster.move_cost = 15
            if phoenix2.location != None: set_cell_available(phoenix2.location)
            BM.unselect_ship(phoenix2)
            booster.lbl = im.Flip('Battle UI/label_phoenixboaster.png', horizontal=True)
        return

    init_start_funcs.append(("Any",setphoenix))

The important thing is to use a variable to see if the function should trigger, in this example its looking for booster in player ships.
This is because the function will be active in EVERY mission if the check is "Any", even if a needed variable hasn't been set.

Win/Loose functions
Use:
These functions are triggered either on winning or looseing a battle (shock)

Similar to start_funcs, the check is "Any" or matches BM.mission.
The tuple should go in init_win_funcs or init_loss_funcs.

These functions will run and then unless instructed otherwise, the battle will continue to the victory screen. The functions can be used to jump outside the battle flow and direct you to an alternate point in the story with renpy.jump but make sure to clean the battle fully. Have a look at battle_end in classes.rpy

This is of limited use as the code could normally be done by just modifying the after_mission label. There are some times it could be useful though, such as cutting out or redirecting the normal victory screen.

Example Code:
Run on win
this is untested code as an example

Code: [Select]
init python:
    def getsurvivors():
        # Battle cleaning code
        T = get_shipcount_in_list('Merchant Ship', player ships)
        if T > 5:
            renpy.jump('Mission_500_sucsess')

        if T < 3:
            renpy.jump('Mission_500_poor')

        renpy.jump('Mission_500_ok')
        return

    init_win_funcs.append((43,getsurvivors))

Gated player actions
Lets you run actions on every player turn on any battle. This lets you make mods that span the entire game and trigger as time progresses.

Use:
You can use this for slow-acting effects such as weapon functions or time based effects like deteriorating wreckage. It can be run on specific missions or on every mission.

The function should be added to init_gated_player_actions

Example Code:
Wreckage deterioration
Code taken from the Salvage mod, it checks all covers for a timer. If the timer exists then it is reduced and if the timer is reduced to 0, it is destroyed. if the timer reaches a certain value it changes its lbl.

Code: [Select]
init python:
    def coverclock():
        if BM.covers != []:
            for c in BM.covers[:]:
                if hasattr(c, 'timer'):
                    c.timer -= 1
                    if c.timer == 2: c.label = c.Degrade
                    if c.timer == 1: c.label = c.Degrade2
                    if c.timer == 0: c.destroy(c)
        return

    init_gated_player_actions.append(coverclock)
Timed weapon handler
This is used in the Shamble mission's secret weapon. It checks all ships in BM.ships for a modifier that's set, then runs a function to apply an effect to any afflicted ships.

Code: [Select]
init python:
    def S_debuf_handler(target = False, Effect = False):
        for ship in BM.ships:
            if "Rampaging Shambler" in ship.modifiers:
                if ship.modifiers['Rampaging Shambler'][0] != 0:
                    check = ship.modifiers["Rampaging Shambler"]
                    check[0] += 10
                    if renpy.random.random()*100 <= check[0]:
                        ship.modifiers.pop("Rampaging Shambler",None)# = [0, 0]
                        show_message("The Shambler on a "+ ship.name +" has been caught")
                    shamblereffect(ship)
        return
    init_gated_player_actions.append(S_debuf_handler)
This is run at the start of every turn so you get a turn-by-turn debuff until the shambler is caught.

Enemy actions
This is the counterpart to gated_player_actions, its run before the enemy moves and can be used to trigger functions that spawn reinforcements or other effects such as auras.

They should be added to the enemy_actions list as (check,function) where check can be "Any" or BM.mission.

Reinforce around ships
This is code from an unreleased mod that checks for certain ships and reinforces around them.
Reinforce is a function included in the framework.

Code: [Select]
init python:
    def M50function():
        for ship in BM.ships:
            if ship.name == 'Smuggler':
                reinforce( ((ship.location[0]-2,ship.location[0]+2),(ship.location[1]-2,ship.location[1]+2)),[[PactMook,1],[PactBomber,2],[PirateGrunt,1]],BM.turn_count/2,0.1 )
        return
 
    enemy_actions.append((50,M50function))

Next action trigger
Use:
This is very specialized, when a function is added to this list it triggers the next player loop in the battle, makeing a self-contained bubble in the code. This lets it interact with the battle in ways that would otherwise throw a UI error. It should be added to player_actions and will trigger pretty much instantly on the player's turn.

Example Code:
Phoenix booster warp weapon
Code taken from Phoenix restoration mod's warp weapon.
It is called from the fire code of the Warp weapon.

Code: [Select]
#weapon code
    class Boosterwarp(Support):
        def __init__(self):
            #stuff

        #Modified fire code
        def fire(self,parent,target,counter=False):
            #set variabls
            store.warpstats = []
            store.warpstats = [BM.selected, self.transport_cost, parent.move_cost, self.energy_use, parent.location, parent.weapons]
            store.player_actions.append([BM.mission,Boosterjump])
            renpy.jump('mission'+str(BM.mission))
            return

# Jumping restarts the battle code and lets the function in player_actions trigger
# Then the Boosterjump function triggers and removes itself from player_actions.
# Function that is added to player_actions, it uses UI.interact to pick a hex and jump to a new location.

    def Boosterjump():
        global WarpShockVar
        #warpstats:
        #    0 = selected ship
        #    1 = transport cost
        #    2 = original move cost
        #    3 = energy use
        #    4 = selected location
        #    5 = weaponslist
           
        store.warpstats[0].en -= store.warpstats[3]-store.warpstats[1]
        #reduces user's en by cost - 1 transport cost
        store.warpstats[0].move_cost = store.warpstats[1]
        #sets movement cost to transport cost
        store.warpstats[0].movement_tiles = MovTile2(store.warpstats[0]) # #loads areas that ship can reach
        BM.selected.weapons = []
        #store.WarpShockVar# check for to do warpshock
        #engage movement loop
        looping = True
        while looping:
            BM.selectedmode = True # Shows moveable tiles.
            BM.active_weapon = None
            Loc = ui.interact()
            if Loc[0] == 'move': # if player clicked on a movement tile
                    oldcords = store.warpstats[0].location
                    warpdamage = (get_distance(store.warpstats[0].location,Loc[1])*15)
                    store.warpstats[0].en -= (get_distance(store.warpstats[0].location,Loc[1])*store.warpstats[1])
                    JumpShip(store.warpstats[0],Loc[1][0],Loc[1][1]) # splits the location of movement tile into x and y and then uses warp function
                    store.warpstats[0].move_cost = store.warpstats[1] # resets move energy cost
                    store.warpstats[0].movement_tiles = get_movement_tiles(store.warpstats[0]) # resets move tiles
                    if WarpShockVar:
                        for ship in BM.ships[:]:
                            if get_distance(ship.location, booster.location) == 1:
                                ship.hp -= warpdamage
                                if ship.hp < 1: ship.destroy(phoenix)
                        warpdamage = get_distance(oldcords,Loc[1])*20
                        booster.hp -= warpdamage
                        if booster.hp < 1:
                            booster.destroy(booster)
                        show_message("The Phoenix's warp drive has caused a ripple in space.")
                    looping = False
               
            elif Loc[0] == 'deselect':
                store.warpstats[0].en += store.warpstats[3]-store.warpstats[1]
                store.warpstats[0].move_cost = store.warpstats[1]
                store.warpstats[0].movement_tiles = get_movement_tiles(store.warpstats[0])
                looping = False
        store.warpstats[0].move_cost = store.warpstats[2]
        store.warpstats[0].movement_tiles = get_movement_tiles(store.warpstats[0])
        store.warpstats[0].weapons = store.warpstats[5]
        return

Mod Enhancements
These are used to improve mod battles, there are both cosmetic and functional mods.

Visual formation mode.
Use:
This colors hexes that ships can or can't warp into. It works with the formation Place mod.

To enable:
    General overwrite:
        Overwritewarp = True

To use Blue boundries where ships can be placed, set Warpchoice = True, to use Red boundries where ships can't be placed set Warpchoice to False

Formation Place mod
Use:
To define your own placements, do:
Code: [Select]
python:
    BM.formation_range = [hexes]
As long as BM.formation_range is a list, the Visual formation mod will trigger automatically and use the hexes put in the list.
If you want to define an area, try doing something like this:

Code: [Select]
init python:
    coordset = [((12,8),(16,8)),
                 ((11,9),(18,9)),
                 ((10,10),(18,10)),
                 ((10,11),(18,11)),
                 ((9,12),(18,12)),
                 ((9,13),(18,13)),
                 ((8,14),(18,14)),
                 ((9,15),(18,15)),
                 ((9,16),(18,16))]
   
    for outer in coordset[:]:
        for a in range(outer[0][0],outer[1][0]+1):
            shamblehexlist.append((a,outer[0][1]))
This adds hexes between and includeing the hex pairs into a list that can then be used for BM.formation_range.

Pin grid to map
Use:
This lets you move/zoom the background when you move/zoom the grid. This means you can plan the mission about haveing a fixed point in the battle, for example a planet or hazard will stay in the same location rather than zooming around the map every time its dragged.

To trigger set Backgroundmode to True

Autonomus Ally ships
Use:
This lets you set any ship to act on its own AI (identical to the enemy AI). You can trigger the AI phase either before or after the enemy turn. They are able to use support on your ships or you can use them on them. This could be useful to do large scale battles between Alliance and Pact forces where the Sunrider is just a supporting ship.

There is a pre-made mod to the victory screen that lets you assign ally kills to their own list and shows up as an Assist bonus. To use this, attach killed_by_ally to ship.weapon_funcs.

To trigger, set ship.Ally = True.
To use Ally AI before the enemy ships move, set Ally_move to 1, to move after, set Ally_move to 2

Multi-battle Battles
Use:
This is a function that lets you run battles simultaneously, for example a mission where you cause a distraction, letting some other forces achieve a goal. It handles switching between phases and stores ships/covers/background and such.

The list setup is slightly complex but the actual switching is very easy.

Setup:
To use the function, you make a list containing a list for each battle phase you want. This could theoretically be 50+ but it would get very confuseing.

Fliplist = [[Phase1],[Phase2]]

Each Phase list should be: ["background", [player_ships], [enemy_ships], [covers], "phaseid"]
The easy way to set up is do create_ship as normal, put them into the list then blank player_ships and others so you can start either another list or do the main mission.

Trigger code:

This is actually very easy. Just do Fliplist = battleshift(Fliplist,phase number). The Battleshift function returns the list you feed it so there is nothing else you need to do.

Example Code:

Setup code
Taken directly from the Shamble hunting mod.
Code: [Select]
label mission55init:
    python:
        #Standard setup stuff with modified formation range.
        BM.formation_range = shamblehexlist
        Backgroundmode = True
        zoomlevel = 1
        enemy_ships = []
        destroyed_ships = []
        clean_grid()
        BM.xadj.value = 576.0
        BM.yadj.value = 816.0

    # Set up a phase list by makeing ships and setting the background
        BM.battle_bg = 'Shamble/Icyplanetspace.jpg'

        create_ship(Shambler(),(8, 2))
        create_ship(Shambler(),(5, 3))
        create_ship(Shambler(),(7, 3))
        create_ship(Shambler(),(10, 5))

        create_ship(PactElite(),(12, 2))
        enemy_ships[-1].max_en = 110
        enemy_ships[-1].move_cost = 60
        enemy_ships[-1].weapons = []
        enemy_ships[-1].register_weapon(PACTEliteMelee())
        enemy_ships[-1].weapons[0].damage = 150
        create_ship(PactElite(),(4, 7))
        enemy_ships[-1].max_en = 110
        enemy_ships[-1].move_cost = 60
        enemy_ships[-1].weapons = []
        enemy_ships[-1].register_weapon(PACTEliteMelee())
        enemy_ships[-1].weapons[0].damage = 150
        create_ship(PactElite(),(12, 11))
        enemy_ships[-1].max_en = 110
        enemy_ships[-1].move_cost = 60
        enemy_ships[-1].weapons = []
        enemy_ships[-1].register_weapon(PACTEliteMelee())
        enemy_ships[-1].weapons[0].damage = 150

        # There are more ships in the actual code
        # enemy ryders are limited to 1 space and a reduced dmg melee attack as in atmosphere.
       
        enemy_ground = enemy_ships # copy enemy_ships for the fliplist and then blank it
        enemy_ships = []
       
        create_ship(MissileFrigate(),(3, 3))
        create_ship(PactCruiser(),(4, 3))
        create_ship(MissileFrigate(),(3, 4))
        create_ship(PactCarrier(),(10, 6))
        create_ship(PactMook(),(6, 7))
        create_ship(MissileFrigate(),(1, 9))
        create_ship(PactMook(),(4, 9))
        create_ship(PactMook(),(8, 11))
        create_ship(MissileFrigate(),(4, 12))
        create_ship(PactCarrier(),(5, 12))
        create_ship(MissileFrigate(),(2, 15))

        Tship = create_ship(Transport(),sunrider.location)
        player_ships.remove(Tship)

        # Now you can make the fliplist

        Fliplist = [
                     ['Shamble/Icyplanetspace.jpg',player_ships,enemy_ships,[],'Space'],
                     ['Shamble/spaceicey.png',[Tship],enemy_ground,[],'Ground']
                     ]
The final entry in each phase list is used for the mod to check what phase it's in. It sets BM.phase to that value so you can always tell what part of the mission is active.
Trigger code
This example flips the battle between two lists every turn. The % symbol is remainder divided by x and a useful way to know when to flip a battle.
Code: [Select]
label mission55:
    python:

        if turncount != BM.turn_count:
            turncount = BM.turn_count

            if turncount % 2 = 0:
                Fliplist = battleshift(Fliplist, 2)

            if turncount != 1 and turncount % 2 == 1:
                Fliplist = battleshift(Fliplist, 1)

Auras: Visual component
Use:
This lets you show an aura effect on the battlefield, the actual effects need to be coded seperatly but with most effects its quite easy, useing move_funcs and gated actions lets you do a number of complex tasks.

The aura is an image or displayable and can use a transform (such as hoverglow)

ship.aura_size is the number of hexes range the aura is shown
ship.aura_image is the image shown per affected hex
ship.aura_alpha is the image transparency.
ship.aura_targets_self is True if the aura should display on its own host's hex

Auras can also be used on Cover objects.

Example code
Drednought Aura
Code: [Select]
init 2 python:
    class Dreadnought(Battleship):
        def __init__(self):
            super(Dreadnought, self).__init__()
            self.stype = 'Ship'
            self.name = 'Dreadnought'
            self.animation_name = 'alliancebattleship'
            self.faction = 'Player'
            self.max_hp = 2800
            self.hp = self.max_hp
            self.max_en = 180
            self.base_armor = 60
            self.armor = self.base_armor
            self.en = self.max_en
            self.max_missiles = 0
            self.missiles = self.max_missiles
            self.move_cost = 45
            self.hate = 300
            self.evasion = 0
            self.lbl = im.MatrixColor('Battle UI/label_sunrider.png',im.matrix.tint(0.4,0.2,1))
            self.portrait = None
            self.flak = 0
            self.flak_range = 0
            self.default_weapon_list = [DreadnoughtCannon()]

            # Aura attributes
            self.aura_size = 4
            self.aura_image = hoverglow("Battle UI/vanguard hex.png")
            self.aura_alpha = 0.5
The last three attributes set the aura look. This one shows red glowing hexes around the ships location.

AI: Ordering
Use:
This lets you manually order the AI of ships. The standard AI checks for ships flagged as support, ships with higher than average armor and regular ships.

The new order method is: Support, 1, 2, Lead, Standard, 3, 4 and you can set ship.Priority to call them first in their group.

This will be more useful when the ship AI is modified.

Example code:
Code: [Select]
Ship1.Group = 1
Ship1.Priority = True

Ship2.Group = 4
Ship2.Priority = False

In this example, Ship 1 will go just after support, whilst Ship 2 will move very late in the enemy's turn.

Easy Team Switch
Use:

Ships can now be switched between teams by changing the ship.faction and x_ships lists. If useing Dynamic animations, then animations will automatically line up.

There is a new function that will do it for you, change_ship(shipname) or ship.change_faction()

change_ship(ship) or ship.change_faction() will turn it PACT
change_ship(ship,True) or ship.change_faction(True) will turn it Player
change_ship(ship,faction = 'Name') or ship.change_faction(faction = 'Name') will set the faction to 'Name'

Example:
change_ship(blackjack)
or
blackjack.change_faction()

sets the Blackjack to PACT controlled

Weapon Ammo
Use:
Each weapon can be assigned ammo and its own ammo use. The ammo is tracked by the weapon rather than the ship and can be set to refill on mission start or not.

All weapons can use ammo, not just missiles and rockets. Ammo can be controlled by setting weapon.ammo and is automatically reduced by weapon.ammo_use

Code: [Select]
   # Default is set to None, setting it otherwise activates ammo mode
    Weapon.ammo = None # Controls the ammo level
    Weapon.max_ammo = None #Maximum ammo, if auto-replace, will replace to this level.
    Weapon.replace_ammo = True # Does it refill at start of mission?
    Weapon.ammo_use = 1 # ammo used per shot

Example:
From the Shambler hunting mission, unique weapon
Code: [Select]
    Shamblerocket.ammo = 0 # starts with 0 ammo
    Shamblerocket.max_ammo = 3
    Shamblerocket.reset_ammo = False # Is restored through the store
    Shamblerocket.ammo_use = 1

Weapons can be set to use a combined ammo pool if you use weapon functions to reduce the other weapon's available ammo (or missiles as needed)

On-Load functions
Use:
This lets you trigger functions on loading of games, this is helpfull if you need to check/set things like the formation range or label overrides that would otherwise reset on every load. Also good for fixing compatibility problems.

Can launch labels as well as functions, uses (Conditional, Event) where Conditional must eval to True to trigger.

Example:
The code to do a lable and a function is the same. The function can have no arguments and should just be its name in a string
Code: [Select]
after_load_funcs.append((True,"labelname"))
after_load_funcs.append(("Modname_version == 1.5","Function"))

By doing something like:
Code: [Select]
init python:
    if not Modname_version = Current_modname_version:
        after_load_funcs.append((True,"labelname"))
    Modname_version = Current_modname_version
You can call a function or label to patch problems if needed.

Dynamic Animations
Use:
Hit animations are now able to tell what direction an attack came from and what direction ships are faceing, if usedynamic == True. If ModFramework_Dynamic_animations is used, it will automatically do it for attack animations as well.

Hit animations are easy to use with custom ships, but attack animations require a bit more work. If they are not included, however the game will just use the existing ones and will not throw errors.

Modified weapon animations via weapon.attanimation and weapon.hitanimation overwrite these.

Adding custom Ships (hit):

Code: [Select]
init python:
    sila.append(("ship base class", "ship side image"))
    ship_animation_list.append(ship base class)

Example useing the Sunrider:

init python:
    sila.append(("Sunrider", "sunrider_side"))
    ship_animation_list.append(Sunrider)
Adding Custom Ships (Attacks)
Adding a custom dynamic animation is very easy and not time consuming because most of it is just flipping images.
Templates will be added in the future.

Weapon Animation Overrides
These let you fire a different label when the weapon fires and when it hits to the standard defaults, Useing the dynamic ship image from Dynamic attacks, it means you can use one label to (as an example) show laser attacks from the Legion as hitting with red beams like it shows on the attack animation.

Use:

Code: [Select]
Weapon.attanimation = 'attack label overwrite'
Weapon.hitanimation = 'weapon hit label overwrite'

Example:
Taken from the Ryuvian Missile demo: (This adjusts for ships but not their location as it is slightly old code. Look at dynamic code for weapons that can be used by both sides)

this example code is old, needlessly long and is done much better by the Dynamic labels, a better one will be put up soon
Code: [Select]
init 2 python:
    # Attack label (actually unchanged)
    RyuvianCruiserMissile.attanimation = "atkanim_ryuviancruiser_missile"

    # Hit label (changed)
    RyuvianCruiserMissile.hitanimation = "Rymissile"

label Rymissile:
    if config.skipping:
        return

    $renpy.show_screen('show_background',_layer='master')
    show screen animation_hp
   
    ###################################
    ###         Shiftimage          ###
    ###################################
    ###    Dynamic Image Checking   ###
    ###################################
   
    #This uses the dictionary that comes with M52-53_7.2_1.2.rpy to find the label for the right ship.
   
    # The code below tells you if the entry is an image, a label or a function and acts accordingly
   
    #If it isn't in the dict then it's skipped
    # In brief, if the result has Image in front then the label is used.
    # If its a string without Image in front, its assumed to be a filename
    # If its not a string, it is assumed to be a function and is run to attempt to extract an image or filename
    # It then is checked for the Image prefix and decides what it is
   
    # If it is a file then it is converted into an image and shown directly
    # If it is a defined image then it is shown at centerscreen
    # Feel free to just copy-paste if you want to use this
   
    # IF the ship is not in the directory, it can be added automatically if the correct attributes are set, otherwise there will be a plug-in on the Missile page.
   
    python:
        shiftimage = BM.target2.animation_name
       
        if shiftimage in ship_animations:
            shiftimage = ship_animations[shiftimage] # extract the image name
            if shiftimage[0:5] == "Image":
                shiftimage = ["image",shiftimage[5:]]   
        else: shiftimage = True
       
    if shiftimage == True:
        return
       
    python:
        if type(shiftimage) == str:
            imagetype = "file" # if the image is a filename
        elif type(shiftimage) == list:
            imagetype = "list"
        else:
            shiftimage = shiftimage()
            if shiftimage[0:5] == "Image":
                shiftimage = ["image",shiftimage[5:]] 
                imagetype = "list"
            else:
                imagetype = "file"
   
    if imagetype == "file":
        #This makes the image
        image shiftimage:
            shiftimage
            xanchor 0.5 yanchor 0.5
       
        show shiftimage:
            xpos 0.5 ypos 0.5

    if imagetype == "list":
        $renpy.show(shiftimage[1], at_list = [Position(xpos = 0.5, ypos = 0.5)])
       
    ###################################
    ###         Shiftimage          ###
    ###################################
    ###     Dynamic Image Done      ###
    ###################################
    ###  Boreing standard stuff now ###
    ###################################
   
    pause 0.1

    play sound1 "sound/missilefly.ogg"

    show RY_missile1:
        rotate -10
        xpos 1440 ypos 0 alpha 0
        pause 0.4
        alpha 1
        linear 0.7 xpos 620 ypos 480
        alpha 0

    show RY_missile2:
        rotate -10
        alpha 0 xpos 1640 ypos 0
        pause 0.3
        alpha 1
        linear 0.7 xpos 740 ypos 560
        alpha 0

    show RY_missile3:
        rotate -10
        xpos 1750 ypos 0 alpha 0
        pause 0.2
        alpha 1
        linear 0.7 xpos 920 ypos 500
        alpha 0

    show RY_missile4:
        rotate -10
        xpos 1930 ypos 0 alpha 0
        pause 0.1
        alpha 1
        linear 0.7 xpos 1010 ypos 540
        alpha 0

    play sound1 "sound/explosion1.ogg"

    show layer master at shake1(pausetime=0.9,repeats=12)
   
    # If its a large ship, for scale you want small exploads, if ryder you want large
    # contrary to documentation, you can set images OUTSIDE of an image block
    python:
        if BM.target2.stype != "Ryder":
            Ryexpload = im.MatrixColor("gameplay/Animations/Sunrider/sunrider_side_missilehit1.png",im.matrix.tint(0.2,0.2,1)*im.matrix.brightness(0.6))
           
        else:
            Ryexpload = im.MatrixColor("gameplay/Animations/PirateBomber/missileexplode2.png",im.matrix.tint(0.2,0.2,1)*im.matrix.brightness(0.6))
       
   
    image Ryexpload1:
        Ryexpload
        xanchor 0.5 yanchor 0.5 xpos 0.5 ypos 0.5
   
    image Ryexpload2:
        Ryexpload
        xanchor 0.5 yanchor 0.5 xpos 0.5 ypos 0.5
       
    image Ryexpload3:
        Ryexpload
        xanchor 0.5 yanchor 0.5 xpos 0.5 ypos 0.5
       
    image Ryexpload4:
        Ryexpload
        xanchor 0.5 yanchor 0.5 xpos 0.5 ypos 0.5
   
    show Ryexpload1:
        xpos 0.2
        alpha 0
        pause 0.9
        ease 0.2 alpha 1
        pause 0.2
        ease 2 alpha 0

    show Ryexpload2:
        xpos 0.4
        alpha 0
        pause 0.9
        ease 0.2 alpha 1
        pause 0.2
        ease 2 alpha 0

    show Ryexpload3:
        alpha 0
        xpos 0.25 ypos 0.4
        pause 0.9
        ease 0.2 alpha 1
        pause 0.2
        ease 2 alpha 0

    show Ryexpload4:
        alpha 0
        xpos 0.35 ypos 0.55
        pause 0.9
        ease 0.2 alpha 1
        pause 0.2
        ease 2 alpha 0
   
    pause 0.15

    show sunrider_missiletrail_hit with sunridermissilehitwipe:
        ypos 0.45
    hide sunrider_missiletrail_hit with dissolve

    pause 0.3

Story manipulation
Dynamic ship map
This lets you set buttons on the ship map dynamically, as an insert into the story. You can use any combination of variables and it will create the label as long as the eval string returns True.

It also doubles as a way to run functions when you hit the ship map.

Use:

All labels are checked for validity when the ship map is loaded. If the eval string returns True, then a ship button is created. This WILL overrite other buttons so it is best to include something like 'asa_location == None' in its check to avoid overwriting story labels.

Code:
Syntax and Options
The basic code is:
Code: [Select]
chat_labels.append(['conditions','name','loc','label'])
conditions is a string of variables, functions ext that must return True when evaluated
Example: "asaga_affection >= 4 and asaga_location = None" would let the label appear so long as she was not already showing on the ship map and her affection is greater or equal to 4

'name' is the person you want to see (it sets that person's location and their button)

Name Options
pro (Progress button)
gal (Galaxy Map)
asa (Asaga)
ava (Ava)
chi (Chigara)
ica (Icari)
cla (Claude)
sol (Sola)
kry (Kryska)

'loc' is the location the button appears:

Location Options
captainsloft
sickbay
messhall
bridge
engineering
lab
hanger

Label is the string of the label you want to go to.

Talk to Icari in the Lab
From the Phoenix Restoration Mod

Code: [Select]
init python:
    chat_labels.append(['mission11_complete and ica_location == None and chi_location == None and Boostertalk == False','ica','lab','Rebuildbooster'])

Triggers if mission 11 is done, neither Icari or Chigara are on the map and the booster repair hasen't been talked about.

Puts Icari in the Lab and jumps to the Rebuildbooster label when clicked

From Saibotlieh's asaga mission

This is a more complex example useing functions to check if its available, it puts Asaga on the Bridge so long as she has no other location, has not been spoken to about the mission and when a judge function returns True.

Code: [Select]
init python:
    chat_labels.append(["asa_location == None and MoACharacterMissionPossible('Asaga') and SL_SM_AsagaMission_Intro and MainMissionCompleted() >= 12",'asa','bridge','SL_SM_AsagaMission_Intro'])

Generic Warp code
This lets you run a generic warp animation with ~10 lines of code and jump to the label you want without a large amount of space taken up by repetitive code.

Use:

Planet("Name", "jumplabel1", Posx, Posy, "eval string")

Normally you would jump straight to the mission selection, however with this method, you first jump to a label that defined the background and info text, then bounce to the selection label.

label jumplabel1:
    image modbg2 = main background image
    image modinfo2 = info card
    #(You can just use a transparent image if you don't want anything shown)

Now you would normally jump straight to the label from the mod selection, instead you jump to a label to set modjumplabel to the label you want to end up at and then jump to transit

label missionacsepted:
    $modjumplabel = "missionstart"
    jump transit

Then the sunrider is seen warping out and into the modbg2 you set earlier.

Example:
From Shambler Hunting
Code: [Select]
init python:
    Planet("ICY MOON", "Icyselection", 1670, 250, "shamblesvar") # Define the planet

label Icyselection:
    image modbg2 = "Background/space5.jpg" # Define the background
    image modinfo2 = "Shamble/Icymoon_info.png" # Define the infocard
    jump Icemissions # Jump to next step

label Icemissions: # Choose mission screen

    $ map_back = "planetback" # Handled automatically with this code

    if Icymission1: # set a variable if you want the mission to be available
        $ galaxymission1 = True # Is mission available?
        $ mission1 = "Shamblesmission" # Jump to label for mission
        $ mission1_name = "Investigate: Pact Base" #mission name

    else: # Blank if mission is already done
        $ galaxymission1 = False
        $ mission1 = None
        $ mission1_name = None
    jump showmodplanet

label Shamblesmission: # The label the mission button jumps to
    $ modjumplabel = "aboveicymoon" # The label to visit after you warp our
    $Icymission1 = False # Disable the mission
    jump transit # Show warp and jump to mission label   

Ship Flavor text
Use:
This lets you put writeing in the status box, where buffs are shown by adding strings to ship.mod
Example:

Code: [Select]
init 2 python:
    Mochi.mod.append("Pilot: Claude Trillo")
    Mochi.mod.append("Cargo: Booze")

Would show up on the Mochi's status screen

Bug Fixes
Laser Accuracy: Lasers now benifit from accuracy research
Counter Attacks: Enemy ryders no longer counter attack AND move away from your melee ryders.

Combo Time!
These are extremely versatile on their own but all these features are designed to work together.

For example you can make auras by adding a function to ship.move_funcs that detects nearby ships and applies stealth to them or grants them an accuracy bonus.

You can combine a death function with a weapon function to move the phoenix into a destroyed ryder's hex.

You could move the Liberty within range of a cargo vessel to scan its hold and display what Cargo it is carrying in its status screen.

You can attach a slow-acting acid to a ship that deals increasing damage and disables a target ship over time by combining a weapon function and a per-turn function.

You can apply enviromental effects by putting an aura on certain covers, setting their lbl to invisible and makeing their block chance 0% and then triggering functions on ships that move into the aura's range (penalty to accuracy or evasion vs targets outside of the effect)

Future Plans:
Blank Hex targeting: Weapons will be able to fire at empty hexes, usefull for area weapons and weapons such as mines.

Persistent Item Weapons: Will be able to place Mines and other things on the map.

Custom AI modes: Several different AIs that will react to Auras and have their own methods of killing you.

Pictures!
Warp Mod


'Medicinal Booze'


Disintegration Aura (Aura area actually glows/flashes)


Ally victory screen (Yes I cheated and Repair costs can go negative  ;D)
Need to adjust the spacing on the Ally Kill text


Weapon Ammo (Look at the Sunrider Laser)


Installation:
Just drop the ModFramework.rpy file into the Sunrider/game folder. If you have any of the M52 rpy files then either delete them and the rpyc files or drop in the blank files from the google link.

To use the Dynamic animations for fireing and hitting of ships, also download and use the ModFramework_Dynamic_animations.rpy and set usedynamic to True in either the main file or the Override file

The Override file is where player moddable variables can be used to overwrite the Modframework variables to enable/tweak features, it is completely optional.

All my other current mods have been updated to work with this rather than the M52 files. Just drop the new versions in to work with this update.

If there are any bugs, PLEASE let me know, this has been tested both on my mod game and a blank game so they should be quite rare.
As the code is quite disorganized it can be hard to connect what bits do what but I would be happy to go through code for people if there is any need.

Unfortunately this framework is not compatible with the Extensive Re-balance Mod

Updates:

18/12: Ordered ship actions.
20/12: Applied holder attributes to Battleship and let melee run without a character sprite
            This lets you change a ship to player/enemy by just changing ship.faction and switching it's x_ships list.
            change_ship(ship,faction) will do this for you but Animations may need altering
22/12: Selected weapons now show damage that includes any modifiers
            Fixed a bug with the GravityGun
29/12: Enabled ammo for weapons. Ammo is tracked by each weapon specifically and can be set to replenish at start of battle or not.
2/1:     Enabled after-load functions to be used
5/1:     Weapon functions can now also be put into ship.weapon_functions to trigger on each weapon that ship controls.
13/3:   Enabled Auras to display on Covers.
            Added Dynamic animations and improved documentation with examples
            The movement range is no longer automatically unlimited and has been put down to 5. The phoenix mod will set it back to unlimited or it can be done in Override.
            Current line count over three files is... 13,475!

6
Game Play Balance / 7.2 AI ideas
« on: December 07, 2015, 11:55:57 pm »
This could be argued for putting in the mods section but I wanted other player views and it does have relevance to balance. I have made working auras for Drath but in testing, this led to a Dreadnought obliterating every PACT ship in the mission with its aura whilst only doing weapon damage to the SR as the AI doesn't detect Auras.

As it seems my motto is 'don't do something if you can over-do it' I've started picking apart he AI code looking to make 4 new AIs based on the original, each with different priority and 'tactics'. These AIs would be able to detect auras and be able to 'plug' in functions for special situations.

My current thoughts are:

Aura: detect if aura is beneficial, if so act protectively, else move towards enemy ships, prioritize movement over attack

Reckless: Disregards defense hexes and priority attacks mainly support ships if viable, otherwise takes on low hp ships or as a last resort (too much flak or whathaveyou) hangs back. (Designed for bombers and Ironhogs. may change AI if out of missiles/rockets). (They might also target ships that need to be protected more frequently but that may be too cruel)

Guard: These ships try to move before attacking and prioritize protecting other ships but reserve some energy for weapons fire. if player ships have high laser, they will try and close the distance to make their kinetics more useful.

Coward: These will retreat if they are hit badly enough (or support) and try to avoid enemy fire by using cover or shields. This prolongs the damage they can deal and temps the player ships to move closer and become more vulnerable. (bonus: if they are missile frigates with no ammo, they try to run to the right and remove themselves from the battle).

I would like to assign the AIs to ships ideally (Bombers are Reckless so use rockets/missiles to take out support and allow Battleships to hit the larger ships, while cruisers are Guarded and  support missile frigates and battleships by moving to the most defensive place they can)

If anyone has ideas I haven't considered, other archetypes or modifications to the AI's already posted then I can try and work them in.

7
Mods / [7.2] Formation mod
« on: November 05, 2015, 04:34:47 pm »
Formation Mod
Now in Red , Blue and Sticky!


This mod is an overhaul of the formation stage of a battle, in standard play it can be used to show clearly where ships can be placed and in mods it can be used to directly define a limited area where ships can start giving great control over the map!

For Players!

For players, this mod will highlight either where they can start or where they cannot, depending on the settings they use for the mod.

Picture time!

Blue! (This is the mode you get when Warpchoice = True)


Red! (This is my preference for non-mods and shown when Warpchoice = False)


Activation:

As I am intending this for mod use, it is initially deactivated, if you want to make it used as standard then change line 30 to Overwritewarp = True

Then choose on line 29 if you want Blue or Red (Blue = True)

For Modders!

This provides a great tool for mission planning, by including the file in your mod zip and before the mission setting BM.formation_range to the area you want to set you can define your own area for warping into.

This gives you much closer control over the battles whilst still letting players put their ships in a formation they want!

It is auto-activated any time that BM.formation_range is a list rather than an integer and the mission name is valid for formations.

Example:

Code: [Select]
python:
    BM.formation_range = [(10,10),(11,10),(11,11),(10,11)]


This would force the player to warp in only those 4 hexes. If the player has more than 4 ships though you CANNOT start the battle

If you wanted to have a limited range of warp in areas then you could do something like this...
Code: [Select]
python:
    xwidth = 4
    ywidth = 4
    newformationrange = []
   
    for a in range(1,xwidth+1):
        for b in range(1,ywidth+1):
            newformationrange.append((a,b))

    BM.formation_range = newformationrange
This would give you an example square formation of (1,1) to (4,4)

This is even more customisable as you can also change the error message (check the screenshots) by changing warperror to your string and you can change the hex images by changing Bluehex and Redhex to the image you want to use!

Picture Time!

This is with BM.formation as [(11,10),(12,10),(11,11),(12,11),(13,9),(10,10),(11,9),(11,8),(12,8),(10,8),(10,9),(11,12),(12,7),(13,9),(11,7),(11,6),(12,9)]

Blue Mode


Red Mode


General daftness  ;D:


Stickiness:

It is now possible to effectively pin the hexes to the background, when you drag you also move it. This is toggle-able by setting Backgroundmode to True. When this is done then it spreads the background over the entire battlefield, so it DOES look slightly different.

This could be useful to get players placing ships near planets, otherwise when they moved the map, the planet would suddenly run away! This can be set at any time and works both in formation and the actual battles.

Useing Sticky



Not useing Sticky



The pictures for each mode are taken with the ships in the same place but in the second picture the map has been moved. With the sticky ones, the ship's position wit the planet remains the same.

As the image is being applied on the entire battle map then there is a small visible difference, when you swap but unless your specifically looking for it its not that noticeable.

Use:

To activate: Change BM.formation_range to a list of the hexes you want
To change mode: Change Warpchoice to either True or False
To change the out of bounds message: Change warperror to your message
To change blue hex graphics: change Bluehex
To change red hex graphics: change Redhex
To temporarily disable: Change Breakwarp to True
To re-enable: Change Breakwarp to False
To sticky the map: Change Backgroundmode to True

After the battle, please change the settings back to what they were initially to provide continuity for the player.


15-12-15: Incorperated into ModFramework There is now no need for the formation rpy and if running both, the formation mod should be deleted: http://innomenpro.com/forums/index.php?topic=1581.0

8
Mods / [7.2] Ryuvian Missile Mod (+ Weapon Mod Kit)
« on: October 20, 2015, 05:00:48 pm »
Ryuvian Missile Mod

Cast your mind back and remember the first time you were attacked by the unknown cruisers...
You were attacked by really cool glowing blue missiles...
How much more powerful were they than yours?
Would they irradiate the entire Sunrider?
Were they part energy?
...

Why are they yellow on the map?

Why was the explosion just like a PACT frigate's missiles?

Were you... disappointed?

Features!

This mod makes the Ryuvian missiles unique as befits Old Ryuvia

Ryuvian missiles now have a unique missile sprite on the battle map and have a hit animation unique to them!
In addition, to be sadistic they also deal double the flak degeneration!

Picture Time!

Custom Map Sprite
On Hit
Blowing up Black Jack
Shooting the Sunrider

Installation:

Just drop the files in the zips into the Sunrider/game folder

Why is this so special?

This was made as an example for modding a weapon using the framework provided by the functions files attached to this post, it is intended to make weapon modding easy and flexible without needing to edit the fire code and such.

What you can mod?


Pretty much everything!

There are a number of mods that allow dynamic animations but this actually attaches them to the weapon, each weapon could have a different animation for firing and hitting, missile weapons can each have a custom missile sprite. In addition it is also possible to mod flak degeneration where before it was a standard variable by just one line of code.

You can also attach functions to the weapon that trigger in the weapon's fire code. They can fire on miss, hit, both and also directly modify damage and accuracy (depending on what you set).

Suppose you wanted to make a laser that also marked the target, giveing your ships and Ryders a boost in accuracy against that target?

Consider This:
Code: (Evasion debuf on hit) [Select]
def Targetinglaserfunc(*args):
    target = args[2]
    if target.modifiers['evasion'] == [0, 0]:
        target.modifiers['evasion'] = [-10,1]
    else:
        target.modifiers['evasion'][0] -= 10

weapon.functions.append((1,Targetinglaserfunc))

This would apply a -10 evasion penalty to the target when its hit by the weapon that the function was attached to

A more complex possibility would take account of the number of hits and for each one would apply a -10 debuff. This one removes its own debuff after 1 turn, even if there is a 2 turn debuff also affecting evasion, ideal for pulse lasers

Code: (A more complex posibility) [Select]
def Tickdown_evasion()
    if store.Tickdown_evasion_targets != []:
        for item in store.Tickdown_evasion_targets[:]:
            if item.modifiers['evasion'] != [0, 0]:
            #Catch, if its normal then there is nothing you need to do
                item.modifiers['evasion'][0] += 10
                store.Tickdown_evasion_targets.remove(item)
            else:
                store.Tickdown_evasion_targets.remove(item)
    return
   
def Targetinglaserfunc(self, parent, target, damage, hits, accuracy):
   
    if target.modifiers['evasion'] == [0, 0]:
        target.modifiers['evasion'] = [-10*hits,1]
    #If its not been modified before, just do a -10,1
   
    else:
        #If it has an evade debuff longer than 1 turn, flag it on the list to check at start of turn
        if target.modifiers['evasion'][1] > 1:
            target.modifiers['evasion'][0] -= 10
            if not ('Any',Tickdown_evasion) in gated_player_actions:
                gated_player_actions.append(('Any',Tickdown_evasion))
            while hits > 0:
                store.Tickdown_evasion_targets.append(target)
                hits -= 1
        # If it only has a 1 turn duration left, dont worry about it just do a -10
        else: target.modifiers['evasion'][0] -= 10*hits
    return

    weapon.functions.append(1,Targetinglaserfunc)

(Whilst this code should work, I haven't tested it. Its just an example possibility)
The Ryuvian missile mod goes into some more detail in the comments on functions. I didn't want to weigh this down too much in this post

14-12-15: File has been updated to work with ModFramework. Any M52 rpy files should be removed. http://innomenpro.com/forums/index.php?topic=1581.0

9
Mods / [7.2] Movement directions mod + Directional Animations
« on: September 27, 2015, 04:18:25 am »
Movement Directions Mod
(+Counter attack fix)

Have you ever wanted to turn around and never been able to?
Have you ever wanted to look where you're going when moving west?
Then this mod is for you. Using the revolutionary idea of turning, you need never trip over or walk into things behind you!

New: Dynamic animations can detect where attacks are coming from and display as needed!

Features!

When you move a ship to the right or left, if it needs to turn, it will!
If you fire at a ship behind you, you also turn rather than bending light 180*
Now you can retreat by flying away rather than towards the enemy!
Most importantly... Less "beep beep beep, reversing captain!"

This mod detects when you move the ship in reverse and flips the battle label as well as triggering the reversing voiceover.


But there's more!
Bugfix!

Now the Havoc and other Ryders can no longer troll Icari by shooting her when she gets close and then moving away!
This code corrects the counterattack bug making the Phoenix much more useful.


Bonus Feature!
Blindside!

It's always annoying when things hit you when you arn't looking, and now YOU can be annoying.
Attacking an enemy from behind will deal an additional 10% damage and hit with 10% more accuracy.
Be aware though, the enemy is ruthless and will take advantage of this if you try to run...

This is disableable and customizable at the start of the rpy file


Requirements

This mod will run on its own, without any other files but it will be improved by includeing the Function files below (M52 ect).
If not included:
Player ships may start a battle facing west rather than east and enemy ships in the victory screen will be untidy

Use

Just drop the rpy file from the zip in sunrider/game
Download the ModFramework files and drop them in sunrider/game (Modframework.rpy and Modframework_Dynamic animations.rpy)
https://drive.google.com/open?id=0B0D6n1uinVVzTVJ0YzdfM2ZrU2M

Compatibility

This should be entirely compatible with everything.


14-12-15 Edit: The movement fix is now included directly in the ModFramework.rpy here (http://innomenpro.com/forums/index.php?topic=1581.0)
The Movement mod has been updated to work with it and should be overwritten
13-1-16 Edit: Fixed a bug that could cause damage to double... Was an entertaining bug
13/3/16 Edit: Added Dynamic attack/hit animations





10
Mods / [7.2] Energy accuracy Fix
« on: August 15, 2015, 09:52:52 pm »
Just saw on the forums that energy accuracy doesn't work when upgraded so made a small patch for it. Drop it into the Sunrider/game folder and it will auto-activate.

Happy shooting!

The file has now been added into the ModFramework file here: http://innomenpro.com/forums/index.php?topic=1581.0

11
Mods / [7.2] Salvage mod (Partial release)
« on: August 05, 2015, 11:23:48 pm »
Salvageable Ships Mod
This mod is intended to let you salvage destroyed ships for parts and use them for special upgrades.
As of the moment, destroyed ships create covers with all of the special stats for it, but the actual salvaging or store is not implemented.

I have decided to delay finishing this (as I have another three mods I am working on at the same time) so I thought I would post the code that lets ships make wreckage on destruction as it colours the battlefield nicely and is quite fun to use even without salvaging.

All ships (bar ryders and specials) create cover that is colour-coded to its faction.
Depending on the ship, the cover has a set chance to block, a variable HP and when made from large ships, damages ships when it is destroyed by weapons. It also degrades over time with the image changing to match.

Use:
Firstly, drop the files in the Zip into the Sunrider/game folder
Finally, Destroy some ships

Picture Time!
Cover starts off like the far left, the center covers have two turns left and the right covers have only one.

Will this be completed?
Yes, I will finish this but atm I want to finish the first part of my main story mod and my semi-serious story mod first. I am also not happy with using the modified cover image for Ryders and Bases.

Cover Stats:

Cover is different depending on the ship but categories are roughly similar. Cover HP is currently 10% of the Max hp.
Player ryders never leave wreckage because they are recovered and serpaphim/enemyphoenix dont either. Ryuvian ships last longer as they are better quality.

Ryder: Timer: 2-4, Chance to block: 5% (Currently Disabled)
Frigate: Timer: 3, Chance to block: 10%
Cruiser: Timer: 4-6, Chance to block: 15%
Destroyer: Timer: 4, Chance to block: 13%, Detonates if destroyed (200dmg)
Station: Timer: 6-12, Chance to block: 40-60%, Detonates if destroyed. (200dmg)
Carrier: Timer: 5, Chance to block: 30%
Battleship: Timer: 7, Chance to block: 40%, Detonates if destroyed. (200dmg)
Assault Carier: Timer : 5, Chance to block: 40%, Detonates if destroyed. (200dmg)

Compatibility:
Should not interfere with any mods, or with any custom death code.
The M52-53_7.21.1.rpy file improves a function in the M52Functions_7.2.rpy used in the Phoenix Restoration mod and in Saibotleih's mods but steps have been taken so that it should be entirely compatible.

Completed Mod:
The complete mod will contain salvager ships that can be brought to collect wreckage and let you build special items (upgrades, drones and even enemy ships) or sell it for extra money. This must be weighed against the risk of loosing the ships and their haul before the end of the mission meaning you need to be pro-active.

Specifically by the request of John Titor, there will be an organ farming alteration available

Have Fun!

Edit: 14/12/15
The file has been re-orginised to fit the ModFramework file and has a few minor improvements. The basic file is attached and the Functions rpy can be found here: http://innomenpro.com/forums/index.php?topic=1581.0. Any M52function files should be removed

12
Mods / [7.2] Phoenix Restoration Mod
« on: July 08, 2015, 04:37:38 pm »
Phoenix Restoration Mod

Rebuild the Phoenix's booster cradle and expand your tactical capability.
Unleash additional fire-power with changeable laser modes.
Use and weaponize close range warp technology.
Includes new shiny buttons!
Detach and Dock at will.
War-story time!
VVV
V

Includes Functions and Overrides intended to improve mod compatibility and versatility
Thanks to the Maganimous Megillot for doing the upgrade background  far better than I could and the brilliant Saibotleih for inspiration to look at warps and doing awsome buttons

The Ship

The Booster (As enthusiastically explained by Icari) is fast, evasive, well armoured and very durable. It comes with a long range quad laser on par with the Black Jack's lasers and can use the Phoenix's assault cannons. This is not a replacement but an addon. The ships remain two distinct parts.

This may seem very powerful but when the Booster is destroyed, it will need to be re-built and its upgrades are separate to the Phoenix. In addition when the phoenix is detached, the booster becomes helpless and it is unable to defend itself.

The booster requires a number of components that can only be obtained from the Mining Union. To make it available then talk to Icari in the lab after she is brought on-board.

If the booster is destroyed whilst the phoenix is still attached, the phoenix is released but suffers any damage past 0 the booster has taken. It is possible to detach the phoenix at any time and when detached, the booster will retain hate it gained originally but be unable to move or use ability's.

But there's more!

The booster can be modified in three ways (with more to come if there is interest), each requires another talk with Icari:

Ok most of us have played it but its still spoiler
Might be nice to find these for yourself

If the game is started through MoA, then all of these are available from the start.

Linked or Unlinked lasers:
Talk to Icari in the hanger after buying the booster

It is possible to assign each laser its own targeting computer, this means even if one misses, there is still a chance other beams will connect. This reduces its effect against armour but also lets the lasers draw slightly more power.

Repair warp engines:
Talk to Icari in the lab after the wedding crash

Using the Seraphim's computers it is possible to repair the Booster's warp drive and grant the precision to make small micro-jumps. This costs energy, not command points and extends her movement range greatly. In addition, as the enemy are not expecting it, she is not vulnerable to counter attacks whilst warping.

WarpShock (Enable or Disable):
Talk to Icari in your quarters after buying the Warp Drive repair
 
According to Icari's war story it is possible to miss-calibrate a warpdrive and create a localised explosion that inflicts significant damage after warping to surrounding ships... and the booster.
The damage inflicted is dependent on the hexes moved (20* to the Booster, 15* to other ships in a 1hex radius)

Booster Stats

Booster HP : 700
Booster Move range: 6 (15en per hex)
Booster Evasion: 40
Booster Armor: 8
Booster Flak: 20 (Range 1)

Booster Weapons: Quad Laser (70en, 200dmg), Assault Array (30en, 15x15dmg)
Booster Equipment: Warp engine(buyable)(40en + 5 per hex), Detach (20en)

Booster Destruction: Booster is destroyed until re-built. Phoenix survives destruction.
Booster Upgrades: Upgrades are separate to Phoenix, Can be upgraded even when destroyed

Balanceing

The Phoenix is normally restricted to damaging Ryders or badly wounded large ships. With this mod then it becomes more flexible in battle, having a high(ish) damage long range weapon and with upgrades, becoming extremely mobile as well as being able to use two energy pools.

The attempt to balance this is by requiring upgrade money to be split further as without upgrades, the booster weapons will not be significantly more valuable. In addition, although upgrades are not lost when the booster is destroyed, it will need to be re-built with expensive components from the Mining Union before it can be used again.

Costs:

Buy Booster: 800 credits
Repair Booster: 400 credits
Modification 1: 150 credits
Undo Modification 1: -50 credits
Buy Warp Drive: 400 credits
Modification 2: 200 credits
Undo Modification2: 50 credits

Total cost with 1 repair: 1950 credits + any research costs

Picture Time!

Lasers

Movement Range

Warping

Warpshock(look at the booster.hp and the Ryders next to it)



Compatibility:

This mod is designed for beta 7.2, it however overrides a number of core segments of code, These are minor modifications and when the mod is not being used then are un-noticeable except for one. They are mostly used for mod compatibility but they will likely be broken by updates.

Fortunately they are largely simple segments and should be easy to patch in.

There is one modification that will always be visible:

This is used to extend the movement range of ships in battles, to enable the warp to reach more than four hexes. Really this has no effect and is only visible by extending the phoenix and blackjack's movement range.

It should be fully safe for old games and even games loaded from the middle of a battle.

There is a function called boostercheck that can be used to stealth disable/enable the booster for compatibility or balanceing with other mods.

Code: (Use:) [Select]
python:
    if boostercheck() == True: boostercheck(False) # This checks for booster being alive, and disables it.
    if boostercheck() == False: boostercheck(True) # This enables the booster.

Bugs:

With something using this many overrides there are bound to be some problems, I believe I have fixed all of them but if there are any then please let me know.

Installation:

Just Drop the game folder into the Sunrider folder. Everything else should be automatic.

######################################

For Modders:

This is a list of the functions, their uses and their syntax in the M52-53Functions_7.2 file that I have made for use with my main mod and this one. A number of them are very useful for building mods and enhancing what you can do with the game.

As the game updates then I will try and fix any problems these cause and modify the rpy file. Feel free to add this to any mods you make but for the sake of compatibility please do not change it.

Having the file in the game folder will auto-activate the movement range mod and the upgrade mod as otherwise there are pickleing errors. It will also auto-activate the label overrides as they are needed to already be active to use them.

Code: ( Function List:) [Select]

Battle Functions: (Used directly in battle)

    Findships() L 27 (Finds ships within perimeters)

    Reinforce() L170 (Improved create_ship with placement and random+multi ships options)

    Makegrid() L240 (Used with Reinforce to generate a grid for area reinforcing)

    Makehate() L375 (Used to point the enemy at a ship)

    Shiftcover() L392 (Used to move cover and optionally damage ships it collides with)

Battle Overwrites: (Used to enhance battle capability)

    EndPlayerTurn L94 (Lets you run functions at the start of an enemy turn)

    PlayerTurn L486 (Lets you use UI.interact() on your own turns (like the warp weapon))

    Extended Move L567 (Auto) (Lets you move and interact with more than a 4 hex radius)

    TryAgain L742 (Modified battle reset lets you reset custom variables and run functions)

    BattleStart L816 (Modified battle start so you can run a function on battle start)

Story Overwrites:

    Modified Dispatch L433 (auto)(lets you place char buttons from init code, no need for planets or overrides)

    Modified Upgrades L678 (lets you upgrade ships not in player_ships)
   
Story Functions:

    morp() L281 (Lets you check if Captain is moralist or prince, allows you to include a modifier)

    Aff() L301 (Lets you quickly modify affection and morals with shorthand code)

    Locfree() L860 (Lets you check for other characters in a location on the ship map with exceptions and tolerances)


Use of Functions

Battle Functions:

Findships- Line 27

This is used to return a list of ships or run a simple no-argument function on ships it matches. If no ships match its checks then it returns None, so when using it check for that before carrying on.

The returned value is a List.

Code: ( Use:) [Select]
findship(Function,List,Name,Type,Dist,Faction,Hp)

To check for something then use field = value.

Example:
    findship(Name = "Pact Mook", Faction = "PACT") would return all Mooks with the faction PACT

Field Syntax
   
1. Function : defines function to be used, don't use the ()
   Example: findship(Function = function) # This runs the function on all ships in BM.ships

2. List : List to search ships, if not stated, uses BM.ships
   Example: findship(List = listname) # This returns all ships in the stated list, defaults are BM.ships, player_ships, enemy_ships, destroyed_ships

3. Name : returns ships that have a matching name
   Example: findship(Name = "PACT Mook") # Note the " marks as the name is a string

4. Type: returns ships that have a matching type
   Example: findship(Type="Ryder") #defaults are: 'Cruiser', 'Ryder', 'Battleship', 'Frigate', 'Ship', 'Station', 'Carrier', 'Assault Carrier', 'Super Dreadnought', 'Destroyer'

5. Dist: returns ships within a stated distance of location
   Example: findship(Dist = 2,(1,1)) # The first number is the amount of hexes away from the coordinates in the brackets. The coordinates or the number could be already stated variables

6. Faction: returns ships with a stated Faction
   Example: findship(Faction = 'Pirates') # Defaults are 'Player', 'PACT', 'Pirate'

7. HP: returns ships with a hp value that matches
   Example: findship(HP = ('L',400) # This finds ships with hp lower than 400. Qualifiers are 'H'(ship is higher than var), 'E'(ship is equal to hp), 'L'(ship is lower than hp)


Reinforce L170
This is a very powerful version of create_ship that lets you create either a single ship or a group of ships that can be as random as you want, within a supplied location.

Code: (Use:) [Select]
reinforce(((1stcord),(2ndcord)),[[ship,value]],valuecap,moneymod)
                                      or
reinforce(((1,1),(3,3),[[PactMook,1],[PactBomber.2]], 3, 0.2)

This example will make a grid from 1,1 to 3,3.
It then makes random selections from the player supplied list and adds up the values of its selection
When the selection matches the Valuecap then it spawns the ships in the grid, using the money mod if not left empty.

This works very well with makegrid for dynamic ambushes or reinforcements.
makegrid- L 240

This takes a shipname, a preference and a range then returns grid coordinates for a the selected size.

The name can be anything from 'Liberty' to 'Pact Mook', the preference is used to find the most oriented ship with that name (right, left, up, down ('r','l','u','d')

Code: (Use) [Select]
makegrid(shipname, pref, range)
              or
makegrid('PACT Battleship',"r",2) (This would return a 2by2 coordinate for the rightmost battleship, if there are none then it would return "R")

Code: ( Example with reinforce) [Select]
python:
    if check1 == False:
        if sunrider.hp <= sunrider.max_hp/2:
            renpy.say("Alliance Officer","Captain, we are sending you reinforcements")
            reinforce(makegrid('Sunrider','r,2),[[UnionFrigate,1],[AllianceBattleship,2]],2)
            check1 == True

This would show a message from Alliance Officer then spawn either 2 Union frigates or 1 alliance battleship within 2 hexes of the sunrider when the sunrider hp is half or less than half of its max.

This could just as easily be reinforcing a PACT battleship with Elite ryders or cruisers
Makehate- L375

This is used to crudely aim PACT at a ship, it takes the average hate of ships in player_ships and then sets the target ship at average + number.

This is quite flexible, if its set very high then it will focus all firepower on the ship, otherwise it will only encourage them to target the ship. They will still deal with the bigger threat first.

Code: (Use) [Select]
ship.hate = makehate(num)
                or
ship.hate = makehate(400)

If player_ships had 6 ships and hate of 200, the ship affected would be set to a hate of 600

shiftcover- L392

This is used to move cover around the battlefield, it has a chance of moving in any direction or staying still. If it moves then it deals damage to the hex it moves into, if damage is set.

Code: (Use) [Select]
shiftcover(damage)
        example
shiftcover(100)

Will deal 100 damage to ships in the map.
Battle Overwrites:

end_player_turn
Code: (Activate:) [Select]
To activate:
In python block or function:

python:
    BM.end_player_turn = end_player_turn

init python:
    if not 'enemy_actions' in globals(): enemy_actions = []

This runs functions in enemy_actions if the BM.mission matches the qualification.
As it can only run functions with no arguments, its recommended to call a carrier function that calls other functions for you.

Code: (Use:) [Select]
init python:
    enemy_actions.append((BM.missioncheck,Function))
                   example:
    enemy_actions.append((14,dostuff))

The function checks items in enemy_actions, if the variable matches the mission then the function is run. With the function, you cannot use () on the end.
Playerturn

This overwrite lets you insert a function between the battle loops that make up your turn. It is mostly important for weapons involving map tile selection, but you can assign any function to player_actions and have it run once. Look at the warp weapon or detach weapon in this mod for an example.

Code: (Activation) [Select]
python:
    BM.battle = modifiedbattle
    if not 'player_actions' in globals(): globals()['player_actions'] = []


Code: (Usage) [Select]
This should be done from a weapon in most situations.
python:
    player_actions.append((missioncheck,function))

Example:

    player_actions.append((25,Dock2))

Just like the enemy_turn, this runs a function if the mission matches the mission check, however this one also deletes the entry when its run, as player_actions loops without changing the mission turn.
Extended Move
This is auto-used and should not be run directly
TryAgain
This allows you to store variables that need to be reset at the start of a mission and run functions.

Code: (Activation) [Select]
Auto-Activated

Code: (Use) [Select]
Variables:
python:
    [BM.mission,'Variable',Value]
    store.reset_vars.append([BM.mission,'variable',Value])

This checks for the BM.mission and sets the variable to the value.
The variable should be a string ("variable") for this to work.

Functions

This DOES NOT check for BM.mission, it only runs the function. Again this should be a carrier function. Ideally you should remove it from the list after its run, unless its a function that applies globally. If you don't remove it, then it runs on every reset.

Unless there is a specific reason, I recommend using the override below as it will be run anyway if the mission is re-started and it checks for mission names.
BattleStart

Code: ( Activation) [Select]
Not Needed, Auto activates

Effect:
This override is intended to run a function on the start of the mission. It will run any function where the  BM.mission matches its check or its check is "Any". This makes it great for running global functions (such as used to set up the booster).

Code: (To Use:) [Select]

python:
    store.start_funcs.append([BMcheck,func])

BM.check can be a mission name or "Any"
Story Overwrites:

Modified Dispatch:
Code: (Activation) [Select]
Auto Activated

This is used to create buttons on the ship map. It is particularly useful as the buttons will show up whenever the requirements are met, regardless of where you are in the story.

As this can be set from an init block, there is no need to modify files or over-write labels. It is entirely non-invasive

Code: (Use) [Select]
init python:
    chat_labels.append(["eval","name","location",label])


This looks very simple but the eval statement can become complicated very quickly.
Eval should be a string of conditions, like: "mission2_complete == True and mission3_complete not == True"

You should include:

Timeing variables

These are the locators, if they are active or not, you can use them to tell your button if the storyline is set. This can be tricky as there are not that many variables available.

Character Variables

Check to see if character_location is already set, if it is then unless you include this, you will overwrite it.

Talk variables

A variable to check the chat has been seen. If this isn't included then the button will just be constantly refreshed.

Modified Upgrades
This is used to allow ships outside of player_ships to be upgraded. You could use this to upgrade other ships so long as you set the upgrade menu attributes on the ship.

Consider making a master-ship to keep in upgrade_ships and using the start_battle override to copy all the upgrade information onto other ships of the same type to upgrade mercenary ships.

Code: (Activation:) [Select]
Auto activating

Code: (use) [Select]
Ships in upgrade_ships will be auto added to player_ships until upgrades are finished.

Afterwards they will be dropped back into upgrade_ships.

You can also trigger certain things using upgrade_funcs:

upgrade_funcs.upgrade(['evalstring',function])

The eval must be a string which returns true and the function must need to arguments.
Story Functions:

morp:
This is a quick check to see if the captain is a moralist or prince.
You can add a modifier to check the captains inclination if its something that would particularly appeal to him.

If it returns True, captain is being moralist, False for prince

Code: (use:) [Select]
if $morp():
    kay "I belive in being nice"

if $morp() == False:
    kay "I belive in winning"

if $morp(5):
    kay "I belive we should be nice in this situation"

Adding a positive number in the brackets makes morality more likely
Aff
Aff() lets you quickly modify affection and morality using shorthand

Code: (use) [Select]
$aff("asa",1)
would increase asaga_affection by 1

List of names:
    "ava" = Ava
    "asa" = Asaga
    "chi" = Chigara
    "ica" = Icari
    "cla" = Claude
    "sol" = Sola
    "kry" = Kryska
    "cos" = Cossette
    "M" = Moralist
    "P" = Prince
Locfree()
This function can be used to return if anyone is at a position on the ship. Its major use will be for setting chat_labels but it could also be useful for conversations.

Update 17/7/15: Now includes propper buttons thanks to Saibotleih. Zip has been replaced
Update 20/1/15: Fixed an old file that slipped into the last update. If you have a message about start_funcs then just update the m52-53func  file
Update 17/9/15: Updated M52.rpy to prevent possible bugs on restarting a mission and included a dissable/enable function to improve mod compatibility.
Update 14/12/15: Updated to work with ModFramework.rpy found here: http://innomenpro.com/forums/index.php?topic=1581.0

13
Bug Reports [DO NOT POST HERE] / [7.2] Counter attack bug
« on: June 13, 2015, 06:04:12 am »
On mission 2, whenever I move the blackjack into melee range of the havoc, the blackjack glitches with a second blackjack moving from the original hex to the adjacent location and havoc counter attacks but then moves a hex away. It also has energy on the player turn. This does not happen with the sunrider unless it has a melee weapon (I gave it blackjack's weapon list) and the stype is set to Ryder.

(also when useing player AI then the bug doesn't happen)

14
Mods / [Tutorial][7.2] Story mod makeing with explained examples
« on: May 11, 2015, 10:47:49 pm »
Tutorial: Annotated Story mod tutorial

As there is very little information on how to write a mod and incorporate it into the game, I thought I would write this to help anyone who wanted to try it out. This was written at the same time I coded the mod so it should be a good example of how to structure writing a mod as well as the actual coding.

It is written so that no Python needs to be known, but introduces some basic code that will be invaluable for story mods.

The mod we will make is about Kayto and Ava watching a news broadcast about the fall of Cera.

The finished mod will include:

1. Seamless inclusion of the mod into the game
2. A completely stupid plot
3. Kayto and Ava talking
4. Changing sprites and making backgrounds
5. A battle with a modified ship
6. A new minor character
7. A very important choice
8. A new planet!
9. A working store item
10. NO alterations to script or existing files in the game

Attached, there are some rpy files that could be useful. They are designed to enhance modifiability and increase mod compatibility. These non-standard functions are written in red. They are designed to work with 7.2 and using them will possibly break the mod when there are updates, but I will do my utmost to re-code them for each new version.

Ok...

Requirements

The Sunrider game is obvious and the only other requirement is a simple text editor (you can use almost any but I highly recommend Notepad++).

Setup

Setup

What is RenPy and how does modding work?

Renpy is the engine for Sunrider and for our purposes is a very flexible, fantastically complex interpreter. When it is run it scans all the rpy files in its directory and acts on them. As rpy files are simple txt files, editing them is very easy.

The majority of what you will write is written in RenPy language which is very similar to Python crossed with English. Even without knowing python, you can code a great deal.

Before we type anything, you need to know basic formatting. RenPy code is made out of blocks, these can be labels or code segments but a bock is always started with a colon and text in that block is indented 4 spaces. Text should not exist outside a block unless commented out.

Code: [Select]

label Text:
    This is a block
    text is indented by 4 spaces

init:
    An init block is run when Sunrider is launched or a save is loaded

python:
    A python block is comprised entirely of python code

init python:
    This python block is run at the same time as an init block but can only contain python code.

In addition, unless your text editor is set up to convert tabs, donít use them as they will choke RenPy up.

To start, you need to navigate to the Sunrider directory and under game, create a txt file. Rename it as Newsmod.rpy (if you donít have file extensions visible then open it and save as Newsmod.rpy and use the all files format).

Open it up and then select whatever program you want to modify it in.

Now we can start coding. It is best if you type in all the code in code blocks rather than copy/pasting from this tutorial as this will help you familiarise yourself with its structure and make it easier to write your own.


Basic mod building

Labels and Launching
Code: [Select]
label News: # The start of the news block

    ava "Captain! The Alliance is broadcasting a report on the PACT advance and the attack on Cera!" # defined speaker

    "Kayto" "They have to start mobilising soon, put it on the main screen." # Named text


Right, there are four things to look at here.

1. label News: This is the start of the News block and all code that follows it and is indented belongs to that block. When the indent is removed then that block is ended.

2. ava "text" This is text spoken by Ava. ava is defined in the core game files, you will see how to define your own later.

3. "Kayto" "text" This is an alternative way to write text, the first doublequote is the speaker, the second is the text. To have no speaker, just use "text".

4. # Is a comment. Any text to the right of it is ignored by the program so it is useful for documentation of your own code or to remove some temporarily. This is the single exception to breaking indentation.

Now that we have a few lines of text, we can run the mod. Always do this when you try something new and try to follow the code at the same time as you test the mod.

The problem is we have no way to call our label. There are three native ways to integrate the mod into the game without modifying core files and these will come later but for now we will run it manually. There is an additional way to call mods added in the Functions zip.

At the start of the mod, add this:
Code: [Select]
init python: # init blocks run at the start of the game or on loading
    config.developer = True # Enables the dev console
    def Newsmod(): # Defines a function
        renpy.jump("News") # Python version of jump

This init block will run when Sunrider is launched and enable the dev console, to access it, start a new game or load a save and press Shift + O. Then jump to the label News by typing Newsmod() into the console.

The Dev console is very useful as it lets you call functions, jump to points in the story and reload without closing and opening Sunrider. (Shift + R)

--------- Results Breakdown ---------

1. The mod showed us some text but no images
2. The mod kept its background image
3. After the mod ran through its lines it returned to Main Menu

There were no images because they need to be displayed by the show command (which we will cover next). By the same token, the background stayed the same. If you set a background and then jump to an original scene, that scene may not have had to change the background before so you need to do it yourself at the end of your label.

After the mod ran then it returned to Main Menu because after you jump to a label, the game reads down through the text, if it runs out then it returns to menu. This is very bad and you should ALWAYS direct code at the end of a label, even if the label you want is directly below in your file.

To move to a label, use jump lablename, or if your label was called, use return to bounce back to the label it was called from.

Now lets work on images...
Backgrounds and Sprites!
Adding Images

Code: [Select]

label News: # The start of the News Block.

    show bg bridge # show background bridge
   
    show ava uniform altneutral angry with fade: # with fade is how it enters
        xpos 0.5 # X axis location for middle of screen

    ava "Captain! The Alliance is broadcasting a report on the PACT advance and attack on Cera!" # defined speaker
    kay "They have to start mobilising soon, put it on the main screen." # kay is the shorthand for kayto speaking
    "Ava turns to the screen" # With no speaker quotes, this is just placed on the screen.
   
    show ava uniform altneutral angry with move: # moves rather than places, angry because she normally is!
        xpos 0.3
   
    ava "It looks like their showing our escape..."
    return


The major difference between show and scene is that scene wipes all images on that layer, making it useful but not mandatory for backgrounds

show on the other hand just displays the image, making it perfect for sprites

Transformations are added to the image name to control how it appears, and the xpos is the x position on the screen (0 to 1). There are more ways to display an image but it is best to look them up in the RenPy documentation. Remember, showing an image uses a block for position code! That means you need a : and to indent the xpos code.

Code: [Select]
Show:

Syntax:

        show imagename with transformation # Basic show image.

        show imagename with transformation: # Lets you choose where on the x axis it is.
                xpos x

        scene imagename # Displays a scene, wipeing all previous images.

for example;
show ava uniform altneutral angry with fade:
    xpos 0.5
      -or-
scene bridge

Hide:

Hide imagename

#just like show, you can use transforms but you don't need to be so explicit as with show. hide ava will work just as well as hide ava uniform altneutral angry

##Transformations list##

with fade: - Fades in or out
with dissolve: - Dissolves in or out
with pixellate: - pixilates for .5 seconds
with move: moves existing sprite

More at http://www.renpy.org/doc/html/transitions.html#transitions

To find character sprites, look in Sunrider/Game/Character and in most cases just copy the filename and remove the underscores, most of them are already defined.

If you want to change ava from angry to happy, just show her with the new sprite and it will change her, thereís no need to hide her first.

---------Breakdown---------

show is a very easy keyword, just remember that if you are moving labels you may need to change a background before you do!

---------Breakdown---------

Now lets get into the meat of a story mod... Choices

Story-changeing choices!
There is nothing more thrilling than life or death choices, they are the meat of a good mod, its just a shame you arn't writing one!

Ok so follow the drill and edit your code to...:
Code: [Select]
    ava "It looks like their showing our escape..."   

    menu: # Menu block, everything is indented.
        ava "Do you want any popcorn?" # Is not a new block so displays normally
       
        "Yes": # [u]Is[/u] a new block so this is a choice
            kay "Watching us almost get killed does make me hungry..." # Indented
            jump Popcorn # Jump to label Popcorn
       
        "No":
            kay "No thanks, just a diet coke."
            jump Nopopcorn
           
label Popcorn:
    "Ava passes you the popcorn and you settle into your seats as the lights dim"
    jump setupmissionnews
   
label Nopopcorn:
    "Ava shrugs and passes you the bottle. You had better watch those expensive command consoles!"
    "As you settle back into your chairs, the lights start to dim in preparation"
    jump setupmissionnews

Well... That was just thrilling. Controlling trivial things like that makes this feel almost like a stat management game! Fortunately after we go through the code we can move on to something genuinely exciting.

You can use menu: either as the start of a menu block or as an actual label. The difference is that you can jump directly to the choice if you make it a label. This helps if you want to show or hide options by variable.

Code: [Select]
# For this more complex example, we will improve code already in the game
# From the end of beach_episode where you choose who to talk to.
# The game currently has a choice for all the girls and reduces a counter before re-writeing the menu for each of them.
# This means wasted code and people can be picked twice.
# This menu will use variables to track who has been spoken to and remove them from the choices.

#Original code:
    $ beachtalk = 3
   
    menu:
        "Asaga":
            jump beachasaga
        "Chigara":
            jump beachchigara
        "Ava":
            jump beachava
        "Icari and Kryska":
            jump beachicarikryska
        "Sola":
            jump beachsola
        "Claude":
            jump beachclaude
   
label beachasaga:
   
    $ beachtalk -= 1
    $ affection_asaga += 1

#asaga words
#retyped menu

Instead we will do
    $ ava, asa, chi, ica, sol, cla = 0,0,0,0,0 # defines all variables at 0
    $ bt = 3
    menu beachmenu:
            kay "(Now, what should I do?)"

            "Talk to Asaga" if asa == 0 and bt > 0: # if asa equals 0 and bt is greater than 0 return true.

                $asa = 1 # disables this choice.
                $beachtalk -= 1 # beachtalk = beachtalk -1

                jump beachasaga

            "Help Ava with the Barbecue" if beachtalk == 0:
                jump afterbeachtalk

label beachasaga
    text
    jump beachmenu

# This creates a loop that sends you to the label for the character you pick, In this case Asaga and then disables the choice.
# After the beach talk, you are bounced back to the menu.
# When you have spoken to three people then you are given only one choice, to help Ava and the story continues.


This creates compact code that is easy to read and options which cannot be reused.

Setting Variables
Ok, we are near the end of the Basic section, the last thing that is important to a story mod is setting variables.

Variables are vital to track story paths, manipulate menus and pretty much everything interactive. This will not use code for the module but it is still important to learn.

Setting a variable:
Code: [Select]
#There are a number of different variable types, python sorts them for you so all you need to do is supply the information.

# To assign a variable, use name = value.
# Values can be text, numbers, lists of values and a number of advanced things like tupples and dictionarys or other values.

The value type is determined by how the value is added, for example

String = "Hello!" # a string is a string of characters, either letters or numbers. they always have quotation marks to distinguish them from variables
 
integer = 1 # an integer is a whole number, it has no special container characters

float = 1.1 # as integer but not limited to a whole number.

list = [value1, value2] # a list of values

variable = T # variable becomes the value of T

You can use almost any name for a value such as chi or bt in the previous section.

Variables are trivial to set and easy to use, as seen in the menu:

Code: [Select]
T = 1
If T == 1: # if true T is equal to 1, then:
    T += 1 # T is equal to T + 1 (T becomes 2)

#This was used in the menu example to make choices appear and disappear. They do however need to be set before they can be mentioned in the code, otherwise there is an error.

#When you use variables, python sees the content so if you say:

X = 5
T = 2
E = T * X

#python sees

X = 5
T = 2
E = 5 * 2


Thatís all there is to it. Its very simple and with use it will become natural. The only thing to remember is that when python code is used outside a python block, you need to add a $ before it. like $ E = T * X

Now take a break and let it all soak in. If you were observant you might have realised after the menu, we are programming a battle!

Battle Making

Starting a battle!
Does it feel good to be back to your crummy little mod? I hope so because its about to get awesome!

Battles at first seem quite complex so I have split it into three parts. First of all, lets look at the setup code:

Code: [Select]
   

    jump setupmissionnews

label setupmissionnews:
    window hide # Hides message bar
    $ BM.mission = 'news' # tells Battle manager mission name
    call missionnewsinit # sets mission variables
    jump battle_start # starts the battle


Well that was short and disappointing!
Effectively it hides the message bar and sets BM.mission to 'news'. This is important because of how battles work and you should always be careful how you name your missions

Then a label called missionnewsinit is called, call means the label. is jumped to and when it is returned it returns to the point it was called.

Then we jump to the battle_start label, this isnít one we set so we just leave it alone.

Note: If you use a string for the battle ID ($BM.mission = 'whatever') then the formation phase is skipped. If you want your mission to have formation placing then use an integer that is above 12  like ($BM.mission = 67).

The next segment is better I promise.
The Shipyard

In the missionxinit section we set up the enemy ships, place our own on the map and make any cover or set music. Making your ships placeable will come later.

Here is the code, remember donít be lazy!
Code: [Select]
    jump battle_start # starts the battle
   
label missionnewsinit:
       
    python:                                # /
        zoomlevel = 1                      #| This code is used in every mission init
        enemy_ships = []                   #| It just cleans the battlefield
        destroyed_ships = []               # \

        sunrider.set_location(1,1)    # Comment out ships that you havenít met yet!
        #blackjack.set_location(2,2)  # These are coordinates to place the ship
        #liberty.set_location(3,3)
        #phoenix.location = None   # Set location to None to use in battle, there is a better way to do this but its simple for now
        #bianca.set_location(5,5)   
        #seraphim.set_location(6,6)
        #paladin.set_location(7,7)

        BM.xadj.value = 872 # set battle view, doesnít really need to be changed ever
        BM.yadj.value = 370

        create_ship(MissileFrigate(),(13,5)) # This creates a ship with default armament
        create_ship(MissileFrigate(),(15,5),[PactFrigateMissile()]) # Creates a modifed ship
        enemy_ships[-1].max_en=80
        enemy_ships[-1].boss = False
        enemy_ships[-1].Faction ='PACT'
        enemy_ships[-1].name = 'Damaged Missile Frigate'
        enemy_ships[-1].max_hp = 100
        enemy_ships[-1].hp = 30
       
        create_cover((8,4)) # create cover
        create_cover((1,1))

    $ PlayerTurnMusic = "music/Titan.ogg" # set music
    $ EnemyTurnMusic = "music/Dusty_Universe.ogg"
    return #goes back to setupmissionnews where it was called

Ok then, lets work through this.

The label moves immediately into a python block, but thatís not really important here, lets break it up.

Code: [Select]
        zoomlevel = 1                      #| This code is used in every mission init
        enemy_ships = []                   #| It just cleans the battlefield
        destroyed_ships = []               # \

The first three commands are nothing of import, you can just use them time and again without needing to change them.

Code: [Select]
        sunrider.set_location(1,1)    # Comment out ships that you havenít met yet!
        #blackjack.set_location(2,2)  # These are coordinates to place the ship
        #liberty.set_location(3,3)
        #phoenix.set_location(4,4)   # Set location to None to not use in battle
        #bianca.set_location(5,5)   
        #seraphim.set_location(6,6)
        #paladin.set_location(7,7)

The second set are placing your own ships. You are running a python function embedded within your ship objects (donít worry if you donít understand, its not important right now) and telling it to use the co-ordinates you give it.

sunrider.set_location(1, 1) will set the sunrider to the top left corner.

The ship location doesnít change when the battle is won so even if the ship is commented out, if you have already met it, it will spawn there. To not use the ship in a battle, set location to None (no quotes, this is a keyword).

For a better way to remove the ship from battle, then remove it from player_ships and BM.ships. This will be dealt with later.

Code: [Select]
        BM.xadj.value = 872 # set battle view, doesnít really need to be changed ever
        BM.yadj.value = 370
Again these two are inconsequential, just where the battlemap view starts.

Code: [Select]
        create_ship(MissileFrigate(),(13,5)) # This creates a ship with default armament
        create_ship(MissileFrigate(),(13,5),[PactFrigateMissile()]) # modify a ship
        enemy_ships[-1].max_en=100
        enemy_ships[-1].boss = False
        enemy_ships[-1].Faction ='PACT'
        enemy_ships[-1].name = 'Damaged Missile Frigate'
        enemy_ships[-1].max_hp = 100
        enemy_ships[-1].hp = 30
The first line in the code tells the game to make and place a MissileFrigate at 13, 5. Its very easy to use and in the second file attached, there is a list of names you can use for it.

create_ship() is the function name, it is called by having brackets on the end.
MissileFrigate() is the ship object, the brackets are important. You can find a list in the library rpy.
[PactFrigateMissile()] # This is a list of weapons that are assigned to the ship. If not used then it uses whatever default is assigned to it. Again the () is important.

The second block is making another ship but modifying its attributes. It uses enemy_ships[-1] to access the last ship in that list (this is the last as it was most recently created and all enemy ships go in this list).

Code: [Select]
        create_cover((8,4)) # create cover at 8, 4
        create_cover((5,4))

These commands make cover as found when you first meet Sola.

Code: [Select]
    $ PlayerTurnMusic = "music/Titan.ogg" # set music
    $ EnemyTurnMusic = "music/Dusty_Universe.ogg"
    return #goes back to setupmissionnews where it was called
The last commands set the music on each turn and the return command bounces us back to setup as this label was started by call lablename. Just note that they intentionally out of line from the python block to show that the python code needs to have $ in front.

Now we have only one more segment to cover and we can start the battle!
Begin the Fight!
Code: [Select]
label missionnews: # This is the actual mission, very simple
   
    $ BM.battle() # Calls your turn and waits for a response
    if BM.battlemode == True: #When set to false battle ends
        jump missionnews #Loop Back
    else:
        jump after_missionnews

Ok first note that missionnews was not called by us. This is why we need to be consistent with the naming on our labels, otherwise BAD THINGS happen.

This is just a basic loop, whenever an action occurs, it runs and if the battle is over, it jumps to after_missionnews, otherwise it loops again.

If you want to script the battle, then enter it between the $BM.battle() and if BM.battlemode == True: lines. This will be dealt with in depth later. For now, we have very little interaction with this code.

The final thing to note is the jump if battlemode isnít True, that is where you go after the battle. The victory screen comes before then.

Now just add this to the end of the code...
Code: [Select]
label after_missionnews:
    "Newsperson" "So at least some of the Cera military are known to have survived the PACT assault"
    kay "So... At least someone in Cera may know we survived"
    ava "But PACT are more than likely to come after us now we're in the public view."
    jump dispatch # shipmap
And you are ready to launch your mod!

Do so now to check it all works, have a break and then head on to Integrating the Mod. If it doesn't work then just check the code against mine and try to find any spelling mistakes. The error screen on RenPy is quite good at pointing out where the problems are.
Complete Battle Code
Code: [Select]
label setupmissionnews:
    window hide
    $ BM.mission = 'news'
    call missionnewsinit
    jump battle_start

label missionnewsinit:
       
    python:                                # /
        zoomlevel = 1                      #| This code is used in every mission init
        enemy_ships = []                   #|
        destroyed_ships = []               # \

        sunrider.set_location(1,1)    #comment out ships that you haven't met yet
        #blackjack.set_location(2,2)  # These are coordinates to place the ship
        #liberty.set_location(3,3)
        #phoenix.set_location(4,4)   #set location to None to not use in battle
        #bianca.set_location(5,5)
        #seraphim.set_location(6,6)
        #paladin.set_location(7,7)

        BM.xadj.value = 872 # set battle view
        BM.yadj.value = 370

        create_ship(MissileFrigate(),(13,5)) # create a ship
       
        create_ship(MissileFrigate(),(15,5),[PactFrigateMissile()]) # modify a ship
        enemy_ships[-1].max_en=80
        enemy_ships[-1].boss = False
        enemy_ships[-1].Faction ='PACT'
        enemy_ships[-1].name = 'Damaged Missile Frigate'
        enemy_ships[-1].max_hp = 100
        enemy_ships[-1].hp = 30
       
        create_cover((8,4)) # create cover
        create_cover((5,4))

    $ PlayerTurnMusic = "music/Titan.ogg" # set music
    $ EnemyTurnMusic = "music/Dusty_Universe.ogg"
    return #goes back to setupmissionnews where it was called
   
label missionnews: # This is the actual mission, very simple
   
    $ BM.battle() #Continues battle
    if BM.battlemode == True: #When set to false battle ends
        jump missionnews #Loop Back
    else:
        jump after_missionnews

Hooking into the game.
How?
We already know to get to a label you use a jump command. However it is a bad idea to alter the core files as these will be wiped whenever the game is updated and could clash with other mods.

Without being able to alter the core scripts, you need to look at init blocks.

These arenít called by the script but automaticly run whenever the game launches so you can use them to sidestep into the game through one of three (four) ways.

1. You can Hijack a label
2. You can make your own Planet.
3. You can make an item in the Store.
4. You can add a talk button to the Sunrider map.

Hijacking a label. is good for minor inserts or edits to the story, it is useful to redirect a jump or make text/code changes. This is entirely seamless but has the potential to cause mod conflicts (although the chances are low and there are methods to make this very unlikely.

Making your own planet is quite simple and although there is a lot of code, especially if you use it to show a transit screen, I have made a rpy file that lets you just plug in names of labels and map images that is suited to most cutscenes. The downside to using only this method is a planet will appear on the galaxy map and the player will have no idea why, or even miss it entirely.

Making an item for the store is a nice blend of the two but it is flawed as a method of including your own content. It is extremely adaptive as when someone buys an item you can trigger any number of functions, variable changes and jump to labels but it is also hard for the player to see what will happen and hard to alert them to it. It is best used as an enabler such as setting a talk on the ship map to active or revealing a planet on the galaxy map. This may require a lot of python (if you make your own events) or as little as setting a variable.

Adding a chat button to the ship map is the most unobtrusive method of adding mod content but it also can be hard to 'aim' and requires basic understanding of python. This should really be used as a do-once option rather than a more persistent planet.

So for pure story edits, use hijacks. For additions to the story, a combination of hijacks, store items, planets and chat buttons works very well.

In the following segments, there are examples of each method, however they will need to be adjusted to fit your mod. Everything you need to change is something you will have seen. Try and get each method to work and then combine them
so you use all three (or four) at once.

If you cant do it then have a look at the finished mod code at the end of the post and see if you can find how it was done, then try and do it without copy-pasteing. Feel free to ask questions as I am sure many people on the forum will be happy to help.

Setting Attributes
Before going further, attributes will need to be covered.

Unfortunately as init blocks are ran as the game starts, they will overwrite standard variables, therefore resetting your mod. Object Attributes however are a more substantial type of variable. They are available throughout the mod (even in functions without being declared global) and are easy to check for.

Attributes can be any of the variable types including functions and are referenced by object.attribute. These are what we were tweaking in the battle init section.

RenPy games natively have a store object so assigning variables to that is a convenient thing to do.

You would add it like this.

Code: [Select]
python init:
    if not hasattr(store,'variablename'): # This triggers if the attribute has not been set
        store.variablename = value # sets the attribute
        #This space can also be used to create objects like planets or store items

Of course what variables and objects you need to store depends on how you intend to implement the mod and variables.
Label Hijacking
You can use a RenPy config code to replace a label with your own like this.

Code: [Select]
init python:
    config.label_overrides['original label'] = 'your label'
Its exactly that simple.

Example:

init python:
    config.label_overrides['checkformissions'] = 'checkformissionsmod'

label checkformissions:
    #text

    kay "Well, more money for us. I can't complain about smashing up pirates for some quick credits. What about the other one?"
   
    ava "The last mission could be more complicated."

    ava "The Union has lost a number of transport ships recently, they were long-range tug ships that were intended to move small asteroids and carry them through warp jumps. These ships, had an advanced homing beacon on-board and have been tracked to deep space, where they appear to be moving under engines only."

    # text

    $ knowaboutmod = True

    jump afterbeachcarry
Planetary Construction
A planet is simply an object with certain attributes, much like a ship. If it is detected when the galaxy map opens, it is placed on there and when clicked it jumps to a label.

This can be anything from a text label. to a mission selection or briefing.

Code: [Select]
init python:
    Planet("MODPLANET", "News", x, y, "condition")

MODPLANET is the name of the planet on the galaxy map
"News" is the label. it loads on clicking
x + y are coordinates for the map
"condition is If true, planet is visible"

Put simply, "condition" checks everything in the quotes, and if it returns True, the planet is displayed.

Code: ( example) [Select]
Knowmod = 1
TalkedAva = True
TalkedChigara = False

    Planet("EXAMPLE", "Label", x, y, "Knowmod == 1") # if Knowmod is 1, planet is visible.
    Planet("EXAMPLE", "Label", x, y, "TalkedAva== True") # if TalkedAva is True, planet is visible.
    Planet("EXAMPLE", "Label", x, y, "True") # Planet is always visible
    Planet("EXAMPLE", "Label", x, y, "TalkedAva == True and TalkedChigara == True") # Because TalkedChigara is False, even though TalkedAva is True, planet is not useable.

#You can use the variable name on its own if it is set to True
    Planet("EXAMPLE", "Label", x, y, "TalkedAva and TalkedChigara") # is the same as the previous example.


At the moment this is quite unimpressive, clicking the planet just sends you to the News label and starts the mod.

I have created a quick method of making the sunrider jump screen but if you dont want to use the rpy files (although this one will not break with updates), you can still set it manually.

If you want to  have missions spawning there then instead of directing it straight to News, do the following code:

(Manual)

Code: ( Mission Selection Screen:) [Select]

init python: # create the planet

    Planet("ModPlanet", "Modplanetwarp", 1370, 450, "True")
#   Planet("PLANET NAME", "LABEL", X, Y, "CONDITION")

label Modplanetwarp: # This is the label in the planet object. RENAME THIS

    $ map_back = "Modplanetback" #plug in the label to go back (below)
   
    if modmission == 0: # set a variable if you want the mission(s) to be available, you can have three missions visible at once but it dosent line up well.
        $ galaxymission1 = True
        $ mission1 = "Newswarpto" #Mission Label (Where you want to go if its clicked)
        $ mission1_name = "Memory: Escape from Cera."

    else: # If the variable isent matched then these missions are used (all set to false atm)
        $ galaxymission1 = False
        $ mission1 = None
        $ mission1_name = None
    jump showmodplanet

There are a number of things here that you need to replace to let this work for your own mod.

1. Use your own label name
2. Use your own map_back (explained in the next code block)
3. Use your own mission variable
4. Set your mission name and mission label

Code: (Show planet code:) [Select]
#This is triggered when you click the planet on the galaxy map

label showmodplanet:       
    scene bg black
    show galaxymap:
        zoom 1 alpha 1
        parallel:
            ease 0.5 alpha 0
        parallel:
            ease 1 xpos -13710 ypos -5190  zoom 10
    show "Map/Farport.jpg": # *** This is the planet image.
        zoom 0.0268041237113402
        xpos 1071 ypos 519 alpha 0
        parallel:
            ease 1.1 alpha 1
        parallel:
            ease 1 zoom 1 xpos 0 ypos -430
    pause 1
    show "Map/farport_info.png": #*** This is the infobox (It can be skipped if you want)
        xpos 1098 ypos 200
    call screen map_travelto
    with dissolve

label planetback: # this is used to return from the planet map to the galaxy map
    hide "Map/farport_info.png" # *** This is the infobox
    scene bg black
    show  "Map/Farport.jpg": # *** This is the planet image.
        zoom 1
        xpos 1370 ypos 450 alpha 1
        parallel:
            ease 0.5 alpha 0
        parallel:
            ease 1 zoom 0.0268041237113402 xpos 1490 ypos 725
    show galaxymap:
        xpos -14900 ypos -7250 zoom 10 alpha 0
        parallel:
            ease 1.1 alpha 1
        parallel:
            ease 1 xpos 0 ypos 0 zoom 1
    pause 1
    call screen galaxymap_buttons

Right then... With this you need your own custom labelnames and you need to replace the planet image and the infobox image. These are marked in the code block with ***

Other than that its very simple and can largely be left alone.

Code: (Warpout code:) [Select]
label Newswarpto:
   
    $ Random = renpy.random.randint(1,9) # sets random integer for below text.

    if Random == 1:       
        scene space back1 
    if Random == 2:       
        scene space back2 
    if Random == 3:       
        scene space back3 
    if Random == 4:       
        scene space back4 
    if Random == 5:       
        scene space back5 
    if Random == 6:       
        scene space back6 
    if Random == 7:       
        scene space back7 
    if Random == 8:       
        scene space back8 
    if Random == 9:       
        scene space back9 
                                                     
    show sunrider_warpout_standard:                   
        xpos 700 ypos 350                             
    with dissolve                                     
                                                     
    pause 1.0                                         
                                                     
    play sound "Sound/large_warpout.ogg"             
    show sunrider_warpout_standard_flash:             
        xpos 426 ypos 0 alpha 0                       
        linear 0.1 alpha 1                           
        linear 0.1 alpha 0                           
    show sunrider_warpout_standard out:               
        xpos 700 ypos 350                             
        ease 0.2 xpos 200 ypos 300 zoom 0             
                                                     
    pause 1.0                                         
                                                     
    show "Map/Farport.jpg": # *** Insert planet image here
        ypos 0
        ease 1.5 ypos -120   
    with dissolve                                   
    pause 1                                           
                                                     
    show sunrider_warpout_standard out:               
        xpos 2300 ypos 1200 zoom 2                   
        ease 0.2 xpos 1000 ypos 500 zoom 0.5         
    pause 0.2
    play sound "Sound/large_warpout.ogg"
    show cg_legionwarpin_missilefrigate_warpflash:   
        zoom 1.5 xpos 1550 ypos 750               
    show sunrider_warpout_standard
    pause 2.0
    jump News

With this, all you need to do is replace the label with the one for your planet, replace the planet image (again marked by ***) and replace the jump at the end with your mod's label.

And its done, the Sunrider now warps to the planet when the mission is clicked and you can start your scene.

This is quite substantial code and it is quite bulky to put in a mod, especially if you have more than one planet to deal with. Fortunatly a lot of the code is just left alone and dosent change between planets.

In the Functions.zip there is a Rpy called Transit that lets you do all of this with under 20 lines of code, not includeing planet definition.


Code: [Select]
#The select mission part is very simmilar to the previous code.

    Planet("ModPlanet", "Modplanetwarp", 1370, 450, "True")
#   Planet("PLANET NAME", "LABEL", X, Y, "CONDITION")

label Modplanetwarp: # Have one of these for each planet
    image modbg2 = "Map/Farport.jpg"
    image modinfo2 = "Map/farport_info.png"

label missionselectionlabel: #Make a custom label for your planet

    $ map_back = "planetback" # No need to touch this
   
    if modmission == 0:
        $ galaxymission1 = True         
        $ mission1 = "Missionselected" #Label to jump to for mission, make one per mission.
        $ mission1_name = "Memory: Escape from Cera." #mission name

    else: # If the variable isent matched then these missions are used (all set to false atm)
        $ galaxymission1 = False
        $ mission1 = None
        $ mission1_name = None
    jump showmodplanet

label Missionselected:
    $ modjumplabel = "News" # This is the label for the mod content.
    jump transit

To use this:
1. Create one label per planet defineing its image and infobox (modbg2 and modinfo2)
2. Create one label per mission setting modjumplabel to its label
3. Set mission variables

The other bits of code are tucked away in transit.rpy

Store method
The store has been set up to automaticly add storeobjects found in store.mod_items. This is a godsend, look at this code!

Code: [Select]
init python:

    class Activeatemod(StoreItem): # needs to be a unique name
        def __init__(self):
            StoreItem.__init__(self)
            self.id = 'Activatemod' # needs to be unique, I normally use the class name
            self.display_name = "Click to play" # name in store
            self.cost = 2000            #cost
            self.tooltip = "Clicking me does something"
            self.visibility_condition = "Test == 1" # (Visible if Test is equal to 1) If something is true or it is equal to True, then this is visible.
            #This can be set to a false value in the buy function so it is not re-buyable.
           
        def buy(self):   #buy function         
            global Test # This needs to be here so that the Test variable updates
            Test = 0 # This would let you make the item invisible
            store.variablename = value # update an object attribute
            Event() # This is triggered when the object is brought

    if not 'Activeatemod' in store.mod_items: store.mod_items.append(Activeatemod)
    # This line adds the item into the mod_items list if its not in there already.


When it is brought, the buy function triggers and activates all the things indented under it.

This can therefore be used to trigger something... like a planet appearing by buying a map.

[/code]

So to modify Sunrider's Missiles to not take up ammo but increase energy cost, you could put:
Code: [Select]
    def buy():
        sunrider.weapons[2].ammo_use = 0 # 3rd weapon in sunrider.weapons list
        sunrider.weapons[2].energy_use = 50

The store is an incredibly flexible and usefull tool, an example of modifying a weapon is below

Code: (Example of modifying a weapon) [Select]
init python:

    class ModifyGG(StoreItem): # unique class ID
        def __init__(self):
            StoreItem.__init__(self) # same as class ID
            self.id = ModifyGG
            self.visibility_condition = 'bianca.weapon[1].energy_use == 60 and bianca.max_en >= 120' # visible if bianca max energy is greater than or equal to 120 and Bianca's 2nd weapon (gravity gun) has an energy cost of 60.
            self.display_name = 'UPRADE: GRAV GUN'
            self.cost = 800
            self.tooltip = 'This upgrade uses Bianca's upgraded reactor to channel excess power into her gravity gun'
            self.variable_name = None
            self.max_amt = 0
       
        def buy(self):
            bianca.weapon[1].energy_use = 40 # sets the energy_use attribute of weapon 2 in bianca.weapons to 40, thus removing itself from the store as its visibility condition is now not met.
           
    store.mod_items.append(ModifyGG) # adds the item to the store.
ChatButtons:

This is a modification of the dispatch label via a label hijack that is auto activated when the M52-54.rpy file is in the Sunrider/game folder.

It lets you add chat buttons to the sunrider map when certain conditions are met and is ideal for introduceing storys or letting the player talk to people at will.

Code: (Use:) [Select]
init python:
    chat_labels.append(['Condition','char','location','label'])

    #Example:

    chat_labels.append(['mission11_complete and ica_location == None and chi_location == None and Boostertalk == False','ica','lab','Rebuildbooster'])

Ok... This can look complicated (and it can be extreemly complicated) but it is fairly simple when broken down into bits.

chat_labels.append([]) # This adds an entery into the chat_labels list. it is a list as you can tell by the []
'condition' # This works the same as the planet visibility and mission visibility. (See below)
'Char' # This is the same shorthand as the text code we saw in the first few lines. It tells you what button to show and the variable to modify
'Location' # This is the location on the map to display the button
'Label' # This is the label to jump to.

The Locations are:

'captainsloft', 'sickbay', 'messhall', 'bridge', 'engineering', 'lab', 'hangar'

Conditions:

This is a bit tricky... When working out the conditions then you want:

1: Timeing variables (These can be your own, or they could be mission**_complete and such.
2: Character variables (so you dont overwrite any existing buttons, think ica_location == None)
3: Stopping variables (A variable set when you use the label so it ise't refreshed constantly)


The Ending

Well... If you have managed to read through this entire tutorial I am deeply impressed and if you actually managed to piece the mod together and it helped make a mod, I am ecstatic!

The complete mod code is here: (although it doesnít have a label. hijack because I donít know where you would be in the story and it doesn't have a chatbutton code)

Spoiler
Code: [Select]
init python: # init blocks run at the start of the game or on loading
   
    def Newsmod(): # Defines a function
        renpy.jump("News") # Python version of jump
       
       
    class Map(StoreItem): # needs to be a uniuqe name
        def __init__(self):
            StoreItem.__init__(self)
            self.id = 'ModMap' # needs to be uniuqe, I normaly use the class
            self.display_name = "Mystry Map" # name in store
            self.cost = 0            #cost
            self.tooltip = "Clicking me enables the News planet"
            self.visibility_condition = 'store.Hasmap == False'
           
        def buy(self):           
            store.Hasmap = True # Hasmap is a attribute of the store object
       
    if not hasattr(store,'Hasmap'): # This sets Hasmap to False if it dosn't exist
        store.Hasmap = False
        modmission = 0
        store.mod_items.append(Map) # This puts the Map item in store.mod_items

    Planet("ModPlanet", "planetwarp1", 1370, 450, "store.Hasmap == True")

label planetwarp1:
    image modbg2 = "Map/Farport.jpg"
    image modinfo2 = "Map/farport_info.png"
    $ modjumplabel = "News"
    jump missionselectionlabel

label missionselectionlabel: # You can call this whatever you want

    $ map_back = "planetback" #plug in the label to go back (below)
   
    if modmission == 0: # set a variable if you want the mission(s) to be available, you can have three missions visible at once but it dosent line up well
        $ galaxymission1 = True         #/ and is not suited to use with flexicode atm
        $ mission1 = "transit" # Jump to label for mission
        $ mission1_name = "Memory: Escape from Cera." #mission name

    else: # If the variable isent matched then these missions are used (all set to false atm)
        $ galaxymission1 = False
        $ mission1 = None
        $ mission1_name = None
        jump showmodplanet
       
label showmodplanet:       
    scene bg black
    show galaxymap:
        zoom 1 alpha 1
        parallel:
            ease 0.5 alpha 0
        parallel:
            ease 1 xpos -13710 ypos -5190  zoom 10
    show modbg2:
        zoom 0.0268041237113402
        xpos 1071 ypos 519 alpha 0
        parallel:
            ease 1.1 alpha 1
        parallel:
            ease 1 zoom 1 xpos 0 ypos -430
    pause 1
    show modinfo2:
        xpos 1098 ypos 200
    call screen map_travelto
    with dissolve

label planetback: # this is used to return from the planet map to the galaxy map
    hide modinfo
    scene bg black
    show modbg2:
        zoom 1
        xpos 1370 ypos 450 alpha 1
        parallel:
            ease 0.5 alpha 0
        parallel:
            ease 1 zoom 0.0268041237113402 xpos 1490 ypos 725
    show galaxymap:
        xpos -14900 ypos -7250 zoom 10 alpha 0
        parallel:
            ease 1.1 alpha 1
        parallel:
            ease 1 xpos 0 ypos 0 zoom 1
    pause 1
    call screen galaxymap_buttons
   
label transit:
   
    $ Random = renpy.random.randint(1,9) # sets random integer for below text.

    if Random == 1:       
        scene space back1 
    if Random == 2:       
        scene space back2 
    if Random == 3:       
        scene space back3 
    if Random == 4:       
        scene space back4 
    if Random == 5:       
        scene space back5 
    if Random == 6:       
        scene space back6 
    if Random == 7:       
        scene space back7 
    if Random == 8:       
        scene space back8 
    if Random == 9:       
        scene space back9 
                                                     
    show sunrider_warpout_standard:                   
        xpos 700 ypos 350                             
    with dissolve                                     
                                                     
    pause 1.0                                         
                                                     
    play sound "Sound/large_warpout.ogg"             
    show sunrider_warpout_standard_flash:             
        xpos 426 ypos 0 alpha 0                       
        linear 0.1 alpha 1                           
        linear 0.1 alpha 0                           
    show sunrider_warpout_standard out:               
        xpos 700 ypos 350                             
        ease 0.2 xpos 200 ypos 300 zoom 0             
                                                     
    pause 1.0                                         
                                                     
    scene modbg2:
        ypos 0
        ease 1.5 ypos -120   
    with dissolve                                   
    pause 1                                           
                                                     
    show sunrider_warpout_standard out:               
        xpos 2300 ypos 1200 zoom 2                   
        ease 0.2 xpos 1000 ypos 500 zoom 0.5         
    pause 0.2
    play sound "Sound/large_warpout.ogg"
    show cg_legionwarpin_missilefrigate_warpflash:   
        zoom 1.5 xpos 1550 ypos 750               
    show sunrider_warpout_standard
   
    pause 2.0
    $renpy.jump(modjumplabel)
   
label News: # The start of the News Block.

    $Modmission = 1
   
    show bg bridge
   
    show ava uniform altneutral angry with fade:
        xpos 0.5

    ava "Captain! The Alliance is broadcasting a report on the PACT advance and attack on Cera!" # defined speaker
    kay "They have to start mobilising soon, put it on the main screen."
    "" "Ava turns to the screen"
    show ava uniform altneutral angry:
    ava "It looks like their showing our escape..."   

    menu: # Menu block, everything is indented.
        ava "Do you want any popcorn?" # Is not a new block so displays normally
       
        "Yes": # Is a new block so this is a choice
            kay "Watching us almost get killed does make me hungery..." # Indented
            jump Popcorn # Jump to label Popcorn
       
        "No":
            kay "No thanks, just a diet coke."
            jump Nopopcorn
           
label Popcorn:
    "Ava passes you the popcorn and you settle into your seats as the lights dim"
    jump setupmission22
   
label Nopopcorn:
    "Ava shrugs and passes you the bottle. You had better watch those expensive command consoles!"
    "As you settle back into your chairs, the lights start to dim in preparation"
    jump setupmission22
   
label setupmission22:

    window hide
    $ BM.mission = '22'
    call mission22init
    jump battle_start

label mission22init:

    python:
        zoomlevel = 1
        enemy_ships = []
        destroyed_ships = []

        BM.xadj.value = 872
        BM.yadj.value = 370

        create_ship(MissileFrigate(),(11, 7))
        create_ship(PhoenixBoaster(),(11, 6))

        #sunrider.set_location(7, 7)

    $ PlayerTurnMusic = "music/Titan.ogg"
    $ EnemyTurnMusic = "music/Dusty_Universe.ogg"
    return

label mission22:

    $ BM.battle()
    if BM.battlemode == True:
        jump missionnews
    else:
        jump aftermission22

label after_mission22:
    "Newsperson" "So at least some of the Cera military are known to have suvived the PACT assault"
    kay "So... At least someone in Cera may know we survived"
    ava "But PACT are more than likely to come after us now we're in the public view."
    jump dispatch

Good Luck and Happy Modding

If there's anything someone thinks should be added, let me know!

Advanced Stuff:

Battle Modding:
Understanding Battles:
The entire player turn is a loop that continues until the player looses or it is ended by clicking the end turn button. At that point, it goes through the enemy_turn code before returning back to the player code.

This means when you play the battle, every click you make starts the loop again.

Therefore we need some way to distinguish between turns.
Turn Gates:
Code: ( Basic battle code) [Select]
label Mission99:

    $ BM.battle()
    if BM.battlemode == True:
        jump mission99
    else:
        jump aftermission99

If you add $turncount = 0 to the init label before the mission you can then do this:

Code: (Turn Gate:) [Select]
label mission99:
    if turncount != BM.turn_count:
        $ turncount = BM.turn_count

        if turncount == 1:
            do stuff

    python:
        if len(BM.ships) > shipcount: shipcount = len(BM.ships)
        if len(BM.ships) < shipcount:
            shipcount = len(BM.ships)
            renpy.say("Ava", "Ship destroyed")

    $ BM.battle()
    if BM.battlemode == True:
        jump mission99
    else:
        jump aftermission99

Anything in the turn-gate gets run once per turn, you can seperate turns by adding a check for what turn it is.

Anything outside of the turn-gate, like the second code block is run with every click of the mouse. This is good for checking the state of the battle and responding to it. In this case, Ava makes a comment about a ship being destroyed when the number of entries in BM.ships is less than it was.

(Assume shipcount is set to 0 or something in the battle-init label)

The M52-53._7.2_1.1.rpy file in functions zip allows you to have a function that runs at the start of every turn of missions. This is most usefull for global mods.
Showing images in battle:
Whilst there is no real difference in speaking in a battle (with the exception of speaking whilst in a python block), to display an image then you need to include a small bit of extra code.

Code: [Select]
label mission99:
    if turncount != BM.turn_count:
        $ turncount = BM.turn_count
       
        show kryska plugsuit alt neutral frown onlayer screens with dissolve
        kry "I AM SPEAKING IN SPACE"
        hide kryska onlayer screens with dissolve

Basically whenever you want an image to appear or dissapear in a battle, you need to specify onlayer screens.

Speaking and showing images in Python

This is all possible inside a python block, it is however slightly different.

Speaking:

There are two ways to do this, either useing the shorthand as normal or useing the renpy.say function. For most purposes you can use the shorthand (as it is actually a function) and just need to make a slight change in format.

Shorthand:

This is by far the easiet way to talk in a python block.

Code: (Shorthand) [Select]
python:
    ava("... Idiot")

Note that you need to include brackets because this is a function, and either single or double quotes to tell python that this is a string. Other than that it is very similar to the standard shorthand text we covered in the first two mins of this tutorial.

The other method is not normally requried but it can be usefull if you dont want to define a speaker and want a one-off character. It is usefull to know as it helps you understand renpy.equivilents with code you have already used.

Renpy say equivalent:

Functions are basicly small snippits of code that are recyclable, in short we have been useing disguised functions whenever we make a character speak. This is what they look like under the skin!

(have a look at: http://www.renpy.org/doc/html/statement_equivalents.html for more)

Code: (renpy say equivalent) [Select]
python:
    renpy.say("character","words") # This will display the name character saying words
    renpy.say("kryska","stuff")
    renpy.say(ava,"idiot") # The shorthands work as well, but why you would use them if their already set up, i'm not sure.

Its very easy to use this, but you must make sure you have the quotes.


Displaying images in python blocks:

Unlike displaying text, images can look quite complex to display. It follows the same patterns but there are extra variables and to get it to fade, you need to add another command.

Code: [Select]
python:
    renpy.show('imagename', at_list = [], layer = 'screens')
   
    renpy.hide('image', layer = 'screens')

This show command is harder to place, the image name can be either the physical location of the image, or it can be a defined image. the at_list variable is used for positioning and transforms. (see: http://www.renpy.org/doc/html/transforms.html)

These can be created but they are still a slightly clunky way to place an image.

Code: (Example:) [Select]
python:
    renpy.show('kryska plugshit altneutral frown' at_list = [topleft], layer = 'screens')
    renpy.with_statement(dissolve)
   
    renpy.hide('kryska', layer = 'screens')
    renpy.with_statement(wipedown)


This sets the image to appear at topleft (look at the link for the complete list) but also includes a new statement. If you don't use an at_list then it defaults to center.

renpy.with_statement(transition) is used when you want to add a transition to an image changing, think of it as the with dissolve we used earlier.

Code: (Complete example) [Select]

label mission99:
    python:
        if turncount != BM.turn_count:
            turncount = BM.turn_count
           
            if turncount == 2:
                renpy.show("ava uniform alt order mouthopen", at_list = [topleft], layer = 'screens')
                renpy.with_statement(wipeup)
               
                ava("Watch out for that ship!")
               
                renpy.show('ava uniform alt neutral mad', atlist = [topleft], layer = 'screens')
               
                renpy.say("Ava", "You need to pay attention!")
               
                renpy.hide('ava',layer = 'screens')
                renpy.with_statement(wipedown)
               
                create_cover((sunrider.location[0]+1,sunrider.location[1]))


This completed example checks the turn number, then if its the 2nd turn, shows ava shouting at the pilot, changing position and hides her, then a cover is made slightly away from the sunrider location.
Battle Maps

Placeing ships can be hard to visualise, and doing so while testing for balance can be frustrating so the Toolkit function in functions.zip, when dropped in Sunrider/game will create a small + button on the top of the skirmish map.

When you have placed the ships you want, then click the + sign and it will make a file with a basic battle printout that you can use to test your battle for balancing.

As of the moment it dosn't make covers and it dosn't separate player ships into its own paragraph but at some point I will update it to do so.

Code: (Example output) [Select]
label setupmission**:

    window hide
    $ BM.mission = '**'
    call mission**init
    jump battle_start

label mission**init:

    python:
        zoomlevel = 1
        enemy_ships = []
        destroyed_ships = []

        BM.xadj.value = 872
        BM.yadj.value = 370

        create_ship(PactMook(),(8, 3))
        create_ship(PactMook(),(10, 3))
        seraphim.set_location(2, 4)
        blackjack.set_location(6, 4)
        create_ship(PactMook(),(8, 4))
        create_ship(PactAssaultCarrier(),(16, 4))
        bianca.set_location(5, 5)
        create_ship(MissileFrigate(),(12, 5))
        create_ship(PactAssaultCarrier(),(16, 5))
        liberty.set_location(4, 6)
        sunrider.set_location(5, 6)
        phoenix.set_location(7, 6)
        create_ship(PactMook(),(8, 6))
        paladin.set_location(5, 7)
        create_ship(MissileFrigate(),(10, 7))
        create_ship(MissileFrigate(),(11, 7))
        create_ship(PactBattleship(),(13, 8))

    $ PlayerTurnMusic = "music/Titan.ogg"
    $ EnemyTurnMusic = "music/Dusty_Universe.ogg"
    return

label mission**:

    $ BM.battle()
    if BM.battlemode == True:
        jump mission**
    else:
        jump aftermission**

Just change the ** to your mission variable and you can drop it right into the battle. This will not pick up non-core player units like mercenaries. This will likely save you hours of work, but as a side effect it also enables all enemy_ships for the skirmish placement and SHOULD NOT be included with your module[/color]
Ending Battles
Battles are won when BM.you_win() is called. This can be by destroying a ship flagged as Boss, destroying all enemy ships or by code directly calling BM.you_win()

Battles are lost by calling BM.you_loose(), this is called when a critical ship is killed or all player ships are destroyed.

As modifying these will affect the entire game, not just a specific mod. I have created replacement functions that will let you schedule functions for either winning or losing and run these either with or instead of the originals.

To use this then include the three M52 files from functions.zip in your mod.

Include the following code in an init block after -10:
Code: (Useage) [Select]
init python:
    init_win_funcs.append([BM MISSION TO USE, FUNCTION TO USE])
#or
    init_loss_funcs.append([BM MISSION TO USE, FUNCTION TO USE])
   
#Example:
    init_loss_funcs.append([60,crashships])


When using this remember that to maintain flexibility, if you do not direct the function out of the battle, it will carry on to either the loss or victory screen.

To escape this then use something like this:


Code: (escape battle) [Select]
init python:
    def function():
        #do stuff
        clean_battle_exit() # this resets the battle map for the next fight
        renpy.jump('labelname')

Programmed Battle phases
This may be useful to coders for understanding how to implement complex things into battles, but remember if done without returning them to their original states, modifying this will cause other battles to be changed as well.

Battle Start
This is a label that is called, it is used to set the battle up and to store variables for restarting a battle.

Code: [Select]
label battle_start:
    play music PlayerTurnMusic # starts up the music
    python:
        BM.battlestart.player_ships = store.player_ships[:]                 #|
        for ship in BM.battlestart.player_ships:                            #|
            ship.battlestart_location = ship.location                       #|
        BM.battlestart.enemy_ships = deepcopy(store.enemy_ships)            #| stores variables
        BM.battlestart.covers = deepcopy(BM.covers)                         #|
        BM.battlestart.sunrider_rockets = sunrider.rockets                  #|
        BM.battlestart.sunrider_repair_drones = sunrider.repair_drones      #|
        BM.battlestart.cmd = BM.cmd                                         #|
        BM.stopAI = False
        BM.order_used = False
        BM.enemy_vanguard_path = []
        BM.player_vanguard_path = []
        BM.active_strategy = [None,0]
        renpy.take_screenshot()             #| Autosaves
        renpy.save('beginturn')             #|
        if BM.show_tooltips:
            renpy.show_screen('tooltips')
        # BM.xadj.value = 872
        # BM.yadj.value = 370
        for ship in player_ships:           #|
            ship.hp = ship.max_hp           #| Refreshes player ships
            ship.en = ship.max_en           #|
        # renpy.show_screen('mousefollow')
        store.zoomlevel = 0.65
        BM.show_grid = False
        sort_ship_list()
        BM.start()              #| Starts the battle loop


    return

BM.start() (L950 classes.rpy)
This is the bit of code that checks if the formation picker is used and then shows the screens. BM.editableformations is the function that chooses and could be modified, but its of limited interest for modding.
Code: [Select]
        def start(self):
            self.battle_log_insert(['system'], "-------------BATTLE START-------------")
            BM.player_ai = False
            battlemode() #stop scrollback and set BM.battlemode = True
            update_stats()  #used to update some attributes like armour and shields
            renpy.show_screen('battle_screen')

            #new formation feature (only after mission 12 for now)
            if self.editableformations(): # if BM.mission > 12 and not string
                self.phase = 'formation'

                for ship in player_ships:
                    if ship.location != None:
                        set_cell_available(ship.location)
                        ship.location = None

                renpy.show_screen('player_unit_pool_collapsed')
                renpy.show_screen('player_unit_pool')
                BM.selectedmode = False #failsafe
                BM.targetmode = False
                BM.selected = None #the selected unit doesn't show up in the pool
                self.formation_phase()
            else:
                self.jumptomission()
formation_phase is the handling function of placing ships and there's nothing really to mod there either.

jumptomission() just jumps you to your mission label. This is why you should always use the mission format given earlier in the tutorial.

The mission label scrolls through your code and then hits BM.battle where it starts to loop until you end your turn.

BM.battle() (981 classes.rpy)
This is getting more interesting! This function checks what the player clicked and then sends a value to BM.dispatch_handler(). It then checks for loss or win and if either of those returns true, they run the you_win and you_lose functions.
After that it bounces you back to your mission label
Code: [Select]
        def battle(self):
            for ship in player_ships:
                if not hasattr(ship, 'blbl'):        #| If they dont have the attr blbl
                    ship.blbl = ship.lbl             #| sets blbl to lbl for display

            if self.player_ai:              #|
                self.player_AI()            #| player can be set to AI, could well
                self.toggle_player_ai()     #| be some interesting mod stuff here...
                self.result = 'endturn'     #| NPC controled ships independant of players?
            else:
                #battle_screen should be shown, and ui.interact waits for your input. 'result' stores the value return from the Return actionable in the screen
                self.result = ui.interact()

            if store.Difficulty < self.lowest_difficulty:
                self.lowest_difficulty = store.Difficulty

            self.just_moved = False #this sets it so you can no longer take back your move
            renpy.hide_screen('game_over_gimmick') #disables the screensaver gimmick

            if self.stopAI and sunrider.hp < 0:  #some failsafe checking. stopAI functions like an emergency stop for AI code
                renpy.jump('sunrider_destroyed')
            if hasattr(store,'mochi'):
                if hasattr(mochi,'hp'):
                    if mochi.hp < 0 and mochi in player_ships:
                        renpy.jump('sunrider_destroyed')

            #sanity check
            for ship in self.ships:
                if ship.hp <= 0:
                    destroyed_ships.append(ship)
                    if ship in player_ships:
                        player_ships.remove(ship)
                    if ship in enemy_ships:
                        enemy_ships.remove(ship)
                    if ship in self.ships:
                        self.ships.remove(ship)

            self.dispatch_handler(self.result)()

            self.check_for_loss()
            self.check_for_win()
            return

Dispatches (classes 187+
The dispatch_handler function checks for the result of ui.interact in the previous function and then works out what to do with it.

This is done by checking it against the dispatcher returns list in classes.rpy lines 95-129. When it matches up the response then it knows what to do. Dispatcher locations are classes.rpy, lines 200-615.

These would be ideal bits to mod but (sorry to say this again you should remember these will affect all battles). It should be possible to add your own dispatchers and dispatch triggers. look in the custom screens rpy file to see more. There is unfortunately too much code to include it here.

End player turn:
This is done by clicking a displayable set in custom screens, it causes the battle_end_turn function to run which in turn calls the BM.end_player_turn function after clearing the selected ship.

Most of what this function does is set the AI to work.  It ticks the turn_count up a bit and resets ship flak

Using M52-53 then you can set a function to run at the end of the player turn (so pretty much on the enemy turn) if the BM.mission matches the requirements.

Code: (end_player_turn (clases line 1054)) [Select]
        def end_player_turn(self):
            self.battle_log_insert(['system'], "---------Player turn end---------")
            self.battle_log_trimm()
            renpy.hide_screen('commands')
            self.selected = None #some sanity checking
            self.target = None
            self.moving = False
            self.selectedmode = False
            self.targetingmode = False
            self.active_weapon = None
            self.weaponhover = None
            self.turn_count += 1
            renpy.music.play(EnemyTurnMusic)
            renpy.call_in_new_context('endofturn')

            for ship in self.ships:
                ship.flak_effectiveness = 100

            self.enemy_AI() #call the AI to take over
            self.battle_log_insert(['system'], "---------{0} turn end---------".format(self.phase))
            self.battle_log_trimm()
             ##I have NO idea why this dumb workaround is needed, but the destroy() method -somehow- doesn't want to jump to this label sometimes.
            if sunrider.hp < 0:
                renpy.jump('sunrider_destroyed')

            for ship in self.ships:
                ship.flak_effectiveness = 100
                ship.getting_curse = False #failsafes
                ship.getting_buff = False
            for ship in player_ships:
                ship.en = ship.max_en * (100 + ship.modifiers['energy regen'][0] ) / 100
                if ship.en < 0: ship.en = 0
            self.active_weapon = None
            self.targetingmode = False
            self.target = None
            self.selected = None
            self.selectedmode = False
            self.order_used = False
            self.moving = False

            #run the end of turn callbacks
            if BM.end_turn_callbacks != []:
                for callback in BM.end_turn_callbacks:
                    callback()

            if self.battlemode:
                renpy.music.play(PlayerTurnMusic)
                renpy.call_in_new_context('endofturn')
            renpy.take_screenshot()

            # I've sometimes been getting this error for some silly reason:
            # WindowsError: [Error 183] Cannot create a file when that file already exists
            # may just be me, but to be safe I'll put a catch here
            try:
                renpy.save('beginturn')
            except:
                pass
At the end of the enemy's turn it jumps to endofturn label and updates buffs/curses. Now, finally the game makes its way back to the player turn and starts looping through BM.battle/dispatchers and the mission label.

Failed Missions:
If you fail then you are booted to the tryagain label, this resets certain variables stored by the mission at the start and offers you the chance to load a game. If you choose to restart then you are bounced back to battle_start.

The m52 file lets you store variables and run functions at battle-start and changes the tryagain label to Try2 so you can reset other variables, not just the ones auto-reset in this label.

Code: [Select]
label tryagain:
    hide badend
    $ clean_battle_exit(True)
    python:
        store.battle1_check1 = False
        store.battle2_check1 = False
        store.battle2_check2 = False
        store.battle_check1 = False
        i = 1
        while True:
            if hasattr(store, 'check{}'.format(i)):
                setattr(store, 'check{}'.format(i), False)
                i += 1
            else:
                break
       
        try:
            store.destroyed_ships = []
            store.player_ships = BM.battlestart.player_ships
            for ship in store.player_ships:
                ship.missiles = ship.max_missiles
                ship.location = ship.battlestart_location
            sunrider.rockets = BM.battlestart.sunrider_rockets
            sunrider.repair_drones = BM.battlestart.sunrider_repair_drones
            BM.cmd = BM.battlestart.cmd
            BM.turn_count = 1
           
            store.enemy_ships = BM.battlestart.enemy_ships
            for ship in store.enemy_ships:
                if isinstance(ship, Havoc):
                    store.havoc = ship
           
            BM.covers = BM.battlestart.covers
            for cover in BM.covers:
                cover.hp = cover.max_hp
        except:
            renpy.jump('tryagain_old')
        BM.ships = []
        for ship in store.player_ships:
            BM.ships.append(ship)
        for ship in store.enemy_ships:
            BM.ships.append(ship)
       
        BM.grid = []
        for a in range(GRID_SIZE[0]):
            BM.grid.append([False]*GRID_SIZE[1])
        for ship in BM.ships:
            if ship.location == None:
                continue
            x, y = ship.location
            BM.grid[x - 1][y - 1] = True
    jump battle_start
    return
If you Win:
When you win, battle_end is called. This ends the battle loop and resets BM variables for selected ships. It then shows the victory screen and displays ships destroyed in the battle_end function. This would be simple to mod for missions where it wasn't so clear if victory had been achieved, you wanted to modify money or you wanted to put your own spin on captured ships.
Code: [Select]
        def battle_end(self, lost = False):
            """ending the battle - reset values for next battle"""
            self.battlemode = False #this ends the battle loop
            if self.selected != None: self.unselect_ship(self.selected)
            self.targetingmode = False
            self.vanguardtarget = False
            self.weaponhover = None
            self.hovered = None
            BM.enemy_vanguard_path = []
            renpy.hide_screen('tooltips')
            BM.phase = 'Player'

            if store.Difficulty < self.lowest_difficulty:
                self.lowest_difficulty = store.Difficulty

            if not lost:
                #show the victory screen
                renpy.music.stop()
                renpy.music.play('Music/Posthumus_Regium_Finale.ogg', loop = False)
                renpy.hide_screen('commands')
                self.draggable = False
                renpy.show_screen('victory')
                renpy.pause(3.0)
                renpy.hide_screen('victory')

                store.repair_cost = 0
                store.total_money = 0
                store.boss_killed = False
                store.surrender_bonus = 0
                for ship in destroyed_ships:
                    if ship.faction == 'Player':
                        store.repair_cost += int(ship.max_hp * 0.2)
                    else:
                        if ship.boss: store.boss_killed = True #check if a boss was killed
                        store.total_money += ship.money_reward

                if store.boss_killed:
                    for ship in enemy_ships:
                        if ship.hp > 0:
                            store.surrender_bonus += ship.money_reward / 2

                for ship in player_ships:
                    store.repair_cost += int((ship.max_hp - ship.hp)*0.1)

                store.net_gain = int(store.total_money + store.surrender_bonus - store.repair_cost)

                #SPACE WHALE TAX!
                if store.Difficulty == 5:
                    store.net_gain *= 0.8

                self.money += int(net_gain)

                #Captain and higher difficulties reduce the total CP you get per battle.
                difficulty_penalty = store.Difficulty - 1
                if difficulty_penalty < 0: difficulty_penalty = 0

                self.cmd += int((net_gain*10)/(BM.turn_count+difficulty_penalty))

                renpy.show_screen('victory2')
                renpy.pause(1)
                renpy.hide_screen('victory2')
                self.draggable = True

            self.turn_count = 1
            self.active_strategy = [None,0]
            self.ships = []
            self.selectedmode = False
            self.battle_log = []
            renpy.hide_screen("battle_log")

            VNmode() #return to visual novel mode. this mostly just restored scrolling rollback
            for ship in destroyed_ships:
                if self.mission == 'skirmish' or (ship.faction == 'Player' and not ship.mercenary):
                    player_ships.append(ship)
                    self.ships.append(ship)
            for ship in player_ships:
                self.ships.append(ship)
            for ship in player_ships:
                ship.en = ship.max_en
                ship.hp = ship.max_hp
                ship.hate = 100
                ship.total_damage = 0
                ship.total_missile_damage = 0
                ship.total_kinetic_damage = 0
                ship.total_energy_damage = 0
                ship.missiles = ship.max_missiles
                ship.location = None #this helps if you add new ships but don't know the current location of the existing ones.
                for modifier in ship.modifiers:
                    ship.modifiers[modifier] = [0,0]

            #reset the entire grid to empty and BM.ships with only the player_ships list
            clean_grid()
            self.covers = []
            renpy.hide_screen('battle_screen')
            renpy.hide_screen('commands')

            renpy.block_rollback()

Createing Ships:
To Come
Createing Weapons
To Come
Battle MapMaker:
To Come

Expanded Mod Functionality:
To Come

Edit - 18-8-15: Added battle modding section and update Functions.zip to include a fixed toolkit.rpy and the ability to add runnable functions to the you_win and you_loose functions
Edit - 17-9-15: Modified the M52 Functions file


15
Mods / Dynamic area of effect ship modification code
« on: December 12, 2014, 04:21:01 am »
Hello!

I was doing a small bit of work on a story-mod to teach myself some basic python and made an upgrade that gives ships an accuracy boost when within range of another ship. I have adapted it to use in a standard battle and converted it into a function which can boost anything found in the ship.modifiers dictionary(type ship.modifiers in the console to see).

The function can be set to run all battle or you can fire it manually by setting a variable (it turns itself off when the named effect suppliers are killed to reduce any lag).

If it is run in the main battle step then it of course updates whenever the ship is moved or when the supplier is destroyed, it can also be run once a turn to reduce lag (it delayed everything by about half a second for me, when using really big stress test 20+ fleets)

I know most of the coders here could write this in their sleep (and a fraction of the time) but I thought it would be nice to share with any other code newbies. It is as flexible as possible to fit a lot of uses

To Use:

paste this into the missionXinit: label

Code: [Select]
statbooston = 1 # or 0
statboostlist = [] # for some reason I cannot get it to work as a purely local variable

setting statbooston to 1 will start it with the mission, putting it to 0 will stop it or let you decide when to call it, like on the destruction of a base.

Paste this into the missionX: label

Code: [Select]
Statboost(statchange, multiplier, factionlist, boostship, effectlist, min, max)
replace the variable names with what you want, see below.

statchange is anything in the ship.modifiers attribute, not all are used but you can find what is listed by typeing ship.modifiers into the console.

multiplier is the % increase you want to add (remember cumilitive)

factionlist is normaly who owns the effect supplier (player_ships, enemy_ships or BM.ships (all)), this was put in for my capture code compatibility, it could be useful though in other ways. It can be pretty much any list of ships though.

boostship is the name of the ship, i.e. 'Pirate Base', you can manually change that when you create the ship in the init label

effectlist is the list of ships effected by it (see factionlist for some examples), you *can* make your own such list as ryders and there are various ways to do that

min is the minimum range, if 0 it will affect itself

max is the maximum range

so something like...

Code: [Select]
label mission99:
    python:
        Statboost('accuracy', -10, enemy_ships, 'Pirate Base', player_ships, 1, 5)

the modifier and the name of the boost ship need the '' if you have more than one ship named the same, it will work on all of them at the same time, so in this case -20 accuracy when in range of both but only -10 when in range of only one.

This is the function, just stick it anywhere and becuse its in an init block it will load up when the game is loaded

Code: [Select]
init python:
    def Statboost(statchange, multiplier, factionlist, boostship, effectlist, min, max):
        global statbooston
        global statboostlist
        if statbooston == 1:
            for ship in BM.ships[:]:
                ship.statmod1 = 0
                ship.statmod2 = 0
                statbooston = 2
            statboostlist = []
           
        if statbooston == 2:
            for ship in statboostlist[:]:
                if ship.statmod1 > 0: # resets on action and only if it happened already
                    ship.statmod2 = [int(round(ship.statmod2[0] - ship.statmod1)), ship.modifiers.get(statchange)[1]]
                    ship.modifiers.update({statchange : ship.statmod2})
                    ship.statmod1 = 0 # after reverseing the effect, if it happened wipe to 0
                    ship.statmod2 = 0

            #statbooston = 0 # so only triggers if the boost ship is around, can comment out.

        for ship in factionlist:
            if ship.name == boostship:
                statbooston = 2 # if owned, run next part
                     
        if statbooston == 2:
            for ship in factionlist[:]:
                if ship.name == boostship: #do to all ships for each boostship of factionlist
                    source = ship
                    for ship in effectlist: #do to all ships in effectlist in range
                        inrange = get_ship_distance(ship, source)
                        if inrange > min: # if in range then +1 to statmod1
                            if inrange < max:
                                ship.statmod1 += 1
                                   
            for ship in effectlist:
                if ship.statmod1 > 0: # if ship qualifies do this
                    ship.statmod2 = ship.modifiers.get(statchange) # get modifier
                    ship.statmod1 = ship.statmod1 * multiplier #get amount to boost by
                    ship.statmod2 = [ship.statmod1 + ship.modifiers.get(statchange)[0], ship.modifiers.get(statchange)[1]] # supplies list to sub into modifiers and attaches to sc2
                    ship.modifiers.update({statchange: ship.statmod2}) #updates dict so it is applied
                    statboostlist.append(ship) #attaches so modified ships can be reversed.
                    statboostlist.append(ship) #attaches so modified ships can be reversed.

If you have questions, I may be around and almost certainly happy to help  ;D

Pages: [1] 2