2019-08-05 00:27:14 (edited by amerikranian 2019-08-05 00:28:32)

So while I'm figuring out Pathfinder and what to do with it (should I even bother with it) I still want to make progress, and this is my next question.
Making multiple levels.
I have tried searching on google, but so far have not found anything too promising. My question is how?
Suppose that we have 2 levels. One of them involves the player killing everything, and another involving the player getting to a certain coordinate. We can, of course, do something like this.

class player:
 def __init__(self, x, y):
  self.x = x
  self.y = y
  self.level = 1

In our main loop, we'd do something like this:

if player.level == 1 and len(enemies) == 0:
 #Everything's dead, we can advance to level 2.
 player.level += 1
elif player.level == 2 and player.x == 450:
 #We reached the winning x, we can advance now
 player.level += 1

That's not bad, right? Of course, our loop will get exponentially bigger the more levels we add to our game. Another way I thought of doing level loops is like so

class player:
 def __init__(self, x, y):
  self.x, self.y = x, y
  self.level = 0

level_loops = [lev1, lev2]
#In our main loop, we'd do something like this:
level_loops[player.level]() #Calling our level loop

def lev1():
 if len(enemies) == 0:
  #We won! Next level
  player.level += 1

def lev2():
 if player.x == 450:
  #We won! Next level
  player.level += 1

This makes my main loop cleaner and smaller, but does not kill my if statements, it just hides them.
What are some other ways of doing level loops? Am I overthinking it all again?

2019-08-05 01:18:25 (edited by magurp244 2019-08-05 01:24:15)

I don't think your particularly over thinking it, if you plan on having different victory conditions per map you may have no choice but to have a separate progression trigger for each. I think the term your looking for is abstraction or compartmentalization, which is fine from a design stand point to make code more easy to manage. Another way of dealing with it would be to use classes for each map with their own independant function handlers, including victory conditions. For example:

class lev1(object):
    def __init__(self):
    #our map data
        self.data = [[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0]]
    #current number of enemies
        self.enemies = 0

    def update(self,player):
        #update map things, changes to the environment, player position, etc.

    #if all enemies defeated, advance to next level
        if self.enemies == 0:
            print('enemies defeated!')
            player.level += 1


class lev2(object):
    def __init__(self):
    #our map data
        self.data = [[0,0,0,0],[0,0,99,0],[0,0,0,0],[0,0,0,0]]

    def update(self,player):
        #update map things

    #player is at position of exit, advance level
        if self.data[player.y][player.x] == 99:
            print('player at exit!')
            player.level += 1


class player(object):
    def __init__(self):
        self.x = 2
        self.y = 1
        self.level = 0


class main(object):
    def __init__(self):
        self.levels = [lev1(),lev2()]
        self.player = player()

#main update loop
    def update(self):
        while True:
        #update level and pass the player to it
            self.levels[self.player.level].update(self.player)
            if self.player.level == 2:
                print('victory!')
                break

a = main()
a.update()

In the given example, the main class loads each map class, then passes the player class and iterates over them but lets the map classes handle things for themselves internally. When their conditions are met they change the player state which switches the map over to the next map handler class with different conditions.

-BrushTone v1.3.3: Accessible Paint Tool
-AudiMesh3D v1.0.0: Accessible 3D Model Viewer

2019-08-05 02:40:07 (edited by amerikranian 2019-08-05 02:40:56)

Right, the reason I asked was because I can't imagine the amount of classes and or level entries a game like Paladin has. If you haven't played it in a nutshell it is an RPG where you follow different quest objectives while moving across multiple maps with different enemies. Still. One thing at a time.
I just considered another way we could do the leveling concept, though this one is a much less concrete idea. Here's the theoretical code.

class level_handler:
 def __init__(self):
  self.win_conditions = {} #Store what is required to win
 def update_data(self, conditions):
  self.win_conditions = conditions
#To use this, we could do something like so.
lv = level_handler()
level_data = ({"alive", 0},)
lv.update_data(level_data[0])
#Pretend that we have the player class and enemy list from post 1 again. Here is what the main loop could look like:
while 1:
 level_logic()

def level_logic():
 for i in lv.win_conditions.items():
  if i[0] == "alive" and len(enemies) == i[1]:
   #We won the level, so let's advance.
   #Please note, player.level is currently at 0, or it would be in a program if I wish to use this technique
   player.level += 1
   lv.update_data(level_data[player.level]) #This will crash if you run this right now because we don't have a second item, but you get the point

So, yeah. Any issues you see with this approach? The downside to this from what I can gather is that I would have to teach the interpreter what each entry means, but I feel like that is going to be there no matter what I go with. I store the level_data as a tuple because I don't see it changing throughout the game, but if I'm ever going to allow user-created content (Most likely not) I would be in trouble. I could also create map_data which is just like level_data except it will hold information pertaining to that level's map. Thoughts?

2019-08-05 06:24:12

The last solution you provided is more close to the one i'm using right now in one of my projects. Each level has a list of conditions which decide upon something happens, e.g. winning or losing conditions. If one of my winning conditions returns True whenever their check() method is called, that means that the condition "succeeded". If a winning condition succeeds, you've obviously won, if a losing condition succeeds, you've lost. That way you can add in multiple winning/losing conditions and just need to loop over them at certain times and call their check() methods to see if you've already won/lost.
I also added in some string representation to those conditions. That way i'm able to either tell the player what they must do in order to win or tell the player why they actually lost. I guess you can think about more useful things to be added to such classes later on.
Best Regards.
Hijacker