Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import sys, platform, os
- import time, random, math
- class STATE:
- ALIVE = 'alive'
- DEAD = 'dead'
- #POISONED = 'poisoned'
- class MOVE:
- UP = 'up'
- DOWN = 'down'
- LEFT = 'left'
- RIGHT = 'right'
- class FLAG:
- ATTACK = 'Error in attack method'
- UPDATE = 'Error in update method'
- CLEAR = 'Error in clear screen method'
- VALID = 'Error in valid move method'
- E_TURN = 'Error in enemy turn function'
- P_TURN = 'Error in player turn function'
- class Entity(object):
- '''Basic entity object
- Has position and character.'''
- def __init__(self, name, x, y, hp, strength, speed, char, state=STATE.ALIVE):
- self.name = name
- self.x = x
- self.y = y
- self.hp = hp
- self.strength = strength
- self.speed = speed
- self.char = char
- self.state = state
- def move(self, direction, board):
- '''To update entity position
- Takes direction and deals with collision.'''
- if direction == MOVE.LEFT and board.is_valid_move(self, (self.x, self.y - 1)):
- board.print_char(self)
- self.y -= 1
- return True
- elif direction == MOVE.RIGHT and board.is_valid_move(self, (self.x, self.y + 1)):
- board.print_char(self)
- self.y += 1
- return True
- elif direction == MOVE.UP and board.is_valid_move(self, (self.x - 1, self.y)):
- board.print_char(self)
- self.x -= 1
- return True
- elif direction == MOVE.DOWN and board.is_valid_move(self, (self.x + 1, self.y)):
- board.print_char(self)
- self.x += 1
- return True
- else:
- return False
- def attack(self, target, called_shot=False, part=''):
- '''Deals with combat.'''
- if self.distance(target) == 1:
- if not called_shot:
- to_hit = random.randint(1, 21) # Roll
- if to_hit >= target.speed + 5: # If hit... (AC = 5 + target speed)
- target.hp -= self.strength # Deal damage (DAM = Self strength)
- print '{} hit!'.format(self.name) # Output
- time.sleep(0.5)
- return True # Return taken turn
- else:
- print '{} missed!'.format(self.name) # Else, miss
- time.sleep(0.5)
- return True # Return taken turn
- elif called_shot: # Stronger hit
- to_hit = random.randint(1, 21) # Roll
- if to_hit >= target.speed + 10: # If hit...
- target.hp -= random.randint(1, 3) + self.strength # Deal damage (DAM = 1-3 + self strength)
- print '{} hits {}\'s {}!'.format(self.name, target.name.lower(), part) # Output
- time.sleep(0.5)
- return True # Return taken turn
- else:
- print '{} missed {}\'s {}!'.format(self.name, target.name.lower(), part) # Else, miss
- time.sleep(0.5)
- return True # Return taken turn
- else:
- return FLAG.ATTACK
- else:
- print 'You can\'t hit from this far away' # Else the attacker is too far away
- time.sleep(0.5)
- return False # Return turn not taken
- def distance(self, target, pos=()):
- '''Get distance from self to target.'''
- if not pos: # If nothing passed to pos, then use entity coordinates
- x, y = self.x, self.y
- else:
- x, y, = pos # else, use passed tuple
- return math.sqrt((x - target.x)**2 + (y - target.y)**2) # Get distance
- def change_state(self, state):
- '''Change Entity state. Only for death right now.
- Soon for position also.'''
- self.state = state
- if self.state == STATE.DEAD:
- self.char = '%'
- else:
- return
- def update(self):
- '''Update Entity, check hp, xp, ect.'''
- if self.name == 'Player':
- if self.hp <= 0 and self.state == STATE.ALIVE: # If hp is 0 or below, change to dead
- self.change_state(STATE.DEAD)
- self.char = '%'
- elif self.hp <= 0 and self.state == STATE.DEAD: # If dead, let the enemy take turns endlessly
- time.sleep(1)
- elif self.name == 'Enemy':
- if self.hp <= 0 and self.state == STATE.ALIVE: # Check for hp and death
- self.change_state(STATE.DEAD)
- self.char = '%'
- elif self.hp <= 0 and self.state == STATE.DEAD: # Do nothing (May add end game function here)
- return
- class Player(Entity):
- '''Player specific entity.'''
- # Unsure how to use this just yet
- pass
- class Enemy(Entity):
- '''Enemy specific entity.'''
- def __init__(self, name, x, y, char, state=STATE.ALIVE):
- self.name = name
- self.x = x
- self.y = y
- self.hp = random.randint(5, 13) # Hp is random
- self.strength = random.randint(1, 2) # Str is random
- self.speed = random.randint(1, 2) # Spd is random
- self.char = char
- self.state = state
- def move_toward(self, target, board, pos=()):
- '''Moves enemy towards target.'''
- possible_moves = (
- (self.distance(target, (self.x, self.y + 1)), MOVE.RIGHT),
- (self.distance(target, (self.x, self.y - 1)), MOVE.LEFT),
- (self.distance(target, (self.x - 1, self.y)), MOVE.UP),
- (self.distance(target, (self.x + 1, self.y)), MOVE.DOWN))
- small_key = sorted(possible_moves)[0] # Sort list to get shortest distance
- self.move(small_key[1], board) # Move that direction
- class Board(object):
- '''Board class to create empty grid of cells.'''
- def __init__(self, width, height, entities, char='-'):
- self.width = width
- self.height = height
- self.entities = entities
- self.board = [[char] * width for _ in range(height)]
- def __str__(self):
- '''To draw the board and all entities within.'''
- self.clear_screen() # Clear to refresh
- for entity in self.entities: # For each entity in list, draw them
- self.board[entity.x][entity.y] = entity.char
- for entity in self.entities: # For UI. Looking for a better way to do this
- if entity.name == 'Player':
- print 'HP:' + str(entity.hp)
- return '\n'.join(' '.join(line) for line in self.board) # Draw entire board
- def print_char(self, ent, char='-'):
- '''Print a character to the board
- Used to clear entities previous move.'''
- self.board[ent.x][ent.y] = char
- def clear_screen(self): # Kinda obvious
- if platform.system() == 'Linux':
- os.system('clear')
- elif platform.system() == 'Windows':
- os.system('cls')
- else:
- return FLAG.CLEAR
- def is_vacant(self, x, y):
- '''Checks the board for vacant cells
- or cells occupied by corpses.'''
- if self.board[x][y] == '-' or self.board[x][y] == '%':
- return True
- else:
- return False
- def is_valid_move(self, ent, future_pos):
- '''Collision. Checks all possible moves,
- determines if the move is in the board
- area, returns a list of valid moves.'''
- moves = []
- adj_cells = [(ent.x+1, ent.y),(ent.x-1, ent.y),
- (ent.x, ent.y+1),(ent.x, ent.y-1)]
- for (x,y) in adj_cells:
- if (0 <= x <= self.width - 1 and 0 <= y <= self.height - 1
- and self.is_vacant(x, y)):
- moves.append((x,y))
- return future_pos in moves
- ##
- # Functions
- ##
- def type_text(string, sec):
- '''Text 'typing' effect'''
- for _ in string:
- #sec = random.uniform((0.1, 0.2) if sec == 'random' else sec)
- sys.stderr.write(_)
- time.sleep(random.uniform(0.1, 0.2) if sec == 'random' else sec)
- def enemy_turn(enemy, player, board):
- '''Deals with enemy turns.'''
- enemy.update() # Update enemy state
- if enemy.state == STATE.DEAD: # End turn if enemy is dead
- return # Possibly a better way to do this?
- else:
- pass # Else, continue
- distance = enemy.distance(player, (enemy.x, enemy.y)) # Get player distance
- if distance == 1 and player.state != STATE.DEAD: # If adjacent, attack
- enemy.attack(player)
- elif distance < 5 and player.state != STATE.DEAD: # Else, if player is close, move towards
- enemy.move_toward(player, board, (enemy.x, enemy.y))
- else:
- moves = [MOVE.UP, MOVE.DOWN, MOVE.LEFT, MOVE.RIGHT] # Else, just move randomly
- move = random.choice(moves)
- if not enemy.move(move, board): # If the move was invalid, get another chance
- enemy_turn(enemy, player, board)
- else:
- return FLAG.E_TURN
- def player_turn(player, enemy, board):
- player.update() # Update player state
- if player.state == STATE.DEAD: # If dead, do not take turn
- return
- else:
- pass # Else, continue
- m_actions = {
- 'move' : ['move', 'walk'],
- 'attack' : ['attack', 'hit'],
- 'die' : ['die'],
- 'kill' : ['kill'],
- 'pass' : ['pass', '']
- }
- s_actions = {
- 'move' : ['up', 'down', 'left', 'right'],
- 'attack' : ['head', 'arms', 'legs', 'torso', 'chest',]
- }
- choice = raw_input('What do you want to do? :> ').lower() # What to do?
- parts = [x.strip() for x in choice.split(',')] # Break up input into a list
- if len(parts) < 2: # If input contains only one word
- if parts[0] in m_actions['move']: # If it is move, ask for direction
- direction = raw_input('Which direction? :> ').lower()
- parts.append(direction.strip()) # Append direction into parts
- if parts[1] in s_actions['move']: # If the direction is in the sub action dict
- if not player.move(parts[1], board): # Move, if you can
- print board # If can't or bad input, retake turn
- player_turn(player, enemy, board)
- elif parts[0] in m_actions['attack']: # If input is attack
- if not player.attack(enemy): # Attack, if you can
- print board # Else, retake turn
- player_turn(player, enemy, board)
- elif parts[0] in m_actions['die']: # Just for testing
- player.state = STATE.DEAD
- elif parts[0] in m_actions['kill']: # Also for testing
- enemy.state = STATE.DEAD
- elif parts[0] in m_actions['pass']: # To pass turn
- pass
- else:
- print board # If bad input, retake turn
- player_turn(player, enemy, board)
- elif len(parts) == 2: # If parts holds two commands
- if parts[0] in m_actions['move']: # If move in first place
- if parts[1] in s_actions['move']: # If direction is in sub actions
- if not player.move(parts[1], board): # If can move, move
- print board # Else, retake turn
- player_turn(player, enemy, board)
- elif parts[0] in m_actions['attack']: # If attack in first place
- if parts[1] in s_actions['attack']: # If body part in sub actions
- if not player.attack(enemy, True, parts[1]): # If can attack, attack
- print board # Else, retake turn
- player_turn(player, enemy, board)
- else: # For any bad input, retake turn
- print board
- player_turn(player, enemy, board)
- else: # For any bad input, retake turn
- print board
- player_turn(player, enemy, board)
- def main():
- ## Title ##
- sys.stdout.write("\x1b]2;Grid_Battle V0.1\x07") # Title
- # Initiate #
- player = Player('Player', 0, 0, 10, 1, 1, 'o')
- enemy = Enemy('Enemy', 4, 4, 'x')
- objects = [player, enemy]
- board = Board(5, 5, objects)
- print board # Clear screen and print board
- while True: # Main loop
- turn = player.speed # Player turn. Amount of actions based on speed
- while turn != 0 and enemy.hp > 0: # As long as turn and hp above 0
- player_turn(player, enemy, board) # Take turn
- turn -= 1 # Remove one turn
- print board # Redraw
- turn = enemy.speed # Enemy turn.
- while turn != 0 and player.hp > 0:
- time.sleep(0.2) # Sleep for effect
- enemy_turn(enemy, player, board)
- turn -= 1
- time.sleep(0.2)
- print board
- if player.hp <= 0: # If player is dead, break
- break
- print board # To clear some text
- if enemy.state == STATE.DEAD: # If enemy is dead
- time.sleep(0.5)
- board.clear_screen()
- type_text('You won', 'random') # Crappy win screen
- type_text('!!!', 0.5)
- print # New line
- break # Leave main loop
- if player.state == STATE.DEAD: # If the player is dead
- enemy_turn(enemy, player, board) # Enemy moves endlessly
- if __name__ == '__main__':
- main()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement