Ren'Py Stellar Crossroads - Space Combat Simulator

Xavster

Well-Known Member
Game Developer
Mar 27, 2018
1,249
7,625
With the next release of Stellar Crossroads, I am planning on including a space combat simulation into the game. I know that I will be able implement something, however with some guidance, what I am able to achieve will be increased. I have several months to work out a solution, however raising now such that I don't get off on the wrong foot.

Below is the project folder available on Mega for the mini game in development. It's fully automated to update whenever I update the mini game code.
Ren'Py Project Folder -

I realise the limitations of Ren'Py, hence plan is for a 2D top down simulation with about a dozen ships on each side. Player would just assign fairly simply overall tactics to different wings of their force. The combat would then run through a turn using initiative sequence. Each ship would then target enemy based upon tactics and chances to hit and damage would be applied should the shot hit.

The first initial challenge is to determine how the ship information will be stored. Thus I require a data set for the fleet that will achieve the following:
- Be able to add and subtract ships with their data from the fleet.
- Able to count the number of ships in that fleet.
- Have a series of variables for a ship containing such information as weapons type, armour type, evasion, hit points etc.
- Individual items of data within the set must be accessible and also easily changed.

I have used data arrays in a format such as follows, however is this the best option?
Python:
    l_fleet = {
    1:
        { 1: "Bomber", 2: "Missile", 3: 5, 4: 50, 5: 1000},
    2:
        { 1: "Fighter", 2: "Laser", 3: 10, 4: 20, 5: 800},
    }
At this stage thinking that a fleet will contain up to 16 ships. There will be multiple fleets and also a hangar to store more ships. It will need to be possible to transfer a ship from one location to another.

Just the data question initially, however more will follow as I develop the system.


PS: To assure you that assistance won't be in vain, a little screen below of the refining system I put into the game about a year ago. There is also mining and manufacturing in the game already. I'll get something implemented. ;)
refining.jpg
 
Last edited:

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,978
16,233
I have used data arrays in a format such as follows, however is this the best option?
Yes but no.

Yes because by itself relying on a dict offer a relatively easy access to the data you need, but no because an object would be more suitable for what you want to do, especially with Python's duck typing approach.

That way, you can define your map, and all the player interface, way easier:
Python:
# The currently defined ship
default currentShip = None

screen map():

    # Display the map itself, assuming  the background is a single image above
    add mapBackground

    #  Display buttons for "move" action, assuming it's a 5x5 grid
    # But only if there's a ship selected.
    if currentShip:
        grid 5 5:
            for y in range( 0, 5 ):
                for x in range( 0, 5 ):
                    imagebutton:
                        # Fully transparent to not hide the background
                        idle Solid( "#00000000" )
                        # "move" the ship, only if the ship can reach this cell
                        if currentShip.canReach( x, y ):
                            action Return( "move", x, y )
                        # Else do nothing.
                        else:
                            action NullAction()

    # Display the enemy ships
    for obj in enemy_fleet:
        # Only if the ship is present
        if obj.inFight:
            imagebutton:
                # Display the image
                idle obj.picture
                # and place it correctly ; assuming 50 pixel by case.
                xpos obj.posX 
                ypos obj.posY
                # If there's a ship selected, and the target is at range
                if currentShip and currentShip.atRange( obj.x, obj.y ):
                    # A click will make the ship fire at the target
                    action Return( "fire", obj )
                # If no ship select, or the target is out of range, nothing will happen
                else:
                    action NullAction()

    # Display the ships
    for obj in l_fleet:
        if obj.inFight:
            imagebutton:
                idle obj.picture
                xpos obj.posX 
                ypos obj.posY
                action SetVariable( "currentShip", obj )

#  While it's possible to do all this from the screen, it NEED to be on a label.
# Ren'Py update the rollback (and so what will be saved) only when an interaction
# starts. So, if everything was in the screen, a save/load would send back to the
# very start of the battle.
label combat:
    # Infinite loop, so have a test to end the fight
    while True:
        call screen map
        if _return[0] == "move":
            $ currentShip.move( _return[1], _return[2] )
        elif _return[0] == "fire":
            $ currentShip.fire( _return[1] )
        [...]
