Guest User

Untitled

a guest
Dec 1st, 2017
135
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. # -*- coding: utf-8 -*-
  2. """
  3.    File Name: main.py
  4.    Author: Ari Madian
  5.    Created: July 23, 2017 2:02 PM
  6.    Python Version: 3.6
  7. """
  8.  
  9. import pygame
  10. from os import path
  11. from time import sleep, time
  12. from sys import argv
  13. from datetime import datetime
  14. from random import randint, choice
  15. from copy import copy
  16. import configparser
  17.  
  18. # Proprietary Resources
  19. import functions
  20. import inputbox
  21. import config
  22. from colors_file import Color
  23.  
  24.  
  25. #TODO: Blit a portion of an image so all related sprites can be in one image
  26. #TODO: Source sprite spawn area ranges to config file
  27. #TODO: Fix caching
  28. #TODO: Fix controls thing when active collision with obstacle
  29.  
  30. pygame_init = pygame.init()
  31.  
  32. # ASSET OBJECTS AND PATHS
  33. base_path              = path.os.path.dirname(path.realpath(argv[0]))
  34. assets_base_path       = base_path + '/Assets/'
  35. fonts_path             = base_path + '/Fonts/'
  36. projectiles_path       = assets_base_path + '/projectiles'
  37. projectile             = projectiles_path + '/blue_projectile.png'
  38. font_base              = pygame.font.Font(fonts_path + 'Futura.ttf', 20)
  39. home                   = assets_base_path + '/home_area_beige.jpg'
  40. player_sprite          = assets_base_path + 'player_sprite.tiff'
  41. player_sprite_reversed = assets_base_path + 'player_sprite_reversed.tiff'
  42. sprite_bad_thing       = projectiles_path + '/mine.png'
  43. health_pack            = assets_base_path + '/healthpack.gif'
  44. dmgup                  = assets_base_path + '/damage_up.png'
  45. player_sprite_image    = pygame.image.load(player_sprite)
  46. projectile_image       = pygame.image.load(projectile)
  47. home_image             = pygame.image.load(home)
  48. bad_thing_image        = pygame.image.load(sprite_bad_thing)
  49. health_pack_image      = pygame.image.load(health_pack)
  50. dmgup_image            = pygame.image.load(dmgup)
  51. player_image_size      = player_sprite_image.get_rect().size
  52. window_title           = 'RPG Game'
  53.  
  54. ## TRACKERS
  55. # COUNTERS
  56. enemies_killed         = 0
  57. last_enemy_boss_death  = 0
  58. last_enemy_small_death = 0
  59. last_healthpack_used   = 0
  60. last_attackboost_used  = 0
  61. last_shield_used       = 0
  62. active_healthpacks     = 0
  63. active_dmgup           = 0
  64. active_shield          = 0
  65. ticks                  = 0
  66. # ACTIVE LISTS
  67. active_keys            = {'w': None, 'a': None, 's': None, 'd': None}
  68. active_projectiles     = []
  69. active_mines           = []
  70. active_enemies_small   = []
  71. active_enemies_boss    = []
  72. active_powerups        = []
  73. active_effectblits     = []
  74. active_effecttimers    = []
  75. active_effects         = []
  76. active_pillars         = []
  77. active_walls           = []
  78. active_collective      = [active_keys, active_projectiles, active_mines, active_enemies_small,
  79.                           active_enemies_boss, active_powerups, active_effectblits, active_effectblits,
  80.                           active_effecttimers, active_effects, active_pillars, active_walls]
  81. misc_blit_queue        = []
  82. active_menus           = []
  83.  
  84. font_render_cache      = []
  85. frame_times            = []
  86.  
  87. ## INITIALIZATION
  88. window_height = config.window_height
  89. window_width  = config.window_width
  90.  
  91. pygame.display.set_caption(window_title)
  92. pygame.display.set_icon(projectile_image)
  93. game_display = pygame.display.set_mode((window_width, window_height),
  94.                                        pygame.RESIZABLE)
  95.  
  96. persist_cfg = configparser.ConfigParser()
  97. persist_cfg.read('config.ini')
  98.  
  99.  
  100. start_t = time()
  101.  
  102. ## CLASSES
  103. # ENEMIES
  104. class Enemy(pygame.sprite.Sprite):
  105.  
  106.     def __init__(self, x, y, img_obj):
  107.         pygame.sprite.Sprite.__init__(self)
  108.         self.x = x
  109.         self.y = y
  110.         self.pos = (self.x, self.y)
  111.         self.facing = None
  112.         self.step = 0
  113.         self.last_attack = ticks
  114.         self.img_size = img_obj.get_rect().size
  115.         self.rect = pygame.Rect(self.pos, self.img_size)
  116.  
  117.     def attack(self, atk_type, atk_tup):
  118.         """Attacks the player
  119.        :param atk_type: Which attack the enemy should do.s
  120.        :param atk_tup: A tuple with the subclass's attack packages."""
  121.         player.recalc_center()
  122.         projectile_ = Projectile(self.pos,
  123.                                  (player.center[0], player.center[1]),
  124.                                  ticks, 'enemy',
  125.                                  atk_tup[0] if atk_type == 1 else atk_tup[1])
  126.  
  127.         active_projectiles.append(projectile_)
  128.         self.last_attack = ticks
  129.  
  130.     def blit_facing(self, sprite_tup):
  131.         if self.facing == 'right':
  132.             game_display.blit(sprite_tup[0], (self.x, self.y))
  133.         elif self.facing == 'left':
  134.             game_display.blit(sprite_tup[1], (self.x, self.y))
  135.  
  136.  
  137.     def move(self, move_dict):
  138.         if self.step >= move_dict['ticks_to_move']:
  139.             add_or_sub = ('+', '-')
  140.             if choice(add_or_sub) == '+':
  141.                 self.x += randint(move_dict['x']['min'], move_dict['x']['max'])
  142.                 self.update_rect()
  143.             else:
  144.                 self.x -= randint(move_dict['x']['min'], move_dict['x']['max'])
  145.                 self.update_rect()
  146.  
  147.             if choice(add_or_sub) == '+':
  148.                 self.y += randint(move_dict['y']['min'], move_dict['y']['max'])
  149.                 self.update_rect()
  150.             else:
  151.                 self.y -= randint(move_dict['y']['min'], move_dict['y']['max'])
  152.                 self.update_rect()
  153.             self.step = 0
  154.             self.update_rect()
  155.         self.step += 1
  156.  
  157.     def update_rect(self):
  158.         self.rect = pygame.Rect(self.x, self.y, self.img_size[0], self.img_size[1])
  159.  
  160.     def do_kill(self, kill_dict):
  161.         global last_enemy_boss_death
  162.         global last_enemy_small_death
  163.  
  164.         if kill_dict['type'] == 'boss': last_enemy_boss_death = ticks
  165.         else: last_enemy_small_death = ticks
  166.         try:
  167.             del active_enemies_small[active_enemies_small.index(self)]
  168.         except ValueError:
  169.             del active_enemies_boss[active_enemies_boss.index(self)]
  170.  
  171.         player.score += kill_dict['score_val']
  172.  
  173.     def blit_health(self, health):
  174.         game_display.blit(font_base.render('Health - ' + str(health), True, Color.Black),
  175.                           (self.x + 10, self.y - 30))
  176. class BossEnemy(Enemy):
  177.  
  178.     def __init__(self, x, y):
  179.         pygame.sprite.Sprite.__init__(self)
  180.         self.type        = 'enemy'
  181.         self.img_obj     = pygame.image.load(assets_base_path + 'enemy_boss.png')
  182.         self.health      = config.enemy_boss_health
  183.         self.atk_tup     = (config.enemy_boss_atk1, config.enemy_boss_atk2)
  184.         self.atks_dict   = {'atk1': {'freq': config.enemy_boss_atk1_freq,
  185.                                      'atk_pack': self.atk_tup[0],
  186.                                      'type': 1},
  187.                             'atk_2': {'freq': config.enemy_boss_atk2_freq,
  188.                                       'atk_pack': self.atk_tup[1],
  189.                                       'type': 2}}
  190.         self.sprite_tup  = (pygame.image.load(assets_base_path + 'enemy_boss.png'),
  191.                             pygame.image.load(assets_base_path + 'enemy_boss_reversed.png'))
  192.         self.move_dict   = config.enemy_boss_move_properties
  193.         self.kill_dict   = {'score_val': config.enemy_boss_score_val,
  194.                             'type': 'boss'}
  195.         Enemy.__init__(self, x, y, self.img_obj)
  196. class SmallEnemy(Enemy):
  197.     """The small enemy class
  198.    :param x: The x position for the enemy to be created at
  199.    :param y: The y position for the enemy to be created at
  200.    :param tick: The tick the enemy was created at"""
  201.  
  202.     def __init__(self, x, y):
  203.         pygame.sprite.Sprite.__init__(self)
  204.         self.type        = 'enemy'
  205.         self.img_obj     = pygame.image.load(assets_base_path + '/enemy_sprite.png')
  206.         self.health      = config.enemy_small_health
  207.         self.atk_tup     = (config.enemy_small_atk1, config.enemy_small_atk2)
  208.         self.atks_dict   = {'atk1': {'freq': config.enemy_small_atk1_freq,
  209.                                      'atk_pack': self.atk_tup[0],
  210.                                      'type': 1},
  211.                             'atk_2': {'freq': config.enemy_small_atk2_freq,
  212.                                       'atk_pack': self.atk_tup[1],
  213.                                       'type': 2}}
  214.         self.sprite_tup  = (pygame.image.load(assets_base_path + '/enemy_sprite.png'),
  215.                             pygame.image.load(assets_base_path + '/enemy_sprite_reversed.png'))
  216.         self.move_dict   = config.enemy_small_move_dict
  217.         self.kill_dict   = {'score_val': config.enemy_small_score_val,
  218.                             'type': 'small'}
  219.         Enemy.__init__(self, x, y, self.img_obj)
  220. # PROJECTILE
  221. class Projectile(pygame.sprite.Sprite):
  222.     """Projectile class
  223.    :param origin: The starting position for the projectile,
  224.                    the player or enemy's position
  225.    :param target: Where the player or enemy intends for the
  226.                    projectile to go, at mouseclick pos or
  227.                    player position.
  228.    :param tick: The tick the projectile was created at
  229.    :param type_: The projectile type, either 'friendly' or 'enemy'"""
  230.  
  231.     def __init__(self, origin, target, tick, type_, attack_package):
  232.         pygame.sprite.Sprite.__init__(self)
  233.         self.pos = origin
  234.         self.type = type_
  235.         self.target = target
  236.         self.atk_package = attack_package
  237.         self.damage = self.atk_package[0]
  238.         self.lifepsan = self.atk_package[2]
  239.         self.angle = functions.get_angle(self.pos, self.target)
  240.         self.speed = self.atk_package[1]
  241.         self.tickmade = tick
  242.         self.rect = pygame.Rect(self.pos[0] + 8, self.pos[1] + 7, 17, 17)
  243.  
  244.     def update(self):
  245.         if self.pos == self.target:
  246.             self.kill()
  247.         self.angle = functions.get_angle(self.pos, self.target)
  248.         self.pos = functions.project(self.pos, self.angle, self.speed)
  249.         self.rect = pygame.Rect(self.pos[0] + 8, self.pos[1] + 7, 17, 17)
  250.  
  251.     def blit(self):
  252.         if self.type == 'friendly':
  253.             game_display.blit(projectile_image, (self.pos[0], self.pos[1]))
  254.         elif self.type == 'enemy':
  255.             game_display.blit(bad_thing_image, (self.pos[0], self.pos[1]))
  256.  
  257.     def collided_with(self, sprite_rect):
  258.         return self.rect.colliderect(sprite_rect)
  259.  
  260.     def kill(self):
  261.         try:
  262.             del active_projectiles[active_projectiles.index(self)]
  263.         except ValueError:
  264.             print('ERR - Value error on projectile kill attempt')
  265. # PLAYER
  266. class Player(pygame.sprite.Sprite):
  267.     """The player class"""
  268.  
  269.     health = config.player_health
  270.     x = config.player_starting_y
  271.     y = config.player_starting_y
  272.  
  273.     def __init__(self):
  274.         pygame.sprite.Sprite.__init__(self)
  275.         self.player_name = inputbox.ask(game_display, "Enter Player Name", font_base)
  276.         self.img_verts = None
  277.         self.center = None
  278.         self.facing = None
  279.         self.score = 0
  280.         self.type = 'friendly'
  281.         self.godmode = config.player_godmode
  282.         self.atks = (config.player_atk1, config.player_atk2)
  283.         self.img_size = player_sprite_image.get_rect().size
  284.         self.last_display_effect_start = None
  285.         self.name_text = font_base.render(self.player_name, True, Color.Black)
  286.         self.rect = pygame.Rect(self.x + 8, self.y + 52, 67, 134)
  287.  
  288.     def refresh_rect(self):
  289.         self.rect = pygame.Rect(self.x + 8, self.y + 52, 67, 134)
  290.  
  291.     def attack(self, eventpos, atk_type):
  292.         self.recalc_center()
  293.         projectile_ = Projectile(self.center,
  294.                                  eventpos,
  295.                                  ticks, 'friendly',
  296.                                  self.atks[0] if atk_type == 1
  297.                                  else self.atks[1])
  298.         active_projectiles.append(projectile_)
  299.  
  300.     def blit_facing(self):
  301.         if self.facing == 'right':
  302.             game_display.blit(pygame.image.load(player_sprite), (self.x, self.y))
  303.         elif self.facing == 'left':
  304.             game_display.blit(pygame.image.load(player_sprite_reversed), (self.x, self.y))
  305.  
  306.     def blit_name(self):
  307.         game_display.blit(self.name_text,
  308.                           ((self.img_size[0] / 2) - 10, (self.img_size[1] / 2) - 25))
  309.  
  310.     def recalc_center(self):
  311.         self.center = functions.player_center((self.x, self.y), self.img_size)
  312.  
  313.     def recalc_img_verts(self):
  314.         self.img_verts = functions.player_verts((self.x, self.y), self.img_size)
  315.  
  316.     def do_display_effect(self, object_):
  317.         if object_.effect_elev <= 15:
  318.             game_display.blit(object_.display_effect_text, (self.x, self.y - 20 - object_.effect_elev * 3))
  319.             object_.effect_elev += 1
  320.         else:
  321.             object_.do_kill()
  322.  
  323.     def kill(self):
  324.         death_screen()
  325. # MINE
  326. class Mine(pygame.sprite.Sprite):
  327.     """The mine class
  328.    :param x: The x position for the mine to be created at
  329.    :param y: The y position for the mine to be created at"""
  330.  
  331.     health = config.mine_health
  332.     damage = config.mine_damage
  333.  
  334.     def __init__(self, x, y):
  335.         pygame.sprite.Sprite.__init__(self)
  336.         self.x = x
  337.         self.y = y
  338.         self.image = pygame.image.load(sprite_bad_thing)
  339.         self.rect = pygame.Rect((self.x, self.y), (32, 32))
  340.  
  341.     def blit(self):
  342.         game_display.blit(self.image, (self.x, self.y))
  343.  
  344.     def collided_with(self):
  345.         return self.rect.colliderect(player.rect)
  346.  
  347.     def kill(self):
  348.         del active_mines[active_mines.index(self)]
  349. # POWERUPS
  350. class PowerUp(pygame.sprite.Sprite):
  351.  
  352.     def __init__(self, x, y, img_obj, effect_, effect_text):
  353.         pygame.sprite.Sprite.__init__(self)
  354.         self.x = x
  355.         self.y = y
  356.         self.pos = (x, y)
  357.         self.img_obj = img_obj
  358.         self.effect = effect_
  359.         self.tickmade = ticks
  360.         self.img_size = img_obj.get_rect().size
  361.         self.effect_text = effect_text
  362.         self.rect = pygame.Rect(self.pos, self.img_size)
  363.  
  364.     def collided_with(self, sprite_rect):
  365.         return self.rect.colliderect(sprite_rect)
  366.  
  367.     def blit(self):
  368.         game_display.blit(self.img_obj, (self.x, self.y))
  369.         text = font_base.render(self.effect_text, True, Color.Black)
  370.         game_display.blit(text, ((self.x + self.img_size[0] / 2) - font_base.size(self.effect_text)[0] / 2,
  371.                                   self.y - 3 - font_base.size(self.effect_text)[1]))
  372.  
  373.     def blit_effect(self, pos):
  374.         text = font_base.render(self.effect, True, Color.Black)
  375.         game_display.blit(text, (window_width - 3 - font_base.size(self.effect)[0], 3 + pos[1]))
  376.  
  377.     def do_kill(self, kill_type):
  378.         if kill_type == 'healthpack':
  379.             global last_healthpack_used
  380.             global active_healthpacks
  381.             last_healthpack_used = ticks
  382.             active_healthpacks -= 1
  383.         elif kill_type == 'dmgup':
  384.             global last_attackboost_used
  385.             global active_dmgup
  386.             last_attackboost_used = ticks
  387.             active_dmgup -= 1
  388.         elif kill_type == 'shield':
  389.             global last_shield_used
  390.             global active_shield
  391.             last_shield_used = ticks
  392.             active_shield -= 1
  393.         try:
  394.             del active_powerups[active_powerups.index(self)]
  395.         except ValueError:
  396.             pass
  397.         if self in active_effects: del active_effects[active_effects.index(self)]
  398.  
  399.     def render_kill(self):
  400.         del active_powerups[active_powerups.index(self)]
  401. class HealthPack(PowerUp): # INSTANT POWERUP
  402.     """The class for the health pack
  403.    :param x: The x position for the pack to be created at
  404.    :param y: The y position for the pack to be created at"""
  405.  
  406.     def __init__(self, x, y):
  407.         pygame.sprite.Sprite.__init__(self)
  408.         self.img_obj = health_pack_image
  409.         self.max_active = config.max_healthpacks
  410.         self.kill_type = 'healthpack'
  411.         self.effect = '+ Health'
  412.         PowerUp.__init__(self, x, y, self.img_obj, None, self.effect)
  413.  
  414.     def do_effect(self):
  415.         player.health += config.healthpack_heal_amount
  416.         new_effectblit = EffectBlit(ticks, '+ ' + str(config.healthpack_heal_amount) + ' Health', (0, 0, 0))
  417.         active_effectblits.append(new_effectblit)
  418.         self.do_kill(self.kill_type)
  419. class DamageUp(PowerUp): # TEMPORARY POWERUP
  420.     def __init__(self,x ,y):
  421.         self.img_obj = dmgup_image
  422.         self.max_active = config.max_dmgup
  423.         self.kill_type = 'dmgup'
  424.         self.lifespan = config.dmgup_lifespan
  425.         self.active_effect = 'Damage Buff'
  426.         self.effect = '+ Damage'
  427.         PowerUp.__init__(self, x, y, self.img_obj, self.active_effect, self.effect)
  428.     def do_effect(self):
  429.         for atk in player.atks: atk[0] += config.dmgup_effect_amount
  430.  
  431.         new_effectblit = EffectBlit(ticks, 'Damage Buff', (0, 0, 0))
  432.         active_effectblits.append(new_effectblit)
  433.  
  434.         new_effecttimer = EffectTimer(self.lifespan, self)
  435.         active_effecttimers.append(new_effecttimer)
  436.         active_effects.append(self)
  437.         self.render_kill()
  438.  
  439.     @staticmethod
  440.     def undo_effect():
  441.         for atk in player.atks: atk[0] -= config.dmgup_effect_amount
  442. class Shield(PowerUp):
  443.     def __init__(self, x, y):
  444.         self.img_obj = pygame.image.load(assets_base_path + '/shield.png')
  445.         self.max_active = config.max_shield
  446.         self.kill_type = 'shield'
  447.         self.lifespan = config.shield_lifespan
  448.         self.active_effect = 'Immunity'
  449.         self.effect = 'Shield'
  450.         PowerUp.__init__(self, x, y, self.img_obj, u'Immunity', u'Immunity')
  451.  
  452.     def do_effect(self):
  453.         player.godmode = True
  454.         new_effectblit = EffectBlit(ticks, 'Shield', (0, 0, 0))
  455.         active_effectblits.append(new_effectblit)
  456.         new_effecttimer = EffectTimer(self.lifespan, self)
  457.         active_effecttimers.append(new_effecttimer)
  458.         active_effects.append(self)
  459.         self.render_kill()
  460.  
  461.     @staticmethod
  462.     def undo_effect():
  463.         player.godmode = False if config.player_godmode == False else True
  464. # EFFECT HELPERS
  465. class EffectBlit:
  466.     def __init__(self, tickmade, effect_type, color):
  467.         self.tickmade = tickmade
  468.         self.effect_elev = 0
  469.         self.effect_text = effect_type
  470.         self.color = color
  471.         self.display_effect_text = font_base.render(self.effect_text, True, self.color)
  472.  
  473.     def do_kill(self):
  474.         del active_effectblits[active_effectblits.index(self)]
  475. class EffectTimer: # ONLY USED IN TEMPORARY POWERUPS
  476.     def __init__(self, lifespan, parent_obj):
  477.         self.tickmade = ticks
  478.         self.lifespan = lifespan
  479.         self.parent = parent_obj
  480.  
  481.     def check_timer(self):
  482.         if ticks - self.tickmade >= self.lifespan:
  483.             self.parent.undo_effect()
  484.             self.do_kill()
  485.  
  486.     def do_kill(self):
  487.         self.parent.do_kill(self.parent.kill_type)
  488.         del active_effecttimers[active_effecttimers.index(self)]
  489.  
  490. class Obstacle(pygame.sprite.Sprite):
  491.     def __init__(self, x, y, rect, img):
  492.         self.x = x
  493.         self.y = y
  494.         self.rect = rect
  495.         self.img_obj = img
  496.         pygame.sprite.Sprite.__init__(self)
  497.  
  498.     def collided_with(self, sprite_rect):
  499.         return self.rect.colliderect(sprite_rect)
  500.  
  501.     def blit(self):
  502.         try:
  503.             game_display.blit(self.img_obj, (self.x, self.y))
  504.         except TypeError as e:
  505.             # print('TypeError in Obstacle Blit')
  506.             # print(e)
  507.             pass
  508. class Pillar(Obstacle):
  509.     def __init__(self, x, y):
  510.         self.img_obj = pygame.image.load(assets_base_path + '/pillar.png')
  511.         self.img_size = self.img_obj.get_rect().size
  512.         self.rect = pygame.Rect(x + 6, y + 19, 49, 107)
  513.         Obstacle.__init__(self, x, y, self.rect, self.img_obj)
  514. class Wall(Obstacle):
  515.     def __init__(self, x, y):
  516.         self.img_obj = pygame.image.load(assets_base_path + '/wall.png')
  517.         self.rect = pygame.Rect(x + 6, y + 19, 49, 107)
  518.         Obstacle.__init__(self, x, y, self.rect, self.img_obj)
  519. class InvisWall(Obstacle):
  520.     def __init__(self, x, y, width, height):
  521.         self.rect = pygame.Rect(x, y, width, height)
  522.         Obstacle.__init__(self, x, y, self.rect, None)
  523.  
  524. class Menu(pygame.sprite.Sprite):
  525.     def __init__(self):
  526.         self.active = False
  527.         pygame.sprite.Sprite.__init__(self)
  528.  
  529.     def set_active(self):
  530.         pass
  531. class SettingsMenu(Menu):
  532.     def __init__(self):
  533.         self.menu_width = 400
  534.         self.menu_height = 400
  535.         self.title = font_base.render('Settings', True, (255, 255, 255))
  536.         self.toggled_true_img = pygame.image.load(assets_base_path + '/checkboxes_False.png')
  537.         self.collapsed_img = pygame.image.load(assets_base_path + '/checkboxes_True.png')
  538.         self.collapsed_img_size = self.collapsed_img.get_rect().size
  539.         self.pos = (window_width - self.collapsed_img_size[0], 0)
  540.         self.collapsed_rect = pygame.Rect(self.pos[0], self.pos[1], self.collapsed_img_size[0], self.collapsed_img_size[1])
  541.         self.expanded_rect = pygame.Rect(window_width - self.menu_width, 0, self.menu_width, self.menu_height)
  542.         self.settings_rects = [pygame.Rect(self.pos[0] + 30, self.pos[1] + 30, 32, 32)]
  543.         self.settings_deps = {'player_godmode': [persist_cfg['Player'].getboolean('player_godmode'), False],
  544.                               'render_player_verts': [persist_cfg['RuntimeSettings'].getboolean('render_player_verts'), True],
  545.                               'render_hitboxes': [persist_cfg['RuntimeSettings'].getboolean('render_hitboxes'), False]}
  546.         self.settings = {'GodMode': {'rect': pygame.Rect(window_width - 390, 0 * 45 + 35,
  547.                                                          32, 32),
  548.                                      'toggle_action': {'Section': 'Player', 'Setting': 'player_godmode', 'Value': self.settings_deps['player_godmode'][0]}},
  549.  
  550.                          'Render_player_verts': {'rect': pygame.Rect(window_width - 390, 1 * 45 + 35,
  551.                                                                      32, 32),
  552.                                                   'toggle_action': {'Section': 'RuntimeSettings', 'Setting': 'render_player_verts', 'Value': self.settings_deps['render_player_verts'][0]}},
  553.  
  554.                          'Render_hitboxes': {'rect': pygame.Rect(window_width - 390, 2 * 45 + 35,
  555.                                                          32, 32),
  556.                                              'toggle_action': {'Section': 'RuntimeSettings', 'Setting': 'render_hitboxes', 'Value': self.settings_deps['render_hitboxes'][0]}}}
  557.         Menu.__init__(self)
  558.  
  559.  
  560.     def update_settings(self, section, setting, value):
  561.         print('Settings Change')
  562.         print(section)
  563.         print(setting)
  564.         print(not value)
  565.  
  566.         persist_cfg.set(str(section), str(setting), str(not value))
  567.         self.settings_deps[setting][1] = not self.settings_deps[setting][1]
  568.  
  569.     def blit(self):
  570.         if self.active:
  571.             count = 0
  572.             backrect = pygame.Surface((self.expanded_rect[2], self.expanded_rect[3]))
  573.             backrect.set_alpha(150)
  574.             backrect.fill((0, 0, 0))
  575.             game_display.blit(backrect, (window_width - self.menu_width, 0))
  576.             game_display.blit(self.title, (window_width - self.menu_width + 10, 10))
  577.  
  578.             for key, value in self.settings.items():
  579.                 game_display.blit(self.toggled_true_img if self.settings_deps[value['toggle_action']['Setting']][1] else self.collapsed_img, (window_width - 390, count * 45 + 35))
  580.                 game_display.blit(font_base.render(str(key), True, (255, 255, 255)), (window_width - 343, count * 45 + 35))
  581.                 count += 1
  582.         else:
  583.             game_display.blit(self.collapsed_img, self.pos)
  584.  
  585.     def check_collision(self, eventpos):
  586.         return self.collapsed_rect.collidepoint(eventpos[0], eventpos[1])
  587.  
  588.     def check_expanded_collision(self, eventpos):
  589.         return self.expanded_rect.collidepoint(eventpos[0], eventpos[1])
  590.  
  591.     def check_which_setting_toggle(self, eventpos):
  592.         for _, subdict in self.settings.items():
  593.             if subdict['rect'].collidepoint(eventpos[0], eventpos[1]):
  594.                 self.update_settings(subdict['toggle_action']['Section'],
  595.                                      subdict['toggle_action']['Setting'],
  596.                                      subdict['toggle_action']['Value'])
  597.  
  598.     def toggle_active(self):
  599.         if self.active:
  600.             self.active = False
  601.         else:
  602.             self.active = True
  603.  
  604.  
  605. settings_menu = SettingsMenu()
  606. active_menus.append(settings_menu)
  607.  
  608. pillar = Pillar(200, 500)
  609. active_pillars.append(pillar)
  610. pillar = Pillar(800, 500)
  611. active_pillars.append(pillar)
  612. pillar = Pillar(200, 100)
  613. active_pillars.append(pillar)
  614. pillar = Pillar(800, 100)
  615. active_pillars.append(pillar)
  616.  
  617. ## Init for border walls
  618. inviswall = InvisWall(-1, -1, window_width + 1, 1)
  619. active_walls.append(inviswall)
  620. inviswall = InvisWall(window_width + 1, -1, 1, 900)
  621. active_walls.append(inviswall)
  622. inviswall = InvisWall(-1, window_height + 1, 1800, 1)
  623. active_walls.append(inviswall)
  624. inviswall = InvisWall(-1, -1, 1, window_height + 1)
  625. active_walls.append(inviswall)
  626.  
  627.  
  628. player = Player()
  629.  
  630. def death_screen():
  631.     quit() # Temporary
  632.  
  633. def title_screen():
  634.     background = assets_base_path + 'title_screen.png'
  635.     font_size = 0
  636.     frames = 0
  637.     enter_game = False
  638.     while not enter_game:
  639.         for event_ in pygame.event.get():
  640.             if event_.type == pygame.QUIT:
  641.                 quit()
  642.             elif event_.type == pygame.KEYDOWN:
  643.                 if event_.key == pygame.K_KP_ENTER or pygame.K_SPACE:
  644.                     enter_game = True
  645.             # print(event_)
  646.  
  647.         game_display.blit(pygame.image.load(background), (0, 0))
  648.         font = pygame.font.Font(fonts_path + 'Futura.ttf', font_size)
  649.         game_display.blit(font.render(str('An RPG'), True, Color.Black), (500, 111))
  650.         # game_display.blit(font.render(str(frames), True, Color.Goldenrod), (0, 0))
  651.  
  652.         if font_size < 65: font_size += 1
  653.         if font_size == 65:
  654.             game_display.blit(font.render(str('Press Enter To Play'), True, Color.Black), (345, 400))
  655.  
  656.         pygame.display.update()
  657.         frames += 1
  658.         sleep(0.013)
  659.  
  660. def kill_all_keys():
  661.     for _, value in active_keys.items():
  662.         active_keys[_] = False
  663.  
  664. def remake_inviswalls(w, h):
  665.     del active_walls[:]
  666.     wall_ = InvisWall(-1, -1, w + 1, 1)
  667.     active_walls.append(wall_)
  668.     wall_ = InvisWall(window_width + 1, -1, 1, h + 1)
  669.     active_walls.append(wall_)
  670.     wall_ = InvisWall(-1, h + 1, w + 1, 1)
  671.     active_walls.append(wall_)
  672.     wall_ = InvisWall(-1, -1, 1, h + 1)
  673.     active_walls.append(wall_)
  674.  
  675.  
  676.  
  677.  
  678. title_screen()
  679. gameExit = False
  680. while not gameExit:
  681.     # print((player.x, player.y))
  682.     # print(player.score)
  683.     persist_cfg.read('config.ini')
  684.     tick_start_time = datetime.now()
  685.     ticks += 1
  686.  
  687.     ## ON EVENT
  688.     for event in pygame.event.get():
  689.         pygame.event.set_allowed([pygame.QUIT, pygame.KEYDOWN, pygame.KEYUP,
  690.                                   pygame.MOUSEMOTION, pygame.MOUSEBUTTONUP,
  691.                                   pygame.MOUSEBUTTONDOWN])
  692.         if event.type == pygame.QUIT:
  693.             gameExit = True
  694.  
  695.         if event.type == pygame.VIDEORESIZE:
  696.             window_height = event.h
  697.             window_width = event.w
  698.             game_display = pygame.display.set_mode((window_width, window_height), pygame.RESIZABLE)
  699.             home_image = pygame.transform.scale(home_image, (window_width, window_height))
  700.             remake_inviswalls(event.w, event.h)
  701.  
  702.         ## Character Movement
  703.         if event.type == pygame.KEYDOWN:
  704.             if event.key == pygame.K_w: active_keys['w'] = True
  705.             elif event.key == pygame.K_a: active_keys['a'] = True
  706.             elif event.key == pygame.K_s: active_keys['s'] = True
  707.             elif event.key == pygame.K_d: active_keys['d'] = True
  708.             elif event.key == pygame.K_r: kill_all_keys()
  709.         if event.type == pygame.KEYUP:
  710.             if event.key == pygame.K_w: active_keys['w'] = False
  711.             elif event.key == pygame.K_a: active_keys['a'] = False
  712.             elif event.key == pygame.K_s: active_keys['s'] = False
  713.             elif event.key == pygame.K_d: active_keys['d'] = False
  714.  
  715.         ## Projectiles and Targeting
  716.         # Making the projectile
  717.         if event.type == pygame.MOUSEBUTTONDOWN:
  718.             if settings_menu.check_collision(event.pos):
  719.                 settings_menu.toggle_active()
  720.             elif settings_menu.check_expanded_collision(event.pos):
  721.                 settings_menu.check_which_setting_toggle(event.pos)
  722.             else:
  723.                 if pygame.mouse.get_pressed()[0] == 1:
  724.                     player.attack(event.pos, 1)
  725.                 elif pygame.mouse.get_pressed()[2] == 1:
  726.                     pass
  727.  
  728.         # Player Facing
  729.         if event.type == pygame.MOUSEMOTION:
  730.             if event.pos[0] > player.x: player.facing = 'right'
  731.             else: player.facing = 'left'
  732.  
  733.  
  734.     if player.health <= 0 and player.godmode is False:
  735.         player.kill()
  736.  
  737.     ## ENEMY ACTIONS
  738.     for enemy in active_enemies_small + active_enemies_boss:
  739.         # ATTACKING
  740.         for __, attack in enemy.atks_dict.items():
  741.             if ticks - enemy.last_attack >= attack['freq']:
  742.                 enemy.attack(attack['type'], enemy.atk_tup)
  743.                 enemy.last_attack = ticks
  744.  
  745.         # HEALTH CHECK
  746.         if enemy.health <= 0:
  747.             enemy.do_kill(enemy.kill_dict)
  748.             enemies_killed += 1
  749.             print('TASK - Enemy killed - ' + str(enemies_killed) + ' Total')
  750.  
  751.         # FACING
  752.         if player.x > enemy.x: enemy.facing = 'right'
  753.         elif player.x < enemy.x: enemy.facing = 'left'
  754.  
  755.         enemy.move(enemy.move_dict)
  756.  
  757.  
  758.     ## SPAWNING
  759.     if config.enable_enemy_spawning:
  760.         # BOSSES
  761.         if ticks - last_enemy_boss_death > config.enemy_boss_create_freq and len(active_enemies_boss) <= config.max_enemy_boss:
  762.             boss = BossEnemy(randint(200, 1000), randint(200, 600))
  763.             active_enemies_boss.append(boss)
  764.  
  765.         # SMALL ENEMIES
  766.         if ticks - last_enemy_small_death > config.enemy_small_create_freq and len(active_enemies_small) <= config.max_enemy_small:
  767.             smallenemy = SmallEnemy(randint(200, 1000), randint(200, 600))
  768.             active_enemies_small.append(smallenemy)
  769.  
  770.     # HEALTH PACKS
  771.     if ticks - last_healthpack_used > config.healthpack_create_freq and active_healthpacks <= config.max_healthpacks:
  772.         pack = HealthPack(randint(60, 1140), randint(40, 760))
  773.         active_healthpacks += 1
  774.         active_powerups.append(pack)
  775.  
  776.     # ATKUP
  777.     if ticks - last_attackboost_used > config.dmgup_create_freq and active_dmgup <= config.max_dmgup:
  778.         buff = DamageUp(randint(60, 1140), randint(40, 760))
  779.         active_dmgup += 1
  780.         active_powerups.append(buff)
  781.  
  782.     # SHIELD
  783.     if ticks - last_shield_used > config.shield_create_freq and active_shield <= config.max_shield:
  784.         if randint(0, 5) == 1:
  785.             shield = Shield(randint(60, 1140), randint(40, 760))
  786.             active_shield += 1
  787.             active_powerups.append(shield)
  788.  
  789.  
  790.     ## MOVEMENT, COLLISION, AND FPS
  791.     rect_ = copy(player)
  792.     if active_keys['w']: rect_.y -= config.player_movespeed_vertical
  793.     if active_keys['s']: rect_.y += config.player_movespeed_vertical
  794.     if active_keys['a']: rect_.x -= config.player_movespeed_horizontal
  795.     if active_keys['d']: rect_.x += config.player_movespeed_horizontal
  796.     rect_.refresh_rect()
  797.     pillar_player_collisions_list = pygame.sprite.spritecollide(rect_, active_pillars, False)
  798.     wall_player_collisions_list   = pygame.sprite.spritecollide(rect_, active_walls, False)
  799.     if len(pillar_player_collisions_list + wall_player_collisions_list) != 0:
  800.         # print('Collisions With Pillars')
  801.         pass
  802.     else:
  803.         # print('No Collisions With Pillars')
  804.         if active_keys['w']: player.y -= config.player_movespeed_vertical
  805.         if active_keys['s']: player.y += config.player_movespeed_vertical
  806.         if active_keys['a']: player.x -= config.player_movespeed_horizontal
  807.         if active_keys['d']: player.x += config.player_movespeed_horizontal
  808.         player.refresh_rect()
  809.  
  810.     # PROJECTILE COLLISION SCANNING
  811.     # PROJECTILES
  812.     # Update projectile position
  813.     for projectile in active_projectiles:
  814.         projectile.update()
  815.         if (ticks - projectile.tickmade) > projectile.atk_package[2] or \
  816.                         projectile.pos == projectile.target:
  817.             projectile.kill()
  818.  
  819.         # If enemy projectile collides with player
  820.         if projectile.collided_with(player.rect):
  821.             if projectile.type == 'enemy':
  822.                 if player.godmode is False:
  823.                     print('Godmode Off')
  824.                     player.health = player.health - projectile.damage
  825.                     effect = EffectBlit(ticks, '- ' + str(projectile.damage) + ' Health', (255, 0, 0))
  826.                     active_effectblits.append(effect)
  827.                 projectile.kill()
  828.  
  829.         # If player projectile collides with enemy
  830.         if projectile.type != 'enemy':
  831.             collisionslist = pygame.sprite.spritecollide(projectile, active_enemies_small, False)
  832.             collisions_list = collisionslist + pygame.sprite.spritecollide(projectile, active_enemies_boss, False)
  833.             # if len(collisionslist) != 0: print(collisionslist)
  834.             if len(collisions_list) != 0:
  835.                 for enemy in collisions_list:
  836.                     enemy.health = enemy.health - projectile.damage
  837.                 projectile.kill()
  838.  
  839.         pillar_projs_collisions_list = pygame.sprite.spritecollide(projectile, active_pillars, False)
  840.         if len(pillar_projs_collisions_list) != 0:
  841.             projectile.kill()
  842.  
  843.     # MINES
  844.     if len(active_mines) != 0:
  845.         for mine in active_mines:
  846.             if mine.collided_with():
  847.                 player.health = player.health - mine.damage
  848.                 mine.kill()
  849.  
  850.     # HEALTHPACKS
  851.     if len(active_powerups) != 0:
  852.         for powerup in active_powerups:
  853.             if powerup.collided_with(player.rect):
  854.                 powerup.do_effect()
  855.  
  856.     if len(active_effecttimers) != 0:
  857.         for timer in active_effecttimers:
  858.             timer.check_timer()
  859.  
  860.  
  861.     end_t = time()
  862.     time_taken = end_t - start_t
  863.     start_t = end_t
  864.     frame_times.append(time_taken)
  865.     frame_times = frame_times[-20:]
  866.     fps = int(len(frame_times) / sum(frame_times))
  867.     fps_string = 'FPS - ' + str(fps)
  868.  
  869.  
  870.     ## Rendering
  871.     '''
  872.    if ticks <= 5:
  873.        player_health = font_base.render('Health - ' + str(player.health), True, Color.Black)
  874.        font_render_cache.append(player_health)
  875.    if player.health != tick_cache[1]:
  876.        player_health   = font_base.render('Health - ' + str(player.health), True, Color.Black)
  877.        font_render_cache[2] = player_health
  878.        '''
  879.  
  880.     player_health = font_base.render('Health - ' + str(player.health), True, Color.Black)
  881.     player_score = font_base.render('Score - ' + str(player.score), True, Color.Black)
  882.     fps_text = font_base.render(fps_string, True, Color.Black)
  883.     game_display.blit(home_image, (0,0))
  884.     game_display.blit(fps_text,
  885.                       (window_width - font_base.size(fps_string)[0] - 15,
  886.                        window_height - font_base.size(fps_string)[1] - 3))
  887.     game_display.blit(player_health, (0, 0))
  888.     game_display.blit(player_score, (0, 24))
  889.  
  890.     player.blit_facing()
  891.  
  892.     active_collective = [active_projectiles, active_mines, active_powerups,
  893.                          active_pillars, active_walls]
  894.  
  895.     count = 0
  896.     for list_ in active_collective:
  897.         count += 1
  898.         if len(list_) != 0:
  899.             for thing_ in list_:
  900.                 thing_.blit()
  901.  
  902.     if len(active_enemies_small + active_enemies_boss) != 0:
  903.         for enemy in active_enemies_small + active_enemies_boss:
  904.             enemy.blit_facing(enemy.sprite_tup)
  905.             enemy.blit_health(enemy.health)
  906.  
  907.     if len(active_effectblits) != 0:
  908.         for effect in active_effectblits:
  909.             player.do_display_effect(effect)
  910.  
  911.     if len(active_effects) != 0:
  912.         i = 0
  913.         for effect in active_effects:
  914.             effect.blit_effect((None, i * 20 + 3 if i >= 1 else 0))
  915.             i += 1 # Spacing between effects
  916.  
  917.  
  918.     settings_menu.blit()
  919.  
  920.     # OPTIONAL, TO ENABLE, SEE CONFIG FILE SETTINGS
  921.     # To monitor player verts
  922.     if persist_cfg['RuntimeSettings'].getboolean('render_hitboxes'):
  923.         if len(active_pillars) != 0:
  924.             for pillar in active_pillars:
  925.                 s = pygame.Surface((pillar.rect[2], pillar.rect[3]))
  926.                 s.set_alpha(150)
  927.                 s.fill((255, 0, 0))
  928.                 game_display.blit(s, (pillar.rect[0], pillar.rect[1]))
  929.  
  930.         if len(active_enemies_small + active_enemies_boss) != 0:
  931.             for enemy in active_enemies_small + active_enemies_boss:
  932.                 s = pygame.Surface((enemy.rect[2], enemy.rect[3]))
  933.                 s.set_alpha(150)
  934.                 s.fill((255, 0, 0))
  935.                 game_display.blit(s, (enemy.rect[0], enemy.rect[1]))
  936.  
  937.         if len(active_walls) != 0:
  938.             for inviswall in active_walls:
  939.                 s = pygame.Surface((inviswall.rect[2], inviswall.rect[3]))
  940.                 s.set_alpha(150)
  941.                 s.fill((255, 255, 0))
  942.                 game_display.blit(s, (inviswall.rect[0], inviswall.rect[1]))
  943.  
  944.         if len(active_projectiles) != 0:
  945.             for projectile in active_projectiles:
  946.                 s = pygame.Surface((projectile.rect[2], projectile.rect[3]))
  947.                 s.set_alpha(150)
  948.                 s.fill((0, 0, 0))
  949.                 game_display.blit(s, (projectile.rect[0], projectile.rect[1]))
  950.  
  951.         s = pygame.Surface((player.rect[2], player.rect[3]))
  952.         s.set_alpha(150)        
  953.         s.fill((255, 0, 0))
  954.         game_display.blit(s, (player.rect[0], player.rect[1]))
  955.  
  956.  
  957.     if config.render_player_verts:
  958.         for _, coords in player.img_verts.items():
  959.             pygame.draw.circle(game_display, Color.Goldenrod, coords, 10)
  960.  
  961.  
  962.     pygame.display.update()
  963.  
  964.     ''' For monitoring tick times and fps for performance eval
  965.    tick_end_time = datetime.now()
  966.    tick_time = tick_end_time - tick_start_time
  967.    # print(str(tick_time) + ' fps - ' + str(fps))
  968.    with open('tick_times.txt', 'a') as f:
  969.        f.write(str(tick_time)[6:] + 'fps - %s' + '\n') %fps
  970.    '''
  971. pygame.quit()
  972. quit()
RAW Paste Data