Need help with coding a game system like pipe dream in Ren'py

L33ch

Newbie
Jun 11, 2017
93
175
So, I have an idea for an adult game I'd like to make at some point in the future, but I'm not sure how viable it would be because I'm still pretty new to coding in general, so any help would be great.

The thing I'm kind of stuck on right now is that I want to make a subsystem in the game kind of like the old pipedream game, or uh... Tentacular would be the closest adult game version.

If I spend a little time, I can figure out how to get pieces to snap to a grid, so that's not a huge issue. The problem is, rather than a timed deal where the pipe fills up with fluid and it's game over after - I want to make a node based puzzle.

One point of origin that can connect to multiple end points, preferably at the same time, with the endpoints triggering conditions to flip variables to True or False so long as there is a complete segment of 'pipe' from the origin. Frankly, I have no idea how I could code it to register the connection from point A to point B, so if anyone has any ideas or could direct me somewhere there is similar coding I could look at, I'd be grateful.
 
Last edited:

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,969
16,222
One point of origin that can connect to multiple end points, preferably at the same time, with the endpoints triggering conditions to flip variables to True or False so long as there is a complete segment of 'pipe' from the origin. Frankly, I have no idea how I could code it to register the connection from point A to point B, so if anyone has any ideas or could direct me somewhere there is similar coding I could look at, I'd be grateful.

Hmm... For the original pipe dream, I would probably use something like this:
/!\ Totally wrote on the fly - Will probably not works well as it /!\
Python:
# Empty 4x4 grid
default nodes = [ [ None, None, None, None ],
          [ None, None, None, None ],
          [ None, None, None, None ],
          [ None, None, None, None ] ]


init python:
    class Nodes( renpy.python.RevertableObject ):
        def __init__( self, image, south=False, north=False, east=False, west=False, origin=None ):
            # Base for the image to display
            self._image = image
            # Is there an input/output point on this side of the node ?
            self.south = south
            self.north = north
            self.east = east
            self.west = west
            # How much of the node is filled
            self.filled = 0
            # From where come the flow
            self.from = None

            #  Apply only for the first node - delay before it start
            # here, five seconds.
            self.waiting = 50
            # Origin from the flow
            self.origin = origin

        # Return the image to display for this node
        @property
        def image( self ):
            #  The image name is "[base name][flow origin][filling level].jpg"
            #  So for a base name "cross", the images are
            # "crossN0.jpg", "crossN1.jpg", ..., "crossN5.jpg"
            # "crossS0.jpg", "crossS1.jpg", ..., "crossS5.jpg"
            # ...
            return "{}{}{}.jpg".format( self._image, self.from, self.filled )

        #  Put the node on the grid.
        # x -> column where the node is
        # y -> row where the node is
        def use( self, x, y ):
            self.x = x
            self.y = y


        #  Fill the node.
        def fill( self, from ):
            #  If the node is already partly filled, but not from this entry point
            if self.filled > 0 and not self.from == from:
                # Then return
                return

            #  If the node is not totally filled, continue to fill it
            elif self.filled < 5:
                self.filled += 1
                # if it's the first time, keep the origin
                if self.filled == 1:
                    self.from = from

            # The node is totally filled
            else:
                # Test all input/output possibilities
                if self.south:
                    # If there's a node south to this one
                    if len( store.nodes ) < self.y and store.nodes[self.y + 1][self.x]:
                        # if it have a north intput/output, fill the next node
                        if store.nodes[self.y + 1][self.x].north:
                            store.nodes[self.y + 1][self.x].fill( "N" )
                if self.north:
                    # If there's a node north to this one
                    if self.y > 0 and store.nodes[self.y - 1][self.x]:
                        # if it have a south intput/output, fill the next node
                        if store.nodes[self.y - 1][self.x].south:
                            store.nodes[self.y - 1][self.x].fill( "S" )
                if self.west:
                    # If there's a node west to this one
                    if len( store.nodes[0] ) < self.x and store.nodes[self.y][self.x +1]:
                        # if it have a east intput/output, fill the next node
                        if store.nodes[self.y + 1][self.x].east:
                            store.nodes[self.y + 1][self.x].fill( "E" )
                if self.east:
                    # If there's a node east to this one
                    if self.x > 0 and store.nodes[self.y][self.x - 1]:
                        # if it have a west intput/output, fill the next node
                        if store.nodes[self.y - 1][self.x].west:
                            store.nodes[self.y - 1][self.x].fill( "W" )

        #  Starting node
        def start( self ):
            # If it's not started yet, continue to wait, else fill.
            if self.waiting > 0:
                self.waiting -= 1
           else:
                self.fill( self.origin )



screen pipedream( x, y ):

    # Every 1/10th second, fill the pipe
    timer 0.1 repeat True action Function( store.nodes[y][x].start )

    # still a 4x4 grid
    grid 4 4:
        # for all rows
        for y in range(0, 4 ):
            # and all columns
            for x in range(0, 4 ):
                # If there's a node, display it
                if store.nodes[y][x]:
                    add store.nodes[y][x].image
                # Else a button to add a node
                else:
                    imagebutton:
                        idle "empty.jpg"
                        action SetField( store.nodes[y][x], Nodes( ... ) )

label whatever:
    # reset the grid
    $ nodes = [ [ None, None, None, None ],
          [ None, None, None, None ],
          [ None, None, None, None ],
          [ None, None, None, None ] ]
    # place the first node
    $ nodes[1][3] = Nodes( "startSouth", north=True, start="S" )

    # start the game
    call screen pipedream( 3, 1 )
