2
\$\begingroup\$

I've tried to make the code overall more PEP-8 friendly and added more comments to make it more readable. Added some new functions and methods like ranged attack and allegiance.

Play around with it. Use "a" to give characters more moves, "s" to spawn more characters, "d" to spawn Goblins that move randomly, "f" to attack and "q" to ranged attack.

Here is the full code, tell me how I can shorten it but retain the same functionality:

import random
import math
import pygame

pygame.init()   #Starting pygame ...                                
Clock = pygame.time.Clock()                   
Screen = pygame.display.set_mode([650, 650])  
DONE = False    #Main game loop variable                                 
MAPSIZE = 25   #how many tiles in the grid
TURN = 0    #incemented to keep track of turn number

TILEWIDTH  = 20    #pixel size of tiles drawn                               
TILEHEIGHT = 20
TILEMARGIN = 4

BLACK = (0, 0, 0)                               
WHITE = (255, 255, 255)
GREEN = (0, 255, 0)
RED = (255, 0, 0)
BLUE = (0, 0, 255)
BROWN = (123, 123, 0)
MOVECOLOR = (150, 250, 150)

KeyLookup = {
    pygame.K_LEFT: "W",
    pygame.K_RIGHT: "E",
    pygame.K_DOWN: "S",
    pygame.K_UP: "N"
}


class MapTile(object):                       #The main class for stationary things that inhabit the grid ... grass, trees, rocks and stuff.
    def __init__(self, name, internal_column, internal_row, visible):
        self.name = name
        self.internal_column = internal_column
        self.internal_row = internal_row
        self.visible = visible


class Character(object):                    #can move around and do cool stuff
    def __init__(self, name, HP, internal_column, internal_row, damage, allegiance):
        self.name = name
        self.HP = HP
        self.internal_column = internal_column
        self.internal_row = internal_row
        self.damage = damage
        self.allegiance = allegiance    #allegiance is either 0 or 1, 0 being "friendly" and 1 being "enemy"

    visionrange = 5
    moves_left = 15
    direction = "N"

    def move(self, direction):      #how characters move around
        if self.collision_check(direction):
            print("Collision")
            return
        if self.moves_left == 0:
            print("No more moves left")
            return
        elif direction == "N":                    
                self.internal_row -= 1
                self.direction = "N"
        elif direction == "W":
                self.internal_column -= 1
                self.direction = "W"
        elif direction == "E":
                self.internal_column += 1
                self.direction = "E"
        elif direction == "S":
                self.internal_row += 1
                self.direction = "S"
        self.moves_left = self.moves_left - 1

    def collision_check(self, direction): #Checks the grid square right infront of the character to see if anything is there      
        if direction == "N":
            if self.internal_row == 0:
                return True
            if len(Map.Grid[self.internal_column][(self.internal_row)-1]) > 1:
                return True
        elif direction == "W":
            if self.internal_column == 0:
                return True
            if len(Map.Grid[self.internal_column-1][(self.internal_row)]) > 1:
                return True
        elif direction == "E":
            if self.internal_column == MAPSIZE-1:
                return True
            if len(Map.Grid[self.internal_column+1][(self.internal_row)]) > 1:
                return True
        elif direction == "S":
            if self.internal_row == MAPSIZE-1:
                return True
            if len(Map.Grid[self.internal_column][(self.internal_row)+1]) > 1:
                return True
        return False

    def attack(self, direction):    #Attacks square right infront of character
        if self.collision_check(direction):
            print("Attack attempt.")
            if self.direction == "N":
              for i in range(0, len(Map.Grid[self.internal_column][self.internal_row-1])):
                if Map.Grid[int(self.internal_column)][int(self.internal_row-1)][i].__class__.__name__ == "Character":
                    Map.Grid[self.internal_column][self.internal_row-1][i].HP -= self.damage
                    print(str(self.damage) + " damage")
            elif self.direction == "E":
                for i in range(0, len(Map.Grid[self.internal_column+1][self.internal_row])):
                    if Map.Grid[self.internal_column+1][self.internal_row][i].__class__.__name__ == "Character":
                        Map.Grid[self.internal_column+1][self.internal_row][i].HP -= self.damage
                        print(str(self.damage) + " damage")
            elif self.direction == "W":
                for i in range(0, len(Map.Grid[self.internal_column-1][self.internal_row])):
                    if Map.Grid[self.internal_column-1][self.internal_row][i].__class__.__name__ == "Character":
                        Map.Grid[self.internal_column-1][self.internal_row][i].HP -= self.damage
                        print(str(self.damage) + " damage")
            elif self.direction == "S":
                for i in range(0, len(Map.Grid[self.internal_column][self.internal_row+1])):
                    if Map.Grid[self.internal_column][self.internal_row+1][i].__class__.__name__ == "Character":
                        Map.Grid[self.internal_column][self.internal_row+1][i].HP -= self.damage
                        print(str(self.damage) + " damage")
            self.moves_left = self.moves_left - 1

    def ranged_attack(self, direction): 
            if self.direction == "S":
                for k in range(1, (MAPSIZE - self.internal_row)):
                    for i in range(0, len(Map.Grid[self.internal_column][self.internal_row + k])):
                        if Map.Grid[self.internal_column][self.internal_row + k][i].__class__.__name__ == "Character":
                            Map.Grid[self.internal_column][self.internal_row + k][i].HP -= self.damage
                            print(str(self.damage) + " damage")
                            self.moves_left = self.moves_left - 1 
            if self.direction == "N":
                for k in range(1, self.internal_row):
                    for i in range(0, len(Map.Grid[self.internal_column][self.internal_row - k])):
                        if Map.Grid[self.internal_column][self.internal_row - k][i].__class__.__name__ == "Character":
                            Map.Grid[self.internal_column][self.internal_row - k][i].HP -= self.damage
                            print(str(self.damage) + " damage")
                            self.moves_left = self.moves_left - 1
            if self.direction == "W":
                for k in range(1, self.internal_column):
                    for i in range(0, len(Map.Grid[self.internal_column - k][self.internal_row])):
                        if Map.Grid[self.internal_column - k][self.internal_row][i].__class__.__name__ == "Character":
                            Map.Grid[self.internal_column - k][self.internal_row][i].HP -= self.damage
                            print(str(self.damage) + " damage")
                            self.moves_left = self.moves_left - 1
            if self.direction == "E":
                for k in range(1, (MAPSIZE - self.internal_column)):
                    for i in range(0, len(Map.Grid[self.internal_column + k][self.internal_row])):
                        if Map.Grid[self.internal_column + k][self.internal_row][i].__class__.__name__ == "Character":
                            Map.Grid[self.internal_column + k][self.internal_row][i].HP -= self.damage
                            print(str(self.damage) + " damage")
                            self.moves_left = self.moves_left - 1
            else:
                return 


class Goblin(Character):    #Character that moves around randomly. Press "d" to summon one
    def __init__(self):
        Character.__init__(self, "Goblin", random.randint(15,20), random.randint(0,MAPSIZE-1), random.randint(0,MAPSIZE-1), 10, random.randint(0,1))

    def random_move(self):
        i = random.randint(0,3)
        if i == 0:
            self.move("N")
        elif i == 1:
            self.move("S")
        elif i == 2:
            self.move("W")
        elif i == 3:
            self.move("E")
        self.moves_left = 10


class Archer(Character):
    def __init__(self):
        Character.__init__(self, "Archer", random.randint(15,20), random.randint(0,MAPSIZE-1), random.randint(0,MAPSIZE-1))


class Warrior(Character):
    def __init__(self):
        Character.__init__(self, "Warrior", random.randint(15,20), random.randint(0,MAPSIZE-1), random.randint(0,MAPSIZE-1))