This being possible because all the computation will be hidden in the object:
Python:
init python:
    class Ship( renpy.python.RevertableObject ):
        def __init__( self, name, x, y ):
            self.name = name
            self.x = x
            self.y = y

            # Default values, will be updated
            self.image = None
            self.range = 0
            self.speed = 0
            [...]

         @property
         def inFight( self ):
             # The ship if present if it haven't been destroyed and it haven't ran away.
             return self.life > 0 and not self.haveFled

        @property
        def picture( self ):
            return self.image

        @property
        def posX( self ):
            return self.x * 50 # assuming 50x50 cells for the map.
       
        @property
        def posY( self ):
            return self.y * 50 # assuming 50x50 cells for the map.

        def canReach( self, x, y ):
            # compute the distance between ( self.x, self.y ) and ( x, y )
            # Return True only if the distance is below the speed
            return distance <= self.speed

        def move( self, x, y ):
            self.x = x
            self.y = y

        def fire( self, target ):
            # Directly remove the damage from the target life
            target.life -= self.damage

        [...]

    # A "fighter" class ship
    class Figther( Ship ):
        def __init__( self, name, x, y ):
            # Initialize the father
            super( Fighter, self).__init__( name, x, y )

            # define the particularities
            self.range = 5
            self.speed = 5
            self.damage = 5
            self.image = "fighter.png"

    # A "scout" class ship
    class Scout( Ship ):
        def __init__( self, name, x, y ):
            # Initialize the father
            super( Scout, self).__init__( name, x, y )

            # define the particularities
            self.range = 5
            # Scout are faster, but not meant to fight
            self.speed = 10
            self.damage = 2
            self.image = "scout.png"

default l_fleet = [ Scout( "Frodo" ), Fighter( "Aragorn" ), Fighter( "Grimly" ) ]
And so on, I'll not write everything since it's more to show the spirit.

It present two advantages:
Firstly you don't need to know what ship it is, the code will always works.
Secondly, since everything is handled by the "Ship" class, you don't need to care about how you do something.

Let's say that you aren't happy with the code to move a ship. You don't have to change your map, you don't have to change "combat" label. Just change the code in canReach, and possibly move, and everything will automatically adapt to that change.

The instant you've correctly defined the object "interface", therefore here the canReach, atRange, and so on, methods, you are good to go and can works on the map itself and the user interface.
It doesn't matter if you don't know how to compute if a target is at range, or if a cell is reachable. Just make those to methods return False until you figured how to do it. And, as I said, it doesn't matter if you change the way you computer it, the only thing that will change is the content of a single method.
 
  • Like
Reactions: gojira667

Xavster

Well-Known Member
Game Developer
Mar 27, 2018
1,249
7,625
anne O'nymous - I think I may have thrown you off with the 2D reference. At this stage just planning on ships being in a few rows on each side of the battlefield. You would just assign the tactics for each row (say 5 ships in each row), most likely just picking between 'aggressive' / 'neutral' / 'defensive' and which row of the enemy you wish them to attack. The ships themselves don't move, just fire at enemy or take damage from enemy fire. Thus don't need x, y coordinates, just what row you assign them to during fleet setup. Main reason for this is that I don't want the mini game to take particularly long to simulate. Perhaps a few minutes for an encounter. It's more like Magic the Gathering than a game of chess in terms of board setup.

I'm also planning to implement a ship designer, where you will select which components are used to assemble the ship. This is actually not particularly complicated to code as all that will end up happening is that you pick a number of component options and then the ship data row is generated from the selections made. For example if you picked rugged heavy components and had a small engine, the resultant ship would have high hit points and low evasion. Should be able to create this ships designer without any assistance.

Edit: Much of the tactical aspect will be how you compose your fleet. In game already are a range of components that will be used in the ship construction. For instance there are lasers, magnetic cannons and missiles. Similarly there are different forms of armour - reflective, ablative, heavy. There will be a rock, paper, scissors elements to designing for a particular enemy. For example if they have reflective armour, you will want magnetic cannons rather than lasers. This will make more sense once I can show screens from what I create or share the code.
 
Last edited:

Xavster

Well-Known Member
Game Developer
Mar 27, 2018
1,249
7,625
scscs01.jpg
Have started coding the mini game in order to show you the intent. It can be downloaded in full or you can browse the game files using the link in the header post.

As you can see above, your ships are on the left with the enemy on the right hand side. The bars represent hitpoints on the ship for structure / plating / armour respectively. When a shot is made and it hits, initially it will be subtracted from the armour value. Subsequent shots will check to see if they land on the armour or the next layer. Hence if half your armour is gone, then it has a 50% chance to land on the plating instead. If it makes it past the armour and the plating, it can hit the structure. Once the structure is below 50%, the ship is inoperable and can't shoot. If structure goes to 0 then the ship is fully lost.

At this point I only have basic form of the data sets coded with some nominal values and the start of the screen that will be the interface. At this time just want to make sure the way in which the fleet data is stored is the best option. Note that as mentioned before I will need to be able to add and remove rows from the fleet data sets.

I would note that the fleet data will be appended row by row (ship by ship) by the fleet management interface. Equally the contents of the row will be created via the ship designer interface. These will not be present in this exercise due to the integration with the main Stellar Crossroads game. Hence the fleet composition will likely just be a choice of a number of preset options.

The enemy data set will be randomly generated based upon an enemy and a difficulty. The faction you are facing will determine which ships are possible and the difficulty will affect the number of ships you encounter. Each faction has different ship styles with different strengths and weaknesses (weapon and armour types). Will likely include this enemy generation system into the mini game.
 

crabsinthekitchen

Well-Known Member
Apr 28, 2020
1,565
9,080
first thing I'd do is replace 1-15 with normal string names for keys. but maybe you have better memory than I do and won't forget what number means what after not looking at the code for a day

or even do as anne suggested and use classes

something like

Code:
init python:
    class Weapon:

        def __init__(self, image="laser01", damage=100):
            self.image = image
            self.damage = damage

        def fire(self, target):
            target.take_damage("armor", self.damage)

    class Body:

        def __init__(self, material="iron", max_hp=500):
            self.material = material
            self.max_hp = max_hp
            self.current_hp = max_hp
        
        def take_damage(self, amount):
            self.current_hp = max(current_hp - amount, 0)

    class Ship:

        def __init__(self, weapon=Weapon(), hull=Body(), plating=Body(), armor=Body()):
            self.weapon = weapon
            self.hull = hull
            self.plating = plating
            self.armor = armor
    
        def take_damage(part, amount):
            if part == "hp" or (plating.current_hp <= 0 and armor.current_hp <= 0):
                hull.take_damage(amount)
            elif part == "plating" or armor.current_hp <= 0:
                plating.take_damage(amount)
            elif part == "armor":
                armor.take_damage(amount)
            else:
                pass

        def image():
            return self.weapon.image
I'd also add a separate class for the battlefield, which I would create before every battle and make it handle turns. also put the rows there because I don't think it's that important for a ship to know where it is on the battlefield, something like
Python:
    class Battlefield:
        def __init__(self, size):
            self.size = size
            self.reset_rows()


        def reset_rows(self):
            self.player_rows = [BattlefieldRow() for i in range(0, self.size//2)]
            self.enemy_rows = [BattlefieldRow() for i in range(0, self.size//2)]


        def add_ship(self, ship, row_position, is_player):
            if is_player:
                self.player_rows[row_position].add_ship(ship)
            else:
                self.enemy_rows[row_position].add_ship(ship)

    class BattlefieldRow:

        def __init__(self):
            self.ships = []

        def add_ship(self, ship):
            self.ships.append(ship)
then most of the combat screen can be done just by using boxes and two loops


Code:
screen sc_battle:
    modal True
    image "battle/b_back.jpg"

    hbox:
        spacing 500
        yalign 0.5
        xalign 0.5
        hbox:
            box_reverse True
            for row in battlefield.player_rows:
                vbox:
                    yalign 0.5
                    spacing 15
                    for ship in row.ships:
                        hbox:
                            bar:
                                bar_vertical True
                                value ship.hull.current_hp
                                range ship.hull.max_hp
                                xysize(10,120)
                                left_bar "battle/bar_empty.png"
                                right_bar "battle/bar_white.png"
                            bar:
                                bar_vertical True
                                value ship.plating.current_hp
                                range ship.plating.max_hp
                                xysize(10,120)
                                left_bar "battle/bar_empty.png"
                                right_bar "battle/bar_cyan.png"
                            bar:
                                bar_vertical True
                                value ship.armor.current_hp
                                range ship.armor.max_hp
                                xysize(10,120)
                                left_bar "battle/bar_empty.png"
                                right_bar "battle/bar_blue.png"
                            image "battle/{}.png".format(ship.weapon.image)
        hbox:
            for row in battlefield.enemy_rows:
                vbox:
                    yalign 0.5
                    spacing 15
                    for ship in row.ships:
                        hbox:
                            bar:
                                bar_vertical True
                                value ship.hull.current_hp
                                range ship.hull.max_hp
                                xysize(10,120)
                                left_bar "battle/bar_empty.png"
                                right_bar "battle/bar_white.png"
                            bar:
                                bar_vertical True
                                value ship.plating.current_hp
                                range ship.plating.max_hp
                                xysize(10,120)
                                left_bar "battle/bar_empty.png"
                                right_bar "battle/bar_cyan.png"
                            bar:
                                bar_vertical True
                                value ship.armor.current_hp
                                range ship.armor.max_hp
                                xysize(10,120)
                                left_bar "battle/bar_empty.png"
                                right_bar "battle/bar_blue.png"
                            image "battle/{}.png".format(ship.weapon.image)
attached full files and the screenshot of how everything looks with all the changes
screenshot0009.png
 

Xavster

Well-Known Member
Game Developer
Mar 27, 2018
1,249
7,625
first thing I'd do is replace 1-15 with normal string names for keys. but maybe you have better memory than I do and won't forget what number means what after not looking at the code for a day

or even do as anne suggested and use classes

something like

Code:
init python:
    class Weapon:

        def __init__(self, image="laser01", damage=100):
            self.image = image
            self.damage = damage

        def fire(self, target):
            target.take_damage("armor", self.damage)

    class Body:

        def __init__(self, material="iron", max_hp=500):
            self.material = material
            self.max_hp = max_hp
            self.current_hp = max_hp
     
        def take_damage(self, amount):
            self.current_hp = max(current_hp - amount, 0)

    class Ship:

        def __init__(self, weapon=Weapon(), hull=Body(), plating=Body(), armor=Body()):
            self.weapon = weapon
            self.hull = hull
            self.plating = plating
            self.armor = armor
 
        def take_damage(part, amount):
            if part == "hp" or (plating.current_hp <= 0 and armor.current_hp <= 0):
                hull.take_damage(amount)
            elif part == "plating" or armor.current_hp <= 0:
                plating.take_damage(amount)
            elif part == "armor":
                armor.take_damage(amount)
            else:
                pass

        def image():
            return self.weapon.image
I'd also add a separate class for the battlefield, which I would create before every battle and make it handle turns. also put the rows there because I don't think it's that important for a ship to know where it is on the battlefield, something like
Python:
    class Battlefield:
        def __init__(self, size):
            self.size = size
            self.reset_rows()


        def reset_rows(self):
            self.player_rows = [BattlefieldRow() for i in range(0, self.size//2)]
            self.enemy_rows = [BattlefieldRow() for i in range(0, self.size//2)]


        def add_ship(self, ship, row_position, is_player):
            if is_player:
                self.player_rows[row_position].add_ship(ship)
            else:
                self.enemy_rows[row_position].add_ship(ship)

    class BattlefieldRow:

        def __init__(self):
            self.ships = []

        def add_ship(self, ship):
            self.ships.append(ship)
then most of the combat screen can be done just by using boxes and two loops


Code:
screen sc_battle:
    modal True
    image "battle/b_back.jpg"

    hbox:
        spacing 500
        yalign 0.5
        xalign 0.5
        hbox:
            box_reverse True
            for row in battlefield.player_rows:
                vbox:
                    yalign 0.5
                    spacing 15
                    for ship in row.ships:
                        hbox:
                            bar:
                                bar_vertical True
                                value ship.hull.current_hp
                                range ship.hull.max_hp
                                xysize(10,120)
                                left_bar "battle/bar_empty.png"
                                right_bar "battle/bar_white.png"
                            bar:
                                bar_vertical True
                                value ship.plating.current_hp
                                range ship.plating.max_hp
                                xysize(10,120)
                                left_bar "battle/bar_empty.png"
                                right_bar "battle/bar_cyan.png"
                            bar:
                                bar_vertical True
                                value ship.armor.current_hp
                                range ship.armor.max_hp
                                xysize(10,120)
                                left_bar "battle/bar_empty.png"
                                right_bar "battle/bar_blue.png"
                            image "battle/{}.png".format(ship.weapon.image)
        hbox:
            for row in battlefield.enemy_rows:
                vbox:
                    yalign 0.5
                    spacing 15
                    for ship in row.ships:
                        hbox:
                            bar:
                                bar_vertical True
                                value ship.hull.current_hp
                                range ship.hull.max_hp
                                xysize(10,120)
                                left_bar "battle/bar_empty.png"
                                right_bar "battle/bar_white.png"
                            bar:
                                bar_vertical True
                                value ship.plating.current_hp
                                range ship.plating.max_hp
                                xysize(10,120)
                                left_bar "battle/bar_empty.png"
                                right_bar "battle/bar_cyan.png"
                            bar:
                                bar_vertical True
                                value ship.armor.current_hp
                                range ship.armor.max_hp
                                xysize(10,120)
                                left_bar "battle/bar_empty.png"
                                right_bar "battle/bar_blue.png"
                            image "battle/{}.png".format(ship.weapon.image)
attached full files and the screenshot of how everything looks with all the changes
View attachment 3025219
Thanks for the assistance. I downloaded the attached files, however I believe they are the originals rather than the ones you modified. Would you please attach the modified files.

The layout is certainly more efficient than what I had employed. I'll have to get my head around the remainder before making the adjustment.

I was planning on having the ships in rows as the intent is that the player will group ships of certain types in certain rows (front, middle, back). Each of the rows will have a different modifier to their attack and defence rolls. Also intending for the player to be able to give different tactics to each row as well.

EDIT: In relation to classes the ships will likely all be different. The laser01 is just the chassis that is employed and an array of other components may be adjusted when designing your ship. Thus each row of a fleet may potentially be unique. There are simply too many options to have a single label that will fully describe the ship parameters.
- 6 weapon types
- 3 structure types
- 4 plating types
- 6 armour types
- 3 engine types
 
Last edited:
  • Like
Reactions: Cornholio231

crabsinthekitchen

Well-Known Member
Apr 28, 2020
1,565
9,080
Thanks for the assistance. I downloaded the attached files, however I believe they are the originals rather than the ones you modified. Would you please attach the modified files.
oh, yeah. accidentally uploaded files from the wrong folder
 

Xavster

Well-Known Member
Game Developer
Mar 27, 2018
1,249
7,625
I have been playing around with the alternative style of data storage (python objects) and can see the benefits from the system. Still need more time to get my head around it fully, as I haven't done object orientation programming before.

If you take a look at the code changes I have made (download from Mega folder), I have restructured the data storage slightly. I need the player fleet to be global, hence I have split battlefield into PlayerFleet and EnemyFleet. I also split into wings instead of rows and added a ship id. There are a few things that still have me mystified a little if someone could help.

- How do a delete a specific ship from the data set?
- How do I move a ship from one wing to another? (checked revised code)
- How do I move a ship from say fleet01 to fleet02 or back to the players station?

Ideally the actions above would be via buttons within a screen. Hence perhaps a 'X' button to delete ship, that is placed under the 'for ship in row.ships' vbox loop. Thus there would be a 'X' button next to each ship.

Note that current goal is to work out how everything functions before I implement the full complexity.

PS: I've included some extra buttons in the images folder from the main game if you need to utilise them.
 
  • Thinking Face
Reactions: crabsinthekitchen

crabsinthekitchen

Well-Known Member
Apr 28, 2020
1,565
9,080
ok, I managed to add drag and drop to change the wings or remove ships from the fleet (didn't write the base and sending them there) but it's acting a bit weird, probably needs some but I need to look more into that and the most detailed tutorial about drag and drops had 20 minutes of explaining some unrelated python code before explaining the basics of drag and drop. I also replaced the list of ships in Wing with Set and check the ship ID so the ships don't get duplicated when you move them around (had that happening)
View attachment 2023-10-23 23-35-28.mp4
 
  • Like
Reactions: Xavster

Xavster

Well-Known Member
Game Developer
Mar 27, 2018
1,249
7,625
The delete / move commands don't actually have to be a drag / drop system. As far as game design, I was planning on doing it within a different screen with buttons. Realistically with the wing system, you will group certain types of ships together. This will allow you to match weapon types with the armour weakness of a wing of enemies. Also wings that are vulnerable to the enemy, can be put on evasive which will hopefully allow them to survive. Don't think there is value mid battle in micro-managing fleet compositions. Really all you will be doing in battle is occasionally adjusting the wing tactics and hitting the next round button. Goal is to provide some tactical options, however the space battle to play out in a few minutes.

If you were at the station you would have access to the station forces and whatever fleets were not on mission. Thus I would create a screen that has the station forces with buttons adjacent to each ship to delete, repair, transfer to fleet01, transfer to fleet02. On this same screen I would have some buttons at the top which would switch screens to fleet01, fleet02 etc. If I was to switch to the fleet01 screen, I would then have delete, repair, transfer to station, transfer to fleet02.

I'll pull out what I can out of the code you have forwarded and attempt to create the additional screens including buttons. Then it should just be a matter of assigning the command to the button and ensuring that the command works properly. Whilst the drag / drop system might be a little more elegant in the intended design, the added complexity may not be worth the benefit.
 

Xavster

Well-Known Member
Game Developer
Mar 27, 2018
1,249
7,625
crabsinthekitchen - I have done another coding update to include additional screens to show you the intent of the system. Download at the normal location. If you can work out the moving of the ships utilising buttons, then the screens will immediately allow you to check your work.

I have inserted a number of buttons on these screens to move / delete functions, however after a couple of hours of trying the syntax of the commands is eluding me. If you look in the battle.rpy file, I have commented what I intend each button to do.

If I see something done once, I can usually work of the permutations. The tutorials I have been watching on classes are all too simple though and have not proven particularly useful. About the only thing I was able to work out was how to call up the fleet total ship count.

Note: For actual gameplay, the station and the fleet organisation screens will be available by visiting a location on the station. The battlefield will only be used should a fleet out on patrol come across an enemy. The player will manufacture ships (to their own design from components) to be stored at the station. They can then allocate them to a fleet and then also organise the wings in that fleet. It will be very rare for players to transfer ships from one fleet to another. If they choose to do so, they can do it via the station.
 

crabsinthekitchen

Well-Known Member
Apr 28, 2020
1,565
9,080
I won't have time to code a working example today but after thinking about it here's the structure I would make

the class for entire army that has all fleets and stations and has methods to create new ones. I'd create it at the start with default player_army = PlayerArmy(), then I can make buttons with action Function(player_army.create_fleet) or action Function(player_army.create_station) to create new stations or fleets

Python:
    class PlayerArmy:
        def __init__(self):
            self.stations = set(Station("S-01"))
            self.fleets = set()
        
        def create_fleet(self):
            if len(self.fleets) == 0:
                fleet_id = "F-01"
            else:
                #so that's just what I came up with for ids, going like F-01, F-02... F-0732 and so on
                #continuously increasing number with every new fleet.
                #could probably just use a number for that, would be easier
                import re
                fleet_id = "F-0{}".format(int(re.findall(r'\d+',self.fleets[-1].id)) + 1)
            new_fleet = PlayerFleet(3, fleet_id)
            self.fleets.add(new_fleet)

        def create_station(self):
            import re
            station_id = "F-0{}".format(int(re.findall(r'\d+',self.fleets[-1].id)) + 1)
            new_fleet = Station(station_id)
            self.stations.add(new_fleet)

        def remove_fleet(self, fleet):
            if fleet in self.fleets:
                self.fleets.remove(fleet)

        def remove_station(self, station):
            if station in self.stations and len(self.stations) > 1:
                self.stations.remove(station)

        def get_fleet_by_id(self, fleet_id):
            return next(fleet for fleet in self.fleets if fleet.id == fleet_id)

        def get_station_by_id(self, fleet_id):
            return next(fleet for fleet in self.fleets if fleet.id == fleet_id)
Station and PlayerFleet classes are almost the same, station is basically a fleet with only one wing so it could have ships stored directly in a set like Wing does. add an id property, __eq__ and __hash__ methods (the ones in Ship should work just fine) to tell them apart and you're good

then you'd only need a function like this to move ships between fleets and stations
Python:
def move_ship(ship, from, to):
    from.remove_ship(ship)
    if to is not None:
        to.add_ship(ship)
screen code would look like this
Code:
for ship in fleet.ships:
    #ship display code
    for station in player_army.stations:
        button:
            #images, text, whatever
            action Function(move_ship, ship, fleet, station)
    for target_fleet in player_army.fleets:
        button:
            #images, text, whatever
            action Function(move_ship, ship, fleet, target_fleet)
It will work for moving ships between Wings too if you add remove_ship method to a wing. And to delete a ship you just pass None as the last argument
 
  • Like
Reactions: Xavster

Xavster

Well-Known Member
Game Developer
Mar 27, 2018
1,249
7,625
crabsinthekitchen - I've had a moderate amount of success in developing the mini game further and updated the code and included additional graphics.

Firstly in relation to the PlayerArmy idea, I think it may cause more issues than it resolves. The number of levels in the data tree is already starting to do my head in. The reason I don't think it is necessary, is that the game doesn't require many fleets.
- Only 1 station (it's the station on which you live in the game)
- Only 1 enemy (will be randomly generated based on opponent and difficulty each encounter)
- Up to 4 fleets (these will all start out completely blank and will be filled with ships from the station)

I have included some additional parameters against the ships that will assist in the actual combat rounds. At this point I am planning upon the following system for battle rounds:
- Ships will attack in order based upon initiative (highest to lowest) [includes your fleet and enemy fleet]
- Ship will target a random ship in the enemy wing, based upon the wing.target
- Ship will make 'shots' attempts to hit enemy ship
- Hit is registered when attacker roll is higher than defender roll
- attacker roll = attack + random(100) + wing_tactic
- defender roll = evasion + random(100) + wing_tactic
- What the damage is applied to depends upon percentage of remaining coverage. Hence order would be armour --> plating --> hull. If you have 50% of your armour left, then there is a 50% chance to penetrate to next level and hit plating instead. Same applies to plating missing.
- Ship can't fire as soon as the hull is below 50%.
- Ship is fully destroyed if hull reaches 0%.
I am also intending on certain armour / plating to have strengths / weaknesses against weapon types. Can worry about this later though.

Suspect that the initiative aspect might be a little difficult if ships from either side are attacking simultaneously. One level simpler would be for wing with highest average initiative to attack first. Two levels similar would be the fleet with the higher initiative attack first. Or simplest just scrap the use of initiative and have the player fleet go first.

Take a look at the updates I have made and see whether you want to have a go at the battle code first. Alternatively I can have a go first, however I will likely leave out the initiative aspect.


EDIT: With the space battles the fleet that will be involved will depend upon which one you send on the mission. Hence is it possible to adjust 'screen sc_battle' to be something like 'screen sc_battle(fleet_id)' such that one screen will cover all fleet options? Similarly can this be done with 'screen sc_fleet(fleet_id)'. I had a go at it however it failed when I attempted to use the PlayerFleet class.
 
Last edited:

Xavster

Well-Known Member
Game Developer
Mar 27, 2018
1,249
7,625
Thought I would list out the strengths / vulnerabilities against weapon types for reference:

Armour:
ReflectiveAblativeHeavy
Laser50%200%100%
Cannon200%100%50%
Missile100%50%200%

Plating:
IronNitinolCarbotaniumZircaloy
Laser100%100%100%50%
Cannon100%50%100%150%
Missile100%150%100%100%
Carbotanium is a light alternative with low weight and low HP's. The low weight assists with evasion.

Hull:
All types take 100% damage for all weapon types.
 

Xavster

Well-Known Member
Game Developer
Mar 27, 2018
1,249
7,625
ship design.jpg
Coded up the ship designer today. The components in the inventory will draw from your actual inventory in the main game, however have placed in some nominal values into this game. All of the ship parameters change with different selections and the create button places the ship, using the currently selected alternatives, into your hangar and subtracts components from inventory. Code is a little verbose and cumbersome, however integrates directly into the main game. Really should have done the inventory system in the game as a data set rather than separate variables. It's done now a going back at this stage would be counter-productive.

ship hangar.jpg
Transfer to the other fleets, deletions and wing assignments are also working fine. Items left to take care of are:
- Salvage from deleted ships
- Ship repairs
- Auto-generation of enemy fleets
- Battle Rounds / Battle Damage

Think I should be able to handle the first 3 fine with what I have picked up over the past week. It's really the Battle Rounds / Battle Damage that is still a little uncertain.

Going to work on the script a little over the next few days, so if someone wants to jump in a have a bit of a go at the battle system it would be appreciated. It's really just the def that will run following pressing the battle round button. The intent of the system is described in this post.
 

Xavster

Well-Known Member
Game Developer
Mar 27, 2018
1,249
7,625
I have been working at it for the past couple of days and think I have most of the issues resolved. A lot of trial and error, but I think I have the core element working. Give me a couple of days and I will try to implement all the planned features. I'll let you all know when I have what I consider is a complete system.

From there it will be a matter of testing and refining. Of course any recommendations on minor fixes to code would be appreciated. Also feedback on any changes or additional features.
 

Xavster

Well-Known Member
Game Developer
Mar 27, 2018
1,249
7,625
A quick little question if someone would be able to assist.

The following code set the target to a random ship within the enemy_wing.
Python:
for target_row in enemy.enemy_wing:
    target = renpy.random.choice(target_row.ships)
Question: How do I code such that it selects a random target from the enemy? Hence returns 1 result from the entire enemy force, rather than 1 result from each wing in that enemy force.
 

crabsinthekitchen

Well-Known Member
Apr 28, 2020
1,565
9,080
Python:
enemy_ships = []
for wing in enemy.enemy_wing:
    enemy_ships.extend(wing.ships)

target = renpy.random.choice(enemy_ships)
 
  • Heart
Reactions: Xavster

Xavster

Well-Known Member
Game Developer
Mar 27, 2018
1,249
7,625
I have updated the battle simulator and for the most part everything is working as intended. I opted to abandon the inclusion of initiative into the battle as the complexity if introduced was not worth the payoffs. Instead I will change the player / enemy order, depending upon what type of encounter it is. Hence if it is an escort mission you are on the enemy will attack first. To do this it is just a matter of including an alterative battle button, where the call of functions is reversed.

There is an item I could do with some assistance with:
- I want to show the progress during the battle round. Hence the progressive lowering of armour / plating / hull as each fighter attacks. Whilst I can get a pause to work within the definition after each ship attack, I can't update the screen, thus the display doesn't change until the end of the round.
Python:
                time.sleep(0.1) # works
                renpy.restart_interaction() # doesn't work

Over the course of the week I'll insert some auto-generation of player and enemy fleets. Need to create some more graphics for the enemy fleets though to provide a range of different types of enemies. All of this is fairly straight forward though.
 
Last edited: