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