As I said, it will not works as it.
Without counting the possible typos, the main problem is the addition of a new pipe. I just thrown a raw code for this part, because there's many possible way to do this, and it's not what you asked for.
There's possibly others small issues. It's a code that I wrote on the fly without taking the time to effectively think about the question ; it's what the algorithm would looks like in my head when I would start thinking, not what I would looks like once I have it all.


To answer the question on a more generic point of view:

Yes, it is possible to make such game with Ren'Py. But Ren'Py isn't necessarily the best engine for this kind of games. It would need to much tiles to have an effectively smooth progression, and I have no idea which grid size and advance speed would be its limit. A 10x10 grid with a 0.05 speed should probably be fine, but I guaranty nothing if you go further.
 
  • Like
Reactions: L33ch

L33ch

Newbie
Jun 11, 2017
93
175
Hmm... For the original pipe dream, I would probably use something like this:
/!\ Totally wrote on the fly - Will probably not works well as it /!\
Python:
# Empty 4x4 grid
default nodes = [ [ None, None, None, None ],
          [ None, None, None, None ],
          [ None, None, None, None ],
          [ None, None, None, None ] ]


init python:
    class Nodes( renpy.python.RevertableObject ):
        def __init__( self, image, south=False, north=False, east=False, west=False, origin=None ):
            # Base for the image to display
            self._image = image
            # Is there an input/output point on this side of the node ?
            self.south = south
            self.north = north
            self.east = east
            self.west = west
            # How much of the node is filled
            self.filled = 0
            # From where come the flow
            self.from = None

            #  Apply only for the first node - delay before it start
            # here, five seconds.
            self.waiting = 50
            # Origin from the flow
            self.origin = origin

        # Return the image to display for this node
        @property
        def image( self ):
            #  The image name is "[base name][flow origin][filling level].jpg"
            #  So for a base name "cross", the images are
            # "crossN0.jpg", "crossN1.jpg", ..., "crossN5.jpg"
            # "crossS0.jpg", "crossS1.jpg", ..., "crossS5.jpg"
            # ...
            return "{}{}{}.jpg".format( self._image, self.from, self.filled )

        #  Put the node on the grid.
        # x -> column where the node is
        # y -> row where the node is
        def use( self, x, y ):
            self.x = x
            self.y = y


        #  Fill the node.
        def fill( self, from ):
            #  If the node is already partly filled, but not from this entry point
            if self.filled > 0 and not self.from == from:
                # Then return
                return

            #  If the node is not totally filled, continue to fill it
            elif self.filled < 5:
                self.filled += 1
                # if it's the first time, keep the origin
                if self.filled == 1:
                    self.from = from

            # The node is totally filled
            else:
                # Test all input/output possibilities
                if self.south:
                    # If there's a node south to this one
                    if len( store.nodes ) < self.y and store.nodes[self.y + 1][self.x]:
                        # if it have a north intput/output, fill the next node
                        if store.nodes[self.y + 1][self.x].north:
                            store.nodes[self.y + 1][self.x].fill( "N" )
                if self.north:
                    # If there's a node north to this one
                    if self.y > 0 and store.nodes[self.y - 1][self.x]:
                        # if it have a south intput/output, fill the next node
                        if store.nodes[self.y - 1][self.x].south:
                            store.nodes[self.y - 1][self.x].fill( "S" )
                if self.west:
                    # If there's a node west to this one
                    if len( store.nodes[0] ) < self.x and store.nodes[self.y][self.x +1]:
                        # if it have a east intput/output, fill the next node
                        if store.nodes[self.y + 1][self.x].east:
                            store.nodes[self.y + 1][self.x].fill( "E" )
                if self.east:
                    # If there's a node east to this one
                    if self.x > 0 and store.nodes[self.y][self.x - 1]:
                        # if it have a west intput/output, fill the next node
                        if store.nodes[self.y - 1][self.x].west:
                            store.nodes[self.y - 1][self.x].fill( "W" )

        #  Starting node
        def start( self ):
            # If it's not started yet, continue to wait, else fill.
            if self.waiting > 0:
                self.waiting -= 1
           else:
                self.fill( self.origin )



screen pipedream( x, y ):

    # Every 1/10th second, fill the pipe
    timer 0.1 repeat True action Function( store.nodes[y][x].start )

    # still a 4x4 grid
    grid 4 4:
        # for all rows
        for y in range(0, 4 ):
            # and all columns
            for x in range(0, 4 ):
                # If there's a node, display it
                if store.nodes[y][x]:
                    add store.nodes[y][x].image
                # Else a button to add a node
                else:
                    imagebutton:
                        idle "empty.jpg"
                        action SetField( store.nodes[y][x], Nodes( ... ) )

label whatever:
    # reset the grid
    $ nodes = [ [ None, None, None, None ],
          [ None, None, None, None ],
          [ None, None, None, None ],
          [ None, None, None, None ] ]
    # place the first node
    $ nodes[1][3] = Nodes( "startSouth", north=True, start="S" )

    # start the game
    call screen pipedream( 3, 1 )
As I said, it will not works as it.
Without counting the possible typos, the main problem is the addition of a new pipe. I just thrown a raw code for this part, because there's many possible way to do this, and it's not what you asked for.
There's possibly others small issues. It's a code that I wrote on the fly without taking the time to effectively think about the question ; it's what the algorithm would looks like in my head when I would start thinking, not what I would looks like once I have it all.


To answer the question on a more generic point of view:

Yes, it is possible to make such game with Ren'Py. But Ren'Py isn't necessarily the best engine for this kind of games. It would need to much tiles to have an effectively smooth progression, and I have no idea which grid size and advance speed would be its limit. A 10x10 grid with a 0.05 speed should probably be fine, but I guaranty nothing if you go further.
Thank you for the help even to this point! It is appreciated, and I'll use it as a starting point to learn from/play around with.