class Scout(Character):
    def __init__(self):
        Character.__init__(self, "Scout", random.randint(15,20), random.randint(0,MAPSIZE-1), random.randint(0,MAPSIZE-1))


class Rogue(Character):
    def __init__(self):
        Character.__init__(self, "Rogue", random.randint(15,20), random.randint(0,MAPSIZE-1), random.randint(0,MAPSIZE-1))


class Wizard(Character):
    def __init__(self):
        Character.__init__(self, "Wizard", random.randint(15,20), random.randint(0,MAPSIZE-1), random.randint(0,MAPSIZE-1))


class Map(object):              #The main class
    global MAPSIZE
    wild_characters = []    #Where goblins go
    can_move = []   #Where characters with moves left are
    no_moves = []
    Grid = []
    friendlies = []
    enemies = []
    everyone = []
    visible = []

    for row in range(MAPSIZE):     # Creating grid
        Grid.append([])
        for column in range(MAPSIZE):
            Grid[row].append([])

    for row in range(MAPSIZE):     #Filling grid with grass
        for column in range(MAPSIZE):
            TempTile = MapTile("Grass", column, row, False)
            Grid[column][row].append(TempTile)

    for row in range(MAPSIZE):     #Putting some rocks near the top
        for column in range(MAPSIZE):
            TempTile = MapTile("Rock", column, row, False)
            if row == 1:
                Grid[column][row].append(TempTile)

    for i in range(10):         #Trees in random places
        random_row = random.randint(0, MAPSIZE - 1)
        random_column = random.randint(0, MAPSIZE - 1)
        TempTile = MapTile("Tree", random_column, random_row, False)
        Grid[random_column][random_row].append(TempTile)

    def generate_hero(self):            #Generate a character and place it randomly
        temp_hero = Character("Hero", 30, random.randint(0, MAPSIZE - 1), random.randint(0, MAPSIZE - 1) , 10, random.randint(0,1))
        self.Grid[temp_hero.internal_column][temp_hero.internal_row].append(temp_hero)
        self.can_move.append(temp_hero)
        if temp_hero.allegiance == 0:
            self.friendlies.append(temp_hero)
        elif temp_hero.allegiance == 1:
            self.enemies.append(temp_hero)

    def return_distance(self, pointA, pointB):  #Gives distance between two points or characters
        distance = math.sqrt((pointA.internal_column - pointB.internal_column)**2 + (pointA.internal_row - pointB.internal_row)**2)
        return distance

    def generate_goblin(self):            #Generate a character and place it randomly
        random_row = random.randint(0, MAPSIZE - 1)
        random_column = random.randint(0, MAPSIZE - 1)
        temp_goblin = Goblin()
        self.Grid[random_column][random_row].append(temp_goblin)
        Map.wild_characters.append(temp_goblin)

    def update(self):           
        for column in range(MAPSIZE):                           #These nested loops go through entire grid 
            for row in range(MAPSIZE):                          #They check if any objects internal coordinates
                for i in range(len(Map.Grid[column][row])):     #disagree with its place on the grid and update it accordingly                            
                    if Map.Grid[column][row][i].internal_column != column:
                        TempChar = Map.Grid[column][row][i]
                        Map.Grid[column][row].remove(Map.Grid[column][row][i])
                        Map.Grid[int(TempChar.internal_column)][int(TempChar.internal_row)].append(TempChar)

                    elif Map.Grid[column][row][i].internal_row != row:
                        TempChar = Map.Grid[column][row][i]
                        Map.Grid[column][row].remove(Map.Grid[column][row][i])
                        Map.Grid[int(TempChar.internal_column)][int(TempChar.internal_row)].append(TempChar)

        temparr = Map.no_moves[:]
        for characterobject in temparr:    #This moves any characters with moves to the can move list 
            if len(temparr) > 0:
                if characterobject.moves_left > 0:
                    Map.can_move.append(characterobject)
                    Map.no_moves.remove(characterobject)
            if characterobject.HP <= 0:
                Map.no_moves.remove(characterobject)

        arr = Map.can_move[:] 
        for item in arr:    #This moves characters from can_move to no_moves when they run out of moves
            if item.moves_left == 0:
                Map.can_move.remove(item)
                Map.no_moves.append(item)
            if item.HP <= 0:
                Map.can_move.remove(item)

        for column in range(MAPSIZE):   #This checks entire grid for any dead characters and removes them                          
            for row in range(MAPSIZE):                          
                for i in range(len(Map.Grid[column][row])):     
                    if Map.Grid[column][row][i].__class__.__name__ == "Character":
                        if Map.Grid[column][row][i].HP <= 0:
                            Map.Grid[column][row].remove(Map.Grid[column][row][i])
                            print("Character died")

        for column in range(MAPSIZE):   #This appends any character in the grid to Map.everyone
            for row in range(MAPSIZE):
                for i in range(len(Map.Grid[column][row])):
                    if Map.Grid[column][row][i].__class__.__name__ == "Character":
                        Map.everyone.append(Map.Grid[column][row][i])

        for character in Map.everyone:  #Showing visible           
            for column in range(MAPSIZE):
                for row in range(MAPSIZE):
                    if Map.return_distance(Map.Grid[column][row][0], character ) <= character.visionrange:
                        Map.Grid[column][row][0].visible = True
                    else:
                        Map.Grid[column][row][0].visible = False

    def listofcharacters(self): #This returns the coordinates and allegiance of any characters that inhabit the grid
        for column in range(MAPSIZE):
            for row in range(MAPSIZE):
                for i in range(len(Map.Grid[column][row])):
                    if Map.Grid[column][row][i].__class__.__name__ == "Character":
                        print("Column: ", Map.Grid[column][row][i].internal_column, ", Row: ", Map.Grid[column][row][i].internal_row, ", Allegiance: ", Map.Grid[column][row][i].allegiance)





Map = Map()
Map.generate_hero()

while not DONE:     #Main pygame loop
    for event in pygame.event.get():         #catching events
        if event.type == pygame.QUIT:
            DONE = True       
        elif event.type == pygame.MOUSEBUTTONDOWN:
            Pos = pygame.mouse.get_pos()
            column = Pos[0] // (TILEWIDTH + TILEMARGIN)  #Translating the position of the mouse into rows and columns
            row = Pos[1] // (TILEHEIGHT + TILEMARGIN)
            print(str(row) + ", " + str(column))

            for i in range(len(Map.Grid[column][row])):
                print(str(Map.Grid[column][row][i].name))  #print stuff that inhabits that square

        elif event.type == pygame.KEYDOWN:
            wild_char_arr = Map.wild_characters[:]

            for wildcharacter in wild_char_arr:     #Making the goblins move randomly every time a key is pressed
                if wildcharacter.name == "Goblin":
                    wildcharacter.random_move() 

            if event.key == 97:      # Keypress: a
                print("New turn.")
                temparr = Map.no_moves[:]
                for item in temparr:
                    if item.moves_left == 0:
                        item.moves_left = 15
                TURN = TURN + 1
                print("Turn: ", TURN)

            elif event.key == 115:    # Keypress: s
                print("Generated hero.")
                Map.generate_hero()

            elif event.key == 113:    # Keypress: q     
                print("ranged attack")
                Map.can_move[0].ranged_attack(Map.can_move[0].direction)

            elif event.key == 119:    # Keypress: w
                print("Generated hero.")
                Map.generate_hero()

            elif event.key == 101:    # Keypress: e
                print("Generated hero.")
                Map.generate_hero()

            elif event.key == 114:    # Keypress: r
                print("Generated hero.")
                Map.generate_hero()

            elif event.key == 116:    # Keypress: t
                print("Generated hero.")
                Map.generate_hero()

            elif event.key == 100:  # Keypress: d
                Map.generate_goblin()
                print("Generated goblin.")

            elif event.key == 102:  # Keypress: f
                Map.can_move[0].attack(Map.can_move[0].direction) 

            elif len(Map.can_move) > 0: #Movement
                Map.can_move[0].move(KeyLookup[event.key])

            else:
                print("invalid")

            Map.update()

    Screen.fill(BLACK)

    for row in range(MAPSIZE):           # Drawing grid
        for column in range(MAPSIZE):
            for i in range(0, len(Map.Grid[column][row])):
                Color = WHITE
                if len(Map.can_move) > 0:   # Creating colored area around character showing his move range
                    if (math.sqrt((Map.can_move[0].internal_column - column)**2 + (Map.can_move[0].internal_row - row)**2)) <= Map.can_move[0].moves_left:
                        Color = MOVECOLOR
                if len(Map.Grid[column][row]) > 1:
                    Color = RED
                if Map.Grid[column][row][i].name == "Tree":
                    Color = GREEN
                if str(Map.Grid[column][row][i].__class__.__name__) == "Character":
                    Color = BROWN

            pygame.draw.rect(Screen, Color, [(TILEMARGIN + TILEWIDTH) * column + TILEMARGIN,
                                             (TILEMARGIN + TILEHEIGHT) * row + TILEMARGIN,
                                             TILEWIDTH,
                                             TILEHEIGHT])

            if str(Map.Grid[column][row][i].__class__.__name__) == "Character":     #Drawing the little red square showing character direction
                if Map.Grid[column][row][i].direction == "N":
                        Color = RED
                    pygame.draw.rect(Screen, Color, [(TILEMARGIN + TILEWIDTH) * column + TILEMARGIN + 8,
                                             (TILEMARGIN + TILEHEIGHT) * row + TILEMARGIN,
                                             TILEWIDTH/4,
                                             TILEHEIGHT/4])
            if str(Map.Grid[column][row][i].__class__.__name__) == "Character": #Drawing the little red square showing character directio
                if Map.Grid[column][row][i].direction == "S":
                    Color = RED
                    pygame.draw.rect(Screen, Color, [(TILEMARGIN + TILEWIDTH) * column + TILEMARGIN + 8,
                                             (TILEMARGIN + TILEHEIGHT) * row + TILEMARGIN*5 - 1 ,
                                             TILEWIDTH/4,
                                             TILEHEIGHT/4])
            if str(Map.Grid[column][row][i].__class__.__name__) == "Character": #Drawing the little red square showing character directio
                if Map.Grid[column][row][i].direction == "E":
                    Color = RED
                    pygame.draw.rect(Screen, Color, [(TILEMARGIN + TILEWIDTH) * column + TILEMARGIN + 15,
                                             (TILEMARGIN + TILEHEIGHT) * row + TILEMARGIN*5 - 8,
                                             TILEWIDTH/4,
                                             TILEHEIGHT/4])
            if str(Map.Grid[column][row][i].__class__.__name__) == "Character": #Drawing the little red square showing character directio
                if Map.Grid[column][row][i].direction == "W":
                    Color = RED
                    pygame.draw.rect(Screen, Color, [(TILEMARGIN + TILEWIDTH) * column + TILEMARGIN
                                                     ,
                                             (TILEMARGIN + TILEHEIGHT) * row + TILEMARGIN*5 - 8 ,
                                             TILEWIDTH/4,
                                             TILEHEIGHT/4])


    Clock.tick(60)      
    pygame.display.flip()     

pygame.quit()
\$\endgroup\$
3
  • \$\begingroup\$ you could factorize a lot of things in most functions. that's the main default of your code. \$\endgroup\$ – Jean-François Fabre Oct 20 '16 at 20:00
  • \$\begingroup\$ What do you mean by factorize? \$\endgroup\$ – user115383 Dec 18 '16 at 16:50
  • \$\begingroup\$ I mean creating procedures with parameters instead of copying/pasting almost the same code all over. \$\endgroup\$ – Jean-François Fabre Dec 18 '16 at 17:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy