Repository: MaxwellSalmon/DUGA Branch: master Commit: 8e9ff611a3a8 Files: 109 Total size: 344.0 KB Directory structure: gitextract_lqp_ktvm/ ├── .gitignore ├── EFFECTS.py ├── ENTITIES.py ├── GENERATION.py ├── GUNS.py ├── HUD.py ├── INVENTORY.py ├── ITEMS.py ├── LEVELS.py ├── LICENSE.txt ├── LevelEditor.py ├── MAIN.py ├── MAIN.spec ├── MAP.py ├── MENU.py ├── MUSIC.py ├── Manuals.txt ├── NPC.py ├── PATHFINDING.py ├── PLAYER.py ├── RAYCAST.py ├── README.md ├── SEGMENTS.py ├── SETTINGS.py ├── SOUND.py ├── SPRITES.py ├── TEXT.py ├── TEXTURES.py ├── TUTORIAL.py ├── data/ │ └── CrashReport.log └── sounds/ ├── music/ │ ├── hard_layer.ogg │ └── soft_layer.ogg ├── npcs/ │ ├── blurry_zombie_attack.ogg │ ├── blurry_zombie_die1.ogg │ ├── blurry_zombie_die2.ogg │ ├── blurry_zombie_hurt1.ogg │ ├── blurry_zombie_hurt2.ogg │ ├── blurry_zombie_hurt3.ogg │ ├── blurry_zombie_spot.ogg │ ├── ninja_attack.ogg │ ├── ninja_die1.ogg │ ├── ninja_die2.ogg │ ├── ninja_hurt1.ogg │ ├── ninja_hurt2.ogg │ ├── ninja_hurt3.ogg │ ├── ninja_hurt4.ogg │ ├── soldier_die.ogg │ ├── soldier_hurt1.ogg │ ├── soldier_hurt2.ogg │ ├── soldier_hurt3.ogg │ ├── soldier_hurt4.ogg │ ├── soldier_shoot.ogg │ ├── soldier_shoot_heavy.ogg │ ├── soldier_spot.ogg │ ├── zombie_attack.ogg │ ├── zombie_die1.ogg │ ├── zombie_die2.ogg │ ├── zombie_hurt1.ogg │ ├── zombie_hurt2.ogg │ ├── zombie_hurt3.ogg │ ├── zombie_spot1.ogg │ └── zombie_spot2.ogg ├── other/ │ ├── blub.ogg │ ├── button.ogg │ ├── damage.ogg │ ├── door_close.ogg │ ├── door_open.ogg │ ├── hitmarker.ogg │ ├── next_level.ogg │ └── none.ogg └── weapons/ ├── AK_click1.ogg ├── AK_click2.ogg ├── AK_magin1.ogg ├── AK_magin2.ogg ├── AK_magout1.ogg ├── AK_magout2.ogg ├── AK_shot1.ogg ├── AK_shot2.ogg ├── AK_shot3.ogg ├── AK_shot4.ogg ├── AK_shot5.ogg ├── gauss_shot1.ogg ├── gauss_shot2.ogg ├── gauss_shot3.ogg ├── hpp_shot1.ogg ├── hpp_shot2.ogg ├── hpp_shot3.ogg ├── knife_swing1.ogg ├── knife_swing2.ogg ├── knife_swing3.ogg ├── pistol_magin1.ogg ├── pistol_magin2.ogg ├── pistol_magout1.ogg ├── pistol_magout2.ogg ├── pistol_shot1.ogg ├── pistol_shot2.ogg ├── pistol_shot3.ogg ├── sgp_shot1.ogg ├── sgp_shot2.ogg ├── sgp_shot3.ogg ├── shotgun_magin1.ogg ├── shotgun_magin2.ogg ├── shotgun_magout1.ogg ├── shotgun_magout2.ogg ├── shotgun_shot1.ogg ├── shotgun_shot2.ogg ├── shotgun_shot3.ogg ├── shotgun_shot4.ogg └── universal_click.ogg ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ __pycache__/ Andet/ build/ dist/ pyinstaller.exe ================================================ FILE: EFFECTS.py ================================================ import pygame import SETTINGS import ITEMS import TEXT import random title = TEXT.Text(0,0, "None :-)", SETTINGS.BLACK, "DUGAFONT.ttf", 60) author = TEXT.Text(0,0, "None :-)", SETTINGS.BLACK, "DUGAFONT.ttf", 40) hurt_intensity = 128 dead_intensity = 0 heal_intensity = 85 armor_intensity = 85 fade_value = 0 title_timer = 0 int_to_string = { 0 : 'FIRST', 1 : 'SECOND', 2 : 'THIRD', 3 : 'FOURTH', 4 : 'FIFTH', 5 : 'SIXTH', 6 : 'SEVENTH', 7 : 'EIGHTH', 8 : 'NINTH', 9 : 'TENTH', 10 : 'ELEVENTH', 11 : 'TWELVTH', 12 : 'THIRTEENTH', 13 : 'FOURTEENTH', 14 : 'FIFTEENTH', 15 : 'SIXTEENTH', 16 : 'SEVENTEENTH', 17 : 'EIGHTTEENTH', 18 : 'NINETEENTH', 19 : 'TWENTIETH', } def render(canvas): if SETTINGS.screen_shake > 0: screen_shake() if SETTINGS.player_states['hurt'] or SETTINGS.player_states['dead']: player_hurt(canvas) if SETTINGS.player_states['heal']: player_heal(canvas) if SETTINGS.player_states['armor']: player_armor(canvas) if SETTINGS.player_states['fade'] or SETTINGS.player_states['black']: fade_black(canvas) if SETTINGS.player_states['title']: show_title(canvas) def screen_shake(): if SETTINGS.screen_shake > 0: SETTINGS.axes = (random.randint(-SETTINGS.screen_shake,SETTINGS.screen_shake), random.randint(-SETTINGS.screen_shake,SETTINGS.screen_shake)) SETTINGS.screen_shake /= 2 SETTINGS.screen_shake = int(SETTINGS.screen_shake) if SETTINGS.screen_shake == 0: SETTINGS.screen_shake = 0 SETTINGS.axes = (0,0) def player_hurt(canvas): global hurt_intensity, dead_intensity blood = pygame.Surface((SETTINGS.canvas_actual_width, SETTINGS.canvas_target_height)).convert_alpha() if SETTINGS.player_states['hurt']: blood.fill((255, 0, 0, max(min(hurt_intensity, 255), 0))) hurt_intensity = int(hurt_intensity / (2-SETTINGS.dt)) if hurt_intensity == 0: SETTINGS.player_states['hurt'] = False hurt_intensity = 128 elif SETTINGS.player_states['dead']: blood.fill((255, 0, 0, dead_intensity)) if dead_intensity <= 120: dead_intensity += 10 canvas.blit(blood, (0,0)) def player_heal(canvas): global heal_intensity heal = pygame.Surface((SETTINGS.canvas_actual_width, SETTINGS.canvas_target_height)).convert_alpha() heal.fill((0, 255, 0, heal_intensity)) heal_intensity = int(heal_intensity / (2-SETTINGS.dt)) if heal_intensity == 0: SETTINGS.player_states['heal'] = False heal_intensity = 85 canvas.blit(heal, (0,0)) def player_armor(canvas): global armor_intensity armor = pygame.Surface((SETTINGS.canvas_actual_width, SETTINGS.canvas_target_height)).convert_alpha() armor.fill((0, 0, 225, armor_intensity)) armor_intensity = int(armor_intensity / (2-SETTINGS.dt)) if armor_intensity == 0: SETTINGS.player_states['armor'] = False armor_intensity = 85 canvas.blit(armor, (0,0)) def fade_black(canvas): global fade_value black = pygame.Surface((SETTINGS.canvas_actual_width, SETTINGS.canvas_target_height)).convert_alpha() black.fill((0, 0, 0, max(0, min(fade_value, 255)))) if SETTINGS.player_states['fade'] and not SETTINGS.player_states['black']: if fade_value < 400: fade_value += 15 else: SETTINGS.player_states['black'] = True SETTINGS.player_states['fade'] = False elif SETTINGS.player_states['fade'] and SETTINGS.player_states['black']: if fade_value > 0: fade_value -= 20 elif fade_value <= 0: fade_value = 0 SETTINGS.player_states['black'] = False SETTINGS.player_states['fade'] = False canvas.blit(black, (0,0)) def show_title(canvas): global title_timer, title, author, white_titles, white_authors, int_to_string if SETTINGS.levels_list == SETTINGS.clevels_list or SETTINGS.levels_list == SETTINGS.tlevels_list: title.update_string(SETTINGS.levels_list[SETTINGS.current_level].name) title.update_pos((SETTINGS.canvas_actual_width/2)-(title.layout.get_width()/2)+8, 200) white_box = pygame.Surface((title.layout.get_width()+5, title.layout.get_height()+5)).convert_alpha() white_box.fill((255,255,255,180)) author.update_string("BY %s" % SETTINGS.levels_list[SETTINGS.current_level].author) author.update_pos((SETTINGS.canvas_actual_width/2)-(author.layout.get_width()/2)+8, 262) white_box2 = pygame.Surface((author.layout.get_width()+5, author.layout.get_height()-16)).convert_alpha() white_box2.fill((255,255,255,180)) if title_timer <= 3: canvas.blit(white_box2, (author.posx-7, author.posy+3)) author.draw(canvas) elif SETTINGS.levels_list == SETTINGS.glevels_list: title.update_pos((SETTINGS.canvas_actual_width/2)-(title.layout.get_width()/2)+8, 200) if SETTINGS.current_level in int_to_string: title.update_string("%s LEVEL" % int_to_string[SETTINGS.current_level]) else: title.update_string("LEVEL %s" % (SETTINGS.current_level + 1)) white_box = pygame.Surface((title.layout.get_width()+5, title.layout.get_height()+5)).convert_alpha() white_box.fill((255,255,255,180)) if title_timer <= 3: canvas.blit(white_box, (title.posx-7, title.posy-8)) title.draw(canvas) title_timer += SETTINGS.dt else: SETTINGS.player_states['title'] = False title_timer = 0 ================================================ FILE: ENTITIES.py ================================================ import SETTINGS import GUNS import NPC import ITEMS from os import * import pygame import copy import random #When creating guns, remember to create an item for the gun as well. def load_guns(): #AK 47 - 0 SETTINGS.gun_list.append(GUNS.Gun( {'spritesheet': path.join('graphics', 'weapon', 'ak_spritesheet.png'), 'item': path.join('graphics', 'items', 'akitem.png') },{ 'dmg' : 3, 'spread' : 50, 'hitchance': 80, 'firerate': 0.08, 'range': 10, 'magsize': 30, 'rlspeed': 1, 'zoom': 6, 'ammotype': 'bullet', 'guntype': 'primary', 'name': 'AK-47' },{ 'shot': [pygame.mixer.Sound(path.join('sounds', 'weapons', 'AK_shot1.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'AK_shot2.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'AK_shot3.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'AK_shot4.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'AK_shot5.ogg'))], 'click': [pygame.mixer.Sound(path.join('sounds', 'weapons', 'AK_click1.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'AK_click2.ogg'))], 'magout': [pygame.mixer.Sound(path.join('sounds', 'weapons', 'AK_magout1.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'AK_magout2.ogg'))], 'magin': [pygame.mixer.Sound(path.join('sounds', 'weapons', 'AK_magin1.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'AK_magin2.ogg'))] },(35,7))) #Double Barrel Shotgun - 1 SETTINGS.gun_list.append(GUNS.Gun( {'spritesheet': path.join('graphics', 'weapon', 'shotgun_spritesheet.png'), 'item': path.join('graphics', 'items', 'shotgun.png') },{ 'dmg' : 10, 'spread' : 200, 'hitchance': 65, 'firerate': 0.3, 'range': 7, 'magsize': 2, 'rlspeed': 1.4, 'zoom': 8, 'ammotype': 'shell', 'guntype': 'primary', 'name': 'DB Shotgun' },{ 'shot': [pygame.mixer.Sound(path.join('sounds', 'weapons', 'shotgun_shot1.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'shotgun_shot2.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'shotgun_shot3.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'shotgun_shot4.ogg'))], 'click': [pygame.mixer.Sound(path.join('sounds', 'weapons', 'universal_click.ogg'))], 'magout': [pygame.mixer.Sound(path.join('sounds', 'weapons', 'shotgun_magout1.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'shotgun_magout2.ogg'))], 'magin': [pygame.mixer.Sound(path.join('sounds', 'weapons', 'shotgun_magin1.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'shotgun_magin2.ogg'))] }, (34,10))) #Hand gun - 2 SETTINGS.gun_list.append(GUNS.Gun( {'spritesheet' : path.join('graphics', 'weapon', 'pistol_spritesheet.png'), 'item' : path.join('graphics', 'items', 'gun.png') },{ 'dmg' : 2, 'spread': 40, 'hitchance': 90, 'firerate': 0.25, 'range': 8, 'magsize': 10, 'rlspeed': 0.8, 'zoom': 2, 'ammotype': 'bullet', 'guntype': 'secondary', 'name': 'Pistol' },{ 'shot': [pygame.mixer.Sound(path.join('sounds', 'weapons', 'pistol_shot1.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'pistol_shot2.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'pistol_shot3.ogg'))], 'click': [pygame.mixer.Sound(path.join('sounds', 'weapons', 'universal_click.ogg'))], 'magout': [pygame.mixer.Sound(path.join('sounds', 'weapons', 'pistol_magout1.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'pistol_magout2.ogg'))], 'magin': [pygame.mixer.Sound(path.join('sounds', 'weapons', 'pistol_magin1.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'pistol_magin2.ogg'))] }, (37,6))) #Knife - 3 SETTINGS.gun_list.append(GUNS.Gun( {'spritesheet' : path.join('graphics', 'weapon', 'knife_spritesheet.png'), 'item' : path.join('graphics', 'items', 'knifeitem.png') },{ 'dmg' : 2, 'spread': 40, 'hitchance': 100, 'firerate': 0.3, 'range': 1.5, 'magsize': 0, 'rlspeed': 0, 'zoom': 0, 'ammotype': None, 'guntype': 'melee', 'name': 'Knife' },{ 'shot': [pygame.mixer.Sound(path.join('sounds', 'weapons', 'knife_swing1.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'knife_swing2.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'knife_swing3.ogg'))], 'click': [pygame.mixer.Sound(path.join('sounds', 'other', 'none.ogg'))], 'magout': [pygame.mixer.Sound(path.join('sounds', 'other', 'none.ogg'))], 'magin': [pygame.mixer.Sound(path.join('sounds', 'other', 'none.ogg'))] }, (37,10))) #Brass Knuckles - 4 SETTINGS.gun_list.append(GUNS.Gun( {'spritesheet' : path.join('graphics', 'weapon', 'brass_spritesheet.png'), 'item' : path.join('graphics', 'items', 'brassitem.png') },{ 'dmg' : 1, 'spread': 30, 'hitchance': 100, 'firerate': 0.2, 'range': 1.5, 'magsize': 0, 'rlspeed': 0, 'zoom': 0, 'ammotype': None, 'guntype': 'melee', 'name': 'Brass Knuckles' },{ 'shot': [pygame.mixer.Sound(path.join('sounds', 'weapons', 'knife_swing1.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'knife_swing2.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'knife_swing3.ogg'))], 'click': [pygame.mixer.Sound(path.join('sounds', 'other', 'none.ogg'))], 'magout': [pygame.mixer.Sound(path.join('sounds', 'other', 'none.ogg'))], 'magin': [pygame.mixer.Sound(path.join('sounds', 'other', 'none.ogg'))] }, (37,10))) # Gauss - 5 SETTINGS.gun_list.append(GUNS.Gun( {'spritesheet': path.join('graphics', 'weapon', 'gauss_spritesheet.png'), 'item': path.join('graphics', 'items', 'gaussitem.png') },{ 'dmg' : 6, 'spread' : 10, 'hitchance': 85, 'firerate': 0.5, 'range': 15, 'magsize': 8, 'rlspeed': 1, 'zoom': 8, 'ammotype': 'ferromag', 'guntype': 'primary', 'name': 'Gauss rifle' },{ 'shot': [pygame.mixer.Sound(path.join('sounds', 'weapons', 'gauss_shot1.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'gauss_shot2.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'gauss_shot3.ogg'))], 'click': [pygame.mixer.Sound(path.join('sounds', 'weapons', 'AK_click1.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'AK_click2.ogg'))], 'magout': [pygame.mixer.Sound(path.join('sounds', 'weapons', 'AK_magout1.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'AK_magout2.ogg'))], 'magin': [pygame.mixer.Sound(path.join('sounds', 'weapons', 'AK_magin1.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'AK_magin2.ogg'))] },(35,7))) #Shotgun pistol - 6 SETTINGS.gun_list.append(GUNS.Gun( {'spritesheet' : path.join('graphics', 'weapon', 'sgp_spritesheet.png'), 'item' : path.join('graphics', 'items', 'shotpistol.png') },{ 'dmg' : 6, 'spread': 100, 'hitchance': 60, 'firerate': 0.2, 'range': 6, 'magsize': 1, 'rlspeed': 0.5, 'zoom': 1, 'ammotype': 'shell', 'guntype': 'secondary', 'name': 'SG Pistol' },{ 'shot': [pygame.mixer.Sound(path.join('sounds', 'weapons', 'sgp_shot1.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'sgp_shot2.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'sgp_shot3.ogg'))], 'click': [pygame.mixer.Sound(path.join('sounds', 'weapons', 'universal_click.ogg'))], 'magout': [pygame.mixer.Sound(path.join('sounds', 'weapons', 'shotgun_magout1.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'shotgun_magout2.ogg'))], 'magin': [pygame.mixer.Sound(path.join('sounds', 'weapons', 'shotgun_magin1.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'shotgun_magin2.ogg'))] }, (37,6))) # ------ SPECIAL WEAPONS ---------- #Fast Brass Knuckles - 7 SETTINGS.gun_list.append(GUNS.Gun( {'spritesheet' : path.join('graphics', 'weapon', 'brass_brass_spritesheet.png'), 'item' : path.join('graphics', 'items', 'brassbrassitem.png') },{ 'dmg' : 1, 'spread': 30, 'hitchance': 100, 'firerate': 0, 'range': 2, 'magsize': 0, 'rlspeed': 0, 'zoom': 0, 'ammotype': None, 'guntype': 'melee', 'name': 'Light Knuckles' },{ 'shot': [pygame.mixer.Sound(path.join('sounds', 'weapons', 'knife_swing1.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'knife_swing2.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'knife_swing3.ogg'))], 'click': [pygame.mixer.Sound(path.join('sounds', 'other', 'none.ogg'))], 'magout': [pygame.mixer.Sound(path.join('sounds', 'other', 'none.ogg'))], 'magin': [pygame.mixer.Sound(path.join('sounds', 'other', 'none.ogg'))] }, (37,10))) #Bloody Brass Knuckles - 8 SETTINGS.gun_list.append(GUNS.Gun( {'spritesheet' : path.join('graphics', 'weapon', 'blood_brass_spritesheet.png'), 'item' : path.join('graphics', 'items', 'bloodbrassitem.png') },{ 'dmg' : 20, 'spread': 60, 'hitchance': 100, 'firerate': 2, 'range': 1, 'magsize': 0, 'rlspeed': 0, 'zoom': 0, 'ammotype': None, 'guntype': 'melee', 'name': 'Rampage Knuckles' },{ 'shot': [pygame.mixer.Sound(path.join('sounds', 'weapons', 'knife_swing1.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'knife_swing2.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'knife_swing3.ogg'))], 'click': [pygame.mixer.Sound(path.join('sounds', 'other', 'none.ogg'))], 'magout': [pygame.mixer.Sound(path.join('sounds', 'other', 'none.ogg'))], 'magin': [pygame.mixer.Sound(path.join('sounds', 'other', 'none.ogg'))] }, (37,10))) #Sharp Knife - 9 SETTINGS.gun_list.append(GUNS.Gun( {'spritesheet' : path.join('graphics', 'weapon', 'shiny_knife_spritesheet.png'), 'item' : path.join('graphics', 'items', 'shinyknifeitem.png') },{ 'dmg' : 3, 'spread': 40, 'hitchance': 100, 'firerate': 0.3, 'range': 1.5, 'magsize': 0, 'rlspeed': 0, 'zoom': 0, 'ammotype': None, 'guntype': 'melee', 'name': 'Sharp Knife' },{ 'shot': [pygame.mixer.Sound(path.join('sounds', 'weapons', 'knife_swing1.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'knife_swing2.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'knife_swing3.ogg'))], 'click': [pygame.mixer.Sound(path.join('sounds', 'other', 'none.ogg'))], 'magout': [pygame.mixer.Sound(path.join('sounds', 'other', 'none.ogg'))], 'magin': [pygame.mixer.Sound(path.join('sounds', 'other', 'none.ogg'))] }, (37,10))) #Fast Knife - 10 SETTINGS.gun_list.append(GUNS.Gun( {'spritesheet' : path.join('graphics', 'weapon', 'desert_knife_spritesheet.png'), 'item' : path.join('graphics', 'items', 'desertknifeitem.png') },{ 'dmg' : 2, 'spread': 30, 'hitchance': 100, 'firerate': 0.1, 'range': 1.8, 'magsize': 0, 'rlspeed': 0, 'zoom': 0, 'ammotype': None, 'guntype': 'melee', 'name': 'Light Knife' },{ 'shot': [pygame.mixer.Sound(path.join('sounds', 'weapons', 'knife_swing1.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'knife_swing2.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'knife_swing3.ogg'))], 'click': [pygame.mixer.Sound(path.join('sounds', 'other', 'none.ogg'))], 'magout': [pygame.mixer.Sound(path.join('sounds', 'other', 'none.ogg'))], 'magin': [pygame.mixer.Sound(path.join('sounds', 'other', 'none.ogg'))] }, (37,10))) #Modded Double Barrel Shotgun - 11 SETTINGS.gun_list.append(GUNS.Gun( {'spritesheet': path.join('graphics', 'weapon', 'modded_shotgun_spritesheet.png'), 'item': path.join('graphics', 'items', 'moddedshotgun.png') },{ 'dmg' : 15, 'spread' : 220, 'hitchance': 65, 'firerate': 0.3, 'range': 6, 'magsize': 3.1415, #lol bad code. 'rlspeed': 1.4, 'zoom': 8, 'ammotype': 'shell', 'guntype': 'primary', 'name': 'Modified Shotgun' },{ 'shot': [pygame.mixer.Sound(path.join('sounds', 'weapons', 'shotgun_shot1.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'shotgun_shot2.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'shotgun_shot3.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'shotgun_shot4.ogg'))], 'click': [pygame.mixer.Sound(path.join('sounds', 'weapons', 'universal_click.ogg'))], 'magout': [pygame.mixer.Sound(path.join('sounds', 'weapons', 'shotgun_magout1.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'shotgun_magout2.ogg'))], 'magin': [pygame.mixer.Sound(path.join('sounds', 'weapons', 'shotgun_magin1.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'shotgun_magin2.ogg'))] }, (34,10))) #Impossible Double Barrel Shotgun - 12 SETTINGS.gun_list.append(GUNS.Gun( {'spritesheet': path.join('graphics', 'weapon', 'shotgun_spritesheet.png'), 'item': path.join('graphics', 'items', 'weirdshotgun.png') },{ 'dmg' : 8, 'spread' : 200, 'hitchance': 65, 'firerate': 0.5, 'range': 8, 'magsize': 3, 'rlspeed': 1.4, 'zoom': 8, 'ammotype': 'shell', 'guntype': 'primary', 'name': 'TB Shotgun' },{ 'shot': [pygame.mixer.Sound(path.join('sounds', 'weapons', 'shotgun_shot1.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'shotgun_shot2.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'shotgun_shot3.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'shotgun_shot4.ogg'))], 'click': [pygame.mixer.Sound(path.join('sounds', 'weapons', 'universal_click.ogg'))], 'magout': [pygame.mixer.Sound(path.join('sounds', 'weapons', 'shotgun_magout1.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'shotgun_magout2.ogg'))], 'magin': [pygame.mixer.Sound(path.join('sounds', 'weapons', 'shotgun_magin1.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'shotgun_magin2.ogg'))] }, (34,10))) #AK 74 - 13 SETTINGS.gun_list.append(GUNS.Gun( {'spritesheet': path.join('graphics', 'weapon', 'ak74_spritesheet.png'), 'item': path.join('graphics', 'items', 'ak74item.png') },{ 'dmg' : 4, 'spread' : 30, 'hitchance': 80, 'firerate': 0.08, 'range': 10, 'magsize': 30, 'rlspeed': 1, 'zoom': 8, 'ammotype': 'bullet', 'guntype': 'primary', 'name': 'AK-74' },{ 'shot': [pygame.mixer.Sound(path.join('sounds', 'weapons', 'AK_shot1.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'AK_shot2.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'AK_shot3.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'AK_shot4.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'AK_shot5.ogg'))], 'click': [pygame.mixer.Sound(path.join('sounds', 'weapons', 'AK_click1.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'AK_click2.ogg'))], 'magout': [pygame.mixer.Sound(path.join('sounds', 'weapons', 'AK_magout1.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'AK_magout2.ogg'))], 'magin': [pygame.mixer.Sound(path.join('sounds', 'weapons', 'AK_magin1.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'AK_magin2.ogg'))] },(35,7))) #Extended mag AK - 14 SETTINGS.gun_list.append(GUNS.Gun( {'spritesheet': path.join('graphics', 'weapon', 'akext_spritesheet.png'), 'item': path.join('graphics', 'items', 'akextitem.png') },{ 'dmg' : 3, 'spread' : 50, 'hitchance': 80, 'firerate': 0.08, 'range': 10, 'magsize': 40, 'rlspeed': 1.2, 'zoom': 6, 'ammotype': 'bullet', 'guntype': 'primary', 'name': 'Ext Mag AK-47' },{ 'shot': [pygame.mixer.Sound(path.join('sounds', 'weapons', 'AK_shot1.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'AK_shot2.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'AK_shot3.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'AK_shot4.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'AK_shot5.ogg'))], 'click': [pygame.mixer.Sound(path.join('sounds', 'weapons', 'AK_click1.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'AK_click2.ogg'))], 'magout': [pygame.mixer.Sound(path.join('sounds', 'weapons', 'AK_magout1.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'AK_magout2.ogg'))], 'magin': [pygame.mixer.Sound(path.join('sounds', 'weapons', 'AK_magin1.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'AK_magin2.ogg'))] },(35,7))) #Camo AK-47 - 15 SETTINGS.gun_list.append(GUNS.Gun( {'spritesheet': path.join('graphics', 'weapon', 'camo_ak_spritesheet.png'), 'item': path.join('graphics', 'items', 'camoakitem.png') },{ 'dmg' : 3, 'spread' : 50, 'hitchance': 90, 'firerate': 0.04, 'range': 10, 'magsize': 30, 'rlspeed': 0.8, 'zoom': 6, 'ammotype': 'bullet', 'guntype': 'primary', 'name': 'Camo AK-47' },{ 'shot': [pygame.mixer.Sound(path.join('sounds', 'weapons', 'AK_shot1.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'AK_shot2.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'AK_shot3.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'AK_shot4.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'AK_shot5.ogg'))], 'click': [pygame.mixer.Sound(path.join('sounds', 'weapons', 'AK_click1.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'AK_click2.ogg'))], 'magout': [pygame.mixer.Sound(path.join('sounds', 'weapons', 'AK_magout1.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'AK_magout2.ogg'))], 'magin': [pygame.mixer.Sound(path.join('sounds', 'weapons', 'AK_magin1.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'AK_magin2.ogg'))] },(35,7))) #Light AK-47 - 16 SETTINGS.gun_list.append(GUNS.Gun( {'spritesheet': path.join('graphics', 'weapon', 'ak_spritesheet.png'), 'item': path.join('graphics', 'items', 'lightakitem.png') },{ 'dmg' : 3, 'spread' : 60, 'hitchance': 80, 'firerate': 0.08, 'range': 10, 'magsize': 20, 'rlspeed': 0.1, 'zoom': 4, 'ammotype': 'bullet', 'guntype': 'primary', 'name': 'Light AK-47' },{ 'shot': [pygame.mixer.Sound(path.join('sounds', 'weapons', 'AK_shot1.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'AK_shot2.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'AK_shot3.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'AK_shot4.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'AK_shot5.ogg'))], 'click': [pygame.mixer.Sound(path.join('sounds', 'weapons', 'AK_click1.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'AK_click2.ogg'))], 'magout': [pygame.mixer.Sound(path.join('sounds', 'weapons', 'AK_magout1.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'AK_magout2.ogg'))], 'magin': [pygame.mixer.Sound(path.join('sounds', 'weapons', 'AK_magin1.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'AK_magin2.ogg'))] },(35,7))) #Gauss Hand gun - 17 SETTINGS.gun_list.append(GUNS.Gun( {'spritesheet' : path.join('graphics', 'weapon', 'gauss_pistol_spritesheet.png'), 'item' : path.join('graphics', 'items', 'gaussgun.png') },{ 'dmg' : 9, 'spread': 30, 'hitchance': 98, 'firerate': 0.25, 'range': 12, 'magsize': 10, 'rlspeed': 0.8, 'zoom': 8, 'ammotype': 'ferromag', 'guntype': 'secondary', 'name': 'Anomaly Pistol' },{ 'shot': [pygame.mixer.Sound(path.join('sounds', 'weapons', 'gauss_shot1.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'gauss_shot2.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'gauss_shot3.ogg'))], 'click': [pygame.mixer.Sound(path.join('sounds', 'weapons', 'AK_click1.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'AK_click2.ogg'))], 'magout': [pygame.mixer.Sound(path.join('sounds', 'weapons', 'AK_magout1.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'AK_magout2.ogg'))], 'magin': [pygame.mixer.Sound(path.join('sounds', 'weapons', 'AK_magin1.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'AK_magin2.ogg'))] }, (37,6))) #High power Hand gun - 18 SETTINGS.gun_list.append(GUNS.Gun( {'spritesheet' : path.join('graphics', 'weapon', 'pistol_spritesheet.png'), 'item' : path.join('graphics', 'items', 'hpgun.png') },{ 'dmg' : 3, 'spread': 40, 'hitchance': 85, 'firerate': 0.25, 'range': 8, 'magsize': 10, 'rlspeed': 0.8, 'zoom': 2, 'ammotype': 'bullet', 'guntype': 'secondary', 'name': 'HP Pistol' },{ 'shot': [pygame.mixer.Sound(path.join('sounds', 'weapons', 'hpp_shot1.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'hpp_shot2.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'hpp_shot3.ogg'))], 'click': [pygame.mixer.Sound(path.join('sounds', 'weapons', 'universal_click.ogg'))], 'magout': [pygame.mixer.Sound(path.join('sounds', 'weapons', 'pistol_magout1.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'pistol_magout2.ogg'))], 'magin': [pygame.mixer.Sound(path.join('sounds', 'weapons', 'pistol_magin1.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'pistol_magin2.ogg'))] }, (37,6))) #Modded Gauss - 19 SETTINGS.gun_list.append(GUNS.Gun( {'spritesheet': path.join('graphics', 'weapon', 'modded_gauss_spritesheet.png'), 'item': path.join('graphics', 'items', 'moddedgaussitem.png') },{ 'dmg' : 9, 'spread' : 10, 'hitchance': 85, 'firerate': 0.5, 'range': 15, 'magsize': 12, 'rlspeed': 1, 'zoom': 9, 'ammotype': 'ferromag', 'guntype': 'primary', 'name': 'Modded gauss' },{ 'shot': [pygame.mixer.Sound(path.join('sounds', 'weapons', 'gauss_shot1.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'gauss_shot2.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'gauss_shot3.ogg'))], 'click': [pygame.mixer.Sound(path.join('sounds', 'weapons', 'AK_click1.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'AK_click2.ogg'))], 'magout': [pygame.mixer.Sound(path.join('sounds', 'weapons', 'AK_magout1.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'AK_magout2.ogg'))], 'magin': [pygame.mixer.Sound(path.join('sounds', 'weapons', 'AK_magin1.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'AK_magin2.ogg'))] },(35,7))) #bump Gauss - 20 SETTINGS.gun_list.append(GUNS.Gun( {'spritesheet': path.join('graphics', 'weapon', 'bump_gauss_spritesheet.png'), 'item': path.join('graphics', 'items', 'bumpgaussitem.png') },{ 'dmg' : 6, 'spread' : 20, 'hitchance': 70, 'firerate': 0.15, 'range': 15, 'magsize': 8, 'rlspeed': 1, 'zoom': 7, 'ammotype': 'ferromag', 'guntype': 'primary', 'name': 'Bump gauss' },{ 'shot': [pygame.mixer.Sound(path.join('sounds', 'weapons', 'gauss_shot1.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'gauss_shot2.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'gauss_shot3.ogg'))], 'click': [pygame.mixer.Sound(path.join('sounds', 'weapons', 'AK_click1.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'AK_click2.ogg'))], 'magout': [pygame.mixer.Sound(path.join('sounds', 'weapons', 'AK_magout1.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'AK_magout2.ogg'))], 'magin': [pygame.mixer.Sound(path.join('sounds', 'weapons', 'AK_magin1.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'AK_magin2.ogg'))] },(35,7))) #Black Shotgun pistol - 21 SETTINGS.gun_list.append(GUNS.Gun( {'spritesheet' : path.join('graphics', 'weapon', 'black_sgp_spritesheet.png'), 'item' : path.join('graphics', 'items', 'blackshotpistol.png') },{ 'dmg' : 8, 'spread': 100, 'hitchance': 60, 'firerate': 0.2, 'range': 6, 'magsize': 1, 'rlspeed': 0.4, 'zoom': 1, 'ammotype': 'shell', 'guntype': 'secondary', 'name': 'Modded SGP' },{ 'shot': [pygame.mixer.Sound(path.join('sounds', 'weapons', 'sgp_shot1.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'sgp_shot2.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'sgp_shot3.ogg'))], 'click': [pygame.mixer.Sound(path.join('sounds', 'weapons', 'universal_click.ogg'))], 'magout': [pygame.mixer.Sound(path.join('sounds', 'weapons', 'shotgun_magout1.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'shotgun_magout2.ogg'))], 'magin': [pygame.mixer.Sound(path.join('sounds', 'weapons', 'shotgun_magin1.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'shotgun_magin2.ogg'))] }, (37,6))) #TWO Shotgun pistol - 22 SETTINGS.gun_list.append(GUNS.Gun( {'spritesheet' : path.join('graphics', 'weapon', 'wtf_sgp_spritesheet.png'), 'item' : path.join('graphics', 'items', 'wtfshotpistol.png') },{ 'dmg' : 12, 'spread': 150, 'hitchance': 60, 'firerate': 0.2, 'range': 6, 'magsize': 2, 'rlspeed': 0.8, 'zoom': 1, 'ammotype': 'shell', 'guntype': 'secondary', 'name': 'What??' },{ 'shot': [pygame.mixer.Sound(path.join('sounds', 'weapons', 'sgp_shot1.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'sgp_shot2.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'sgp_shot3.ogg'))], 'click': [pygame.mixer.Sound(path.join('sounds', 'weapons', 'universal_click.ogg'))], 'magout': [pygame.mixer.Sound(path.join('sounds', 'weapons', 'shotgun_magout1.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'shotgun_magout2.ogg'))], 'magin': [pygame.mixer.Sound(path.join('sounds', 'weapons', 'shotgun_magin1.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'shotgun_magin2.ogg'))] }, (37,6))) #Auto Hand gun - 23 SETTINGS.gun_list.append(GUNS.Gun( {'spritesheet' : path.join('graphics', 'weapon', 'auto_pistol_spritesheet.png'), 'item' : path.join('graphics', 'items', 'autogun.png') },{ 'dmg' : 2, 'spread': 40, 'hitchance': 90, 'firerate': 0.05, 'range': 8, 'magsize': 12, 'rlspeed': 0.9, 'zoom': 2, 'ammotype': 'bullet', 'guntype': 'secondary', 'name': 'Auto pistol' },{ 'shot': [pygame.mixer.Sound(path.join('sounds', 'weapons', 'pistol_shot1.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'pistol_shot2.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'pistol_shot3.ogg'))], 'click': [pygame.mixer.Sound(path.join('sounds', 'weapons', 'universal_click.ogg'))], 'magout': [pygame.mixer.Sound(path.join('sounds', 'weapons', 'pistol_magout1.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'pistol_magout2.ogg'))], 'magin': [pygame.mixer.Sound(path.join('sounds', 'weapons', 'pistol_magin1.ogg')), pygame.mixer.Sound(path.join('sounds', 'weapons', 'pistol_magin2.ogg'))] }, (37,6))) def load_npc_types(): SETTINGS.npc_types = [ #soldier idle { 'pos': [0,0], 'face': 0, 'spf': 0.12, 'dmg': 2, 'health': random.randint(12,15), 'speed': 40, 'mind': 'hostile', 'state': 'idle', 'atcktype': 'hitscan', 'atckrate': 1, 'id': 0, 'filepath' : ('graphics', 'npc', 'soldier_spritesheet.png'), 'name' : 'idle soldier', 'soundpack' : 'soldier', }, #Soldier Patrolling { 'pos' : [0,0], 'face' : 0, 'spf': 0.12, 'dmg': 2, 'health': random.randint(12,15), 'speed': 40, 'mind': 'hostile', 'state': 'patrolling', 'atcktype': 'hitscan', 'atckrate': 1, 'id': 1, 'filepath' : ('graphics', 'npc', 'soldier_spritesheet.png'), 'name' : 'patroul soldier', 'soundpack' : 'soldier', }, #Ninja idle { 'pos' : [0,0], 'face' : 0, 'spf': 0.10, 'dmg': 3, 'health': 11, 'speed': 60, 'mind': 'hostile', 'state': 'idle', 'atcktype': 'melee', 'atckrate': 0.8, 'id': 2, 'filepath' : ('graphics', 'npc', 'ninja_spritesheet.png'), 'name' : 'idle ninja', 'soundpack' : 'ninja', }, #Ninja patrolling { 'pos' : [0,0], 'face' : 0, 'spf': 0.10, 'dmg': 3, 'health': 12, 'speed': 60, 'mind': 'hostile', 'state': 'patrolling', 'atcktype': 'melee', 'atckrate': 0.8, 'id': 3, 'filepath' : ('graphics', 'npc', 'ninja_spritesheet.png'), 'name' : 'patroul ninja', 'soundpack' : 'ninja', }, #Zombie patroling hostile (no dmg?) { 'pos' : [0,0], 'face' : 0, 'spf': 0.12, 'dmg': 3.1415, #lol this is used to randomize dmg. 'health': 6, 'speed': 70, 'mind': 'hostile', 'state': 'patrolling', 'atcktype': 'melee', 'atckrate': 0.6, 'id': 4, 'filepath' : ('graphics', 'npc', 'zombie_spritesheet.png'), 'name' : 'hostile zombie', 'soundpack' : 'zombie hostile', }, #Zombie idle shy { 'pos' : [0,0], 'face' : 0, 'spf': 0.12, 'dmg': 0, 'health': 6, 'speed': 50, 'mind': 'shy', 'state': 'idle', 'atcktype': 'melee', 'atckrate': 0.6, 'id': 5, 'filepath' : ('graphics', 'npc', 'zombie_spritesheet.png'), 'name' : 'shy zombie', 'soundpack' : 'zombie shy', }, #random NPC { 'pos' : [0,0], 'face' : 0, 'spf': 0, 'dmg': 0, 'health': 0, 'speed': 0, 'mind': None, 'state': None, 'atcktype': None, 'atckrate': 0, 'id': 6, 'filepath' : ('graphics', 'npc', 'random_spritesheet.png'), 'name' : 'random', 'soundpack' : None, }, #SPECIAL NPCS -------- #Boss idle { 'pos' : [0,0], 'face' : 0, 'spf': 0.10, 'dmg': 5, 'health': 40, 'speed': 20, 'mind': 'hostile', 'state': 'idle', 'atcktype': 'hitscan', 'atckrate': 3, 'id': 7, 'filepath' : ('graphics', 'npc', 'red_soldier_spritesheet.png'), 'name' : 'idle red', 'soundpack' : 'red soldier', }, #black soldier idle { 'pos': [0,0], 'face': 0, 'spf': 0.12, 'dmg': 2, 'health': random.randint(15,20), 'speed': 30, 'mind': 'hostile', 'state': 'idle', 'atcktype': 'hitscan', 'atckrate': 0.5, 'id': 8, 'filepath' : ('graphics', 'npc', 'black_soldier_spritesheet.png'), 'name' : 'black idle', 'soundpack' : 'soldier', }, #black soldier patroul { 'pos': [0,0], 'face': 0, 'spf': 0.12, 'dmg': 2, 'health': random.randint(15,20), 'speed': 30, 'mind': 'hostile', 'state': 'patrolling', 'atcktype': 'hitscan', 'atckrate': 1.5, 'id': 9, 'filepath' : ('graphics', 'npc', 'black_soldier_spritesheet.png'), 'name' : 'black patroul', 'soundpack' : 'soldier', }, #green ninja idle { 'pos' : [0,0], 'face' : 0, 'spf': 0.12, 'dmg': 3, 'health': random.randint(8, 11), 'speed': 100, 'mind': 'hostile', 'state': 'idle', 'atcktype': 'melee', 'atckrate': 0.5, 'id': 10, 'filepath' : ('graphics', 'npc', 'green_ninja_spritesheet.png'), 'name' : 'idle green', 'soundpack' : 'ninja', }, #green ninja patrolling { 'pos' : [0,0], 'face' : 0, 'spf': 0.12, 'dmg': 2, 'health': random.randint(8, 11), 'speed': 100, 'mind': 'hostile', 'state': 'patrolling', 'atcktype': 'melee', 'atckrate': 0.5, 'id': 11, 'filepath' : ('graphics', 'npc', 'green_ninja_spritesheet.png'), 'name' : 'idle green', 'soundpack' : 'ninja', }, #blue ninja idle { 'pos' : [0,0], 'face' : 0, 'spf': 0.1, 'dmg': 4, 'health': 14, 'speed': 35, 'mind': 'hostile', 'state': 'patrolling', 'atcktype': 'melee', 'atckrate': 1.1, 'id': 12, 'filepath' : ('graphics', 'npc', 'blue_ninja_spritesheet.png'), 'name' : 'idle blue', 'soundpack' : 'ninja', }, #Zombie yellow patrolling { 'pos' : [0,0], 'face' : 0, 'spf': 0.18, 'dmg': 5, 'health': 20, 'speed': 20, 'mind': 'hostile', 'state': 'patrolling', 'atcktype': 'melee', 'atckrate': 1, 'id': 13, 'filepath' : ('graphics', 'npc', 'sick_zombie_spritesheet.png'), 'name' : 'patroul sick', 'soundpack' : 'zombie hostile', }, #zombie yellow idle { 'pos' : [0,0], 'face' : 0, 'spf': 0.18, 'dmg': 6, 'health': 20, 'speed': 20, 'mind': 'hostile', 'state': 'idle', 'atcktype': 'melee', 'atckrate': 0.8, 'id': 14, 'filepath' : ('graphics', 'npc', 'sick_zombie_spritesheet.png'), 'name' : 'idle sick', 'soundpack' : 'zombie hostile', }, #zombie yellow idle shy { 'pos' : [0,0], 'face' : 0, 'spf': 0.18, 'dmg': 10, 'health': 35, 'speed': 20, 'mind': 'hostile', 'state': 'idle', 'atcktype': 'melee', 'atckrate': 1.2, 'id': 15, 'filepath' : ('graphics', 'npc', 'sick_zombie_spritesheet.png'), 'name' : 'shy sick', 'soundpack' : 'zombie hostile', }, #blurry zombie hostile { 'pos' : [0,0], 'face' : 0, 'spf': 0.18, 'dmg': 8, 'health': 5, 'speed': 45, 'mind': 'hostile', 'state': 'patrolling', 'atcktype': 'melee', 'atckrate': 0.4, 'id': 16, 'filepath' : ('graphics', 'npc', 'blurry_zombie_spritesheet.png'), 'name' : 'hostile blurry', 'soundpack' : 'blurry zombie', }, #blurry zombie hostile hitscan?? { 'pos' : [0,0], 'face' : 0, 'spf': 0.18, 'dmg': 1, 'health': 15, 'speed': 45, 'mind': 'hostile', 'state': 'patrolling', 'atcktype': 'hitscan', 'atckrate': 0.4, 'id': 17, 'filepath' : ('graphics', 'npc', 'blurry_zombie_spritesheet.png'), 'name' : 'hostile blurry', 'soundpack' : 'blurry zombie', }, ] load_npc_sounds() def load_npc_sounds(): SETTINGS.npc_soundpacks = [ #Soldier soundpack { 'name' : 'soldier', 'attack' : pygame.mixer.Sound(path.join('sounds', 'npcs', 'soldier_shoot.ogg')), 'spot' : pygame.mixer.Sound(path.join('sounds', 'npcs', 'soldier_spot.ogg')), 'damage' : [pygame.mixer.Sound(path.join('sounds', 'npcs', 'soldier_hurt1.ogg')), pygame.mixer.Sound(path.join('sounds', 'npcs', 'soldier_hurt2.ogg')), pygame.mixer.Sound(path.join('sounds', 'npcs', 'soldier_hurt3.ogg')), pygame.mixer.Sound(path.join('sounds', 'npcs', 'soldier_hurt4.ogg'))], 'die' : [pygame.mixer.Sound(path.join('sounds', 'npcs', 'soldier_die.ogg')),], }, #boss soldier soundpack { 'name' : 'red soldier', 'attack' : pygame.mixer.Sound(path.join('sounds', 'npcs', 'soldier_shoot_heavy.ogg')), 'spot' : pygame.mixer.Sound(path.join('sounds', 'npcs', 'soldier_spot.ogg')), 'damage' : [pygame.mixer.Sound(path.join('sounds', 'npcs', 'soldier_hurt1.ogg')), pygame.mixer.Sound(path.join('sounds', 'npcs', 'soldier_hurt2.ogg')), pygame.mixer.Sound(path.join('sounds', 'npcs', 'soldier_hurt3.ogg')), pygame.mixer.Sound(path.join('sounds', 'npcs', 'soldier_hurt4.ogg'))], 'die' : [pygame.mixer.Sound(path.join('sounds', 'npcs', 'soldier_die.ogg')),], }, #Ninja Soundpack { 'name' : 'ninja', 'attack' : pygame.mixer.Sound(path.join('sounds', 'npcs', 'ninja_attack.ogg')), 'spot' : pygame.mixer.Sound(path.join('sounds', 'other', 'none.ogg')), 'damage' : [pygame.mixer.Sound(path.join('sounds', 'npcs', 'ninja_hurt1.ogg')), pygame.mixer.Sound(path.join('sounds', 'npcs', 'ninja_hurt2.ogg')), pygame.mixer.Sound(path.join('sounds', 'npcs', 'ninja_hurt3.ogg')), pygame.mixer.Sound(path.join('sounds', 'npcs', 'ninja_hurt4.ogg'))], 'die' : [pygame.mixer.Sound(path.join('sounds', 'npcs', 'ninja_die1.ogg')), pygame.mixer.Sound(path.join('sounds', 'npcs', 'ninja_die2.ogg'))], }, #Zombie shy soundpack { 'name' : 'zombie shy', 'attack' : pygame.mixer.Sound(path.join('sounds', 'other', 'none.ogg')), 'spot' : pygame.mixer.Sound(path.join('sounds', 'npcs', 'zombie_spot2.ogg')), 'damage' : [pygame.mixer.Sound(path.join('sounds', 'npcs', 'zombie_hurt1.ogg')), pygame.mixer.Sound(path.join('sounds', 'npcs', 'zombie_hurt2.ogg')), pygame.mixer.Sound(path.join('sounds', 'npcs', 'zombie_hurt3.ogg'))], 'die' : [pygame.mixer.Sound(path.join('sounds', 'npcs', 'zombie_die1.ogg')), pygame.mixer.Sound(path.join('sounds', 'npcs', 'zombie_die2.ogg'))], }, #Zombie hostile soundpack { 'name' : 'zombie hostile', 'attack' : pygame.mixer.Sound(path.join('sounds', 'npcs', 'zombie_attack.ogg')), 'spot' : pygame.mixer.Sound(path.join('sounds', 'npcs', 'zombie_spot1.ogg')), 'damage' : [pygame.mixer.Sound(path.join('sounds', 'npcs', 'zombie_hurt1.ogg')), pygame.mixer.Sound(path.join('sounds', 'npcs', 'zombie_hurt2.ogg')), pygame.mixer.Sound(path.join('sounds', 'npcs', 'zombie_hurt3.ogg'))], 'die' : [pygame.mixer.Sound(path.join('sounds', 'npcs', 'zombie_die1.ogg')), pygame.mixer.Sound(path.join('sounds', 'npcs', 'zombie_die2.ogg'))], }, #Zombie blurry soundpack { 'name' : 'blurry zombie', 'attack' : pygame.mixer.Sound(path.join('sounds', 'npcs', 'blurry_zombie_attack.ogg')), 'spot' : pygame.mixer.Sound(path.join('sounds', 'npcs', 'blurry_zombie_spot.ogg')), 'damage' : [pygame.mixer.Sound(path.join('sounds', 'npcs', 'blurry_zombie_hurt1.ogg')), pygame.mixer.Sound(path.join('sounds', 'npcs', 'blurry_zombie_hurt2.ogg')), pygame.mixer.Sound(path.join('sounds', 'npcs', 'blurry_zombie_hurt3.ogg'))], 'die' : [pygame.mixer.Sound(path.join('sounds', 'npcs', 'blurry_zombie_die1.ogg')), pygame.mixer.Sound(path.join('sounds', 'npcs', 'blurry_zombie_die2.ogg'))], }, ] def spawn_npcs(): seed = SETTINGS.current_level + SETTINGS.seed for npc in SETTINGS.levels_list[SETTINGS.current_level].npcs: if [x for x in SETTINGS.npc_types if x['id'] == npc[2]][0]['name'] == 'random': random.seed(seed) seed += 0.001 stats = copy.deepcopy(random.choice([x for x in SETTINGS.npc_types if x['name'] != 'random'])) print(stats['name']) else: stats = copy.deepcopy([x for x in SETTINGS.npc_types if x['id'] == npc[2]][0]) try: sounds = ([x for x in SETTINGS.npc_soundpacks if x['name'] == stats['soundpack']][0]) except: print("Error loading NPC! No soundpack with name ", stats['soundpack']) stats['pos'] = npc[0] stats['face'] = npc[1] SETTINGS.npc_list.append(NPC.Npc(stats, sounds, path.join(*stats['filepath']))) def load_item_types(): SETTINGS.item_types = [ #Health { 'filepath' : ('graphics', 'items', 'firstaid.png'), 'type' : 'health', 'effect' : 10, 'id' : 0, }, #Armor { 'filepath' : ('graphics', 'items', 'kevlar.png'), 'type' : 'armor', 'effect': 15, 'id': 1, }, #Bullet { 'filepath' : ('graphics', 'items', 'bullet.png'), 'type' : 'bullet', 'effect': 10, 'id': 2 }, #Shell { 'filepath' : ('graphics', 'items', 'shell.png'), 'type' : 'shell', 'effect': 4, 'id': 3 }, #Knife { 'filepath' : tuple(SETTINGS.gun_list[3].itemtexture.split('\\')), 'type' : SETTINGS.gun_list[3].guntype, 'effect': SETTINGS.gun_list[3], 'id': 4 }, #Pistol { 'filepath' : tuple(SETTINGS.gun_list[2].itemtexture.split('\\')), 'type': SETTINGS.gun_list[2].guntype, 'effect': SETTINGS.gun_list[2], 'id': 5 }, #AK-47 { 'filepath' : tuple(SETTINGS.gun_list[0].itemtexture.split('\\')), 'type' : SETTINGS.gun_list[0].guntype, 'effect': SETTINGS.gun_list[0], 'id': 6 }, #DB Shotgun { 'filepath' : tuple(SETTINGS.gun_list[1].itemtexture.split('\\')), 'type' : SETTINGS.gun_list[1].guntype, 'effect': SETTINGS.gun_list[1], 'id': 7 }, #Brass Knuckles { 'filepath' : tuple(SETTINGS.gun_list[4].itemtexture.split('\\')), 'type' : SETTINGS.gun_list[4].guntype, 'effect': SETTINGS.gun_list[4], 'id': 8 }, #Gauss rifle { 'filepath' : tuple(SETTINGS.gun_list[5].itemtexture.split('\\')), 'type' : SETTINGS.gun_list[5].guntype, 'effect': SETTINGS.gun_list[5], 'id': 9, }, #ferromag ammo { 'filepath' : ('graphics', 'items', 'ferromag.png'), 'type' : 'ferromag', 'effect': 6, 'id': 10, }, #Shotgun pistol { 'filepath' : tuple(SETTINGS.gun_list[6].itemtexture.split('\\')), 'type' : SETTINGS.gun_list[6].guntype, 'effect': SETTINGS.gun_list[6], 'id': 11, }, #Random any item { 'filepath' : ('graphics', 'items', 'random.png'), 'type' : 'random', 'effect': ['health', 'armor', 'bullet', 'shell', 'ferromag', 'health', 'armor', 'bullet', 'shell', 'ferromag', 'melee', 'secondary', 'primary'], 'id': 12, }, #Random weapon { 'filepath' : ('graphics', 'items', 'randomgun.png'), 'type' : 'random', 'effect': ['melee', 'secondary', 'primary'], 'id': 13, }, #Random item { 'filepath' : ('graphics', 'items', 'randomitem.png'), 'type' : 'random', 'effect': ['health', 'armor', 'bullet', 'shell', 'ferromag'], 'id': 14, }, #Light Brass Knuckles { 'filepath' : tuple(SETTINGS.gun_list[7].itemtexture.split('\\')), 'type' : SETTINGS.gun_list[7].guntype, 'effect': SETTINGS.gun_list[7], 'id': 15 }, #Bloody Brass Knuckles { 'filepath' : tuple(SETTINGS.gun_list[8].itemtexture.split('\\')), 'type' : SETTINGS.gun_list[8].guntype, 'effect': SETTINGS.gun_list[8], 'id': 16 }, #shiny knife { 'filepath' : tuple(SETTINGS.gun_list[9].itemtexture.split('\\')), 'type' : SETTINGS.gun_list[9].guntype, 'effect': SETTINGS.gun_list[9], 'id': 17 }, #desert knife { 'filepath' : tuple(SETTINGS.gun_list[10].itemtexture.split('\\')), 'type' : SETTINGS.gun_list[10].guntype, 'effect': SETTINGS.gun_list[10], 'id': 18 }, #modded shotgun { 'filepath' : tuple(SETTINGS.gun_list[11].itemtexture.split('\\')), 'type' : SETTINGS.gun_list[11].guntype, 'effect': SETTINGS.gun_list[11], 'id': 19 }, #Impossible Shotgun { 'filepath' : tuple(SETTINGS.gun_list[12].itemtexture.split('\\')), 'type' : SETTINGS.gun_list[12].guntype, 'effect': SETTINGS.gun_list[12], 'id': 20 }, #AK 74 { 'filepath' : tuple(SETTINGS.gun_list[13].itemtexture.split('\\')), 'type' : SETTINGS.gun_list[13].guntype, 'effect': SETTINGS.gun_list[13], 'id': 21 }, #AK 47 extended magazine { 'filepath' : tuple(SETTINGS.gun_list[14].itemtexture.split('\\')), 'type' : SETTINGS.gun_list[14].guntype, 'effect': SETTINGS.gun_list[14], 'id': 22 }, #Camo AK-47 { 'filepath' : tuple(SETTINGS.gun_list[15].itemtexture.split('\\')), 'type' : SETTINGS.gun_list[15].guntype, 'effect': SETTINGS.gun_list[15], 'id': 23 }, #Light AK-47 { 'filepath' : tuple(SETTINGS.gun_list[16].itemtexture.split('\\')), 'type' : SETTINGS.gun_list[16].guntype, 'effect': SETTINGS.gun_list[16], 'id': 24 }, #Gauss pistol { 'filepath' : tuple(SETTINGS.gun_list[17].itemtexture.split('\\')), 'type' : SETTINGS.gun_list[17].guntype, 'effect': SETTINGS.gun_list[17], 'id': 25 }, #HP Pistol { 'filepath' : tuple(SETTINGS.gun_list[18].itemtexture.split('\\')), 'type' : SETTINGS.gun_list[18].guntype, 'effect': SETTINGS.gun_list[18], 'id': 26 }, #Modded Gauss { 'filepath' : tuple(SETTINGS.gun_list[19].itemtexture.split('\\')), 'type' : SETTINGS.gun_list[19].guntype, 'effect': SETTINGS.gun_list[19], 'id': 27 }, #Bump Gauss { 'filepath' : tuple(SETTINGS.gun_list[20].itemtexture.split('\\')), 'type' : SETTINGS.gun_list[20].guntype, 'effect': SETTINGS.gun_list[20], 'id': 28 }, #Black Shotgun Pistol { 'filepath' : tuple(SETTINGS.gun_list[21].itemtexture.split('\\')), 'type' : SETTINGS.gun_list[21].guntype, 'effect': SETTINGS.gun_list[21], 'id': 29 }, #wtf shotgun pistol { 'filepath' : tuple(SETTINGS.gun_list[22].itemtexture.split('\\')), 'type' : SETTINGS.gun_list[22].guntype, 'effect': SETTINGS.gun_list[22], 'id': 30 }, #auto pistol { 'filepath' : tuple(SETTINGS.gun_list[23].itemtexture.split('\\')), 'type' : SETTINGS.gun_list[23].guntype, 'effect': SETTINGS.gun_list[23], 'id': 31 }, ] def spawn_items(): seed = SETTINGS.current_level + SETTINGS.seed for item in SETTINGS.levels_list[SETTINGS.current_level].items: stats = [x for x in SETTINGS.item_types if x['id'] == item[1]][0] if stats['type'] == 'random': random.seed(seed) possible_items = [x for x in SETTINGS.item_types if x['type'] in stats['effect']] stats = random.choice(possible_items) seed += 0.001 elif stats['type'] not in ('primary', 'secondary', 'melee'): stats = copy.deepcopy([x for x in SETTINGS.item_types if x['id'] == item[1]][0]) SETTINGS.all_items.append(ITEMS.Item(item[0], path.join(*stats['filepath']), stats['type'], stats['effect'])) ================================================ FILE: GENERATION.py ================================================ import random import math import copy import os import SEGMENTS import SETTINGS import TEXTURES import LEVELS class Generator: def __init__(self): self.segpath = [] self.all_segs = [] self.seed = None self.spawnable_area = [] #Constants #Item probability self.max_item_amount = 15 #Multiplied by amount of segments self.max_items_per_segment = 5 self.spawn_chance = 8 self.spawn_chance_high = 40 #Also influenced self.ammo_spawn_chance = 35 #Also influenced self.item_probability = [] self.item_spawns = { 0 : 28, #health 1 : 30, #Kevlar 2 : 25, #bullet 3 : 18, #shell 4 : 15, #knife 5 : 16, #pistol 6 : 13, #ak47 7 : 10, #shotgun 8 : 11, #knuckles 9 : 7, #gauss 10 : 20, #ferromag 11 : 14, #sg pistol --¤¤¤¤-- 12 : 8, #light knuckles 13 : 1, #blood knuckles 14 : 8, #shiny knife 15 : 10, #desert knife 16 : 6, #modded shotgun 17 : 6, #impossible shotgun 18 : 8, #ak74 19 : 10, #ak47 ext mag 20 : 12, #camo ak47 21 : 12, #light ak47 22 : 1, #gauss pistol 23 : 8, #hp pistol 24 : 4, #modded gauss 25 : 3, #bump gauss 26 : 5, #black sg pistol 27 : 1, # wtf pistol 28 : 8, #hp pistol 29 : 6, #black sgp 30 : 2, #wtf sgp 31 : 8, #auto pistol } for i in self.item_spawns: for x in range(self.item_spawns[i]): self.item_probability.append(i) #NPC probability self.max_npc_amount = SETTINGS.current_level+1 #Will be multiplied by amount of segments self.max_npcs_per_segment = 3 self.min_npcs_per_level = 3 self.npc_spawn_chance = 20 + SETTINGS.current_level*1.5 #Also influenced self.npc_probability = [0,0, 1,1, 2,2, 3,3, 4,4, 5,5, 7, 8, 9, 10, 11, 12, 13, 15, 16, 17 ] #Color list self.ground_colors = [SETTINGS.GRAY, SETTINGS.LIGHTGRAY, SETTINGS.DARKGRAY, SETTINGS.DARKRED, SETTINGS.DARKGREEN] self.sky_colors = [SETTINGS.GRAY, SETTINGS.LIGHTGRAY, SETTINGS.LIGHTBLUE, SETTINGS.BLUE, SETTINGS.LIGHTGREEN] self.shade_colors = [(0,0,0,255), (255,255,255,255)] def create_seed(self, seed): if seed: self.seed = seed[0] else: self.seed = random.random() SETTINGS.seed = self.seed random.seed(self.seed) print("Seed: ", self.seed) def generate_levels(self, amount, size, *seed): #Generate sequence of levels and append to SETTINGS.all_levels self.create_seed(seed) SETTINGS.glevels_list = [] for i in range(amount): self.generate_level(size, seed) self.seed += 0.001 self.segpath = [] def generate_level(self, size, *seed): if not self.seed: self.create_seed(seed[0]) #Rotate segments in all directions. if not self.all_segs: for seg in SETTINGS.segments_list: self.all_segs.append(seg) for i in range(3): self.all_segs.append(self.rotate_segment(self.all_segs[-1])) num = 0 #Create empty grid. array = [] for i in range(size): array.append([None]*size) path = [] checklist = [] #Pick a random starting point from top row. x = random.randint(0,size-1) y = 0 #([x,y], parent) path.append(([x,y], None)) #Recursive function, which adds or subtracts 1 to x or y. def seg_pos_increment(temppos): tempx, tempy = temppos[0], temppos[1] inc = random.choice([-1, 1]) if random.randint(0,1) == 1: tempx += inc else: tempy += inc if -1 in [tempx, tempy] or size in [tempx, tempy] or ([tempx, tempy]) in checklist: return seg_pos_increment(temppos) else: return([tempx, tempy], temppos) #Checks if the adjacent coordinates are vacant. def check_adjacents(temppos): ax = temppos[0] ay = temppos[1] adjacents = [[ax+1, ay], [ax-1, ay], [ax, ay+1], [ax, ay-1]] for coords in adjacents: if (-1 not in coords and size not in coords) and coords not in checklist: return True return False #Find an end point on the bottom row. end_point = [random.randint(0,size-1), size-1] #Add coordinates to the path until it reaches the end. while path[num][0] != end_point: if path[num][0] not in checklist: checklist.append(path[num][0]) if check_adjacents(path[num][0]): path.append(seg_pos_increment(path[num][0])) num += 1 else: del(path[num]) num -= 1 #Place segments that will be accessible from previous placed segment. for i in range(len(path)): cpos = path[i][0] if i > 0: ppos = path[i-1][0] else: ppos = None if i < len(path)-1: npos = path[i+1][0] else: npos = None #Place the segments. if not ppos: array[cpos[1]][cpos[0]] = self.suitable_segment(array, None, cpos, npos, 'start') elif ppos and npos: array[cpos[1]][cpos[0]] = self.suitable_segment(array, ppos, cpos, npos, None) else: array[cpos[1]][cpos[0]] = self.suitable_segment(array, ppos, cpos, None, 'end') #Place dead ends. for seg in self.segpath: right = [max(seg.level_pos[0], min(len(array[0])-1, seg.level_pos[0]+1)), seg.level_pos[1]] left = [max(seg.level_pos[0], min(len(array[0])-1, seg.level_pos[0]-1)), seg.level_pos[1]] up = [seg.level_pos[0], max(seg.level_pos[1], min(len(array)-1, seg.level_pos[1]-1))] down = [seg.level_pos[0], max(seg.level_pos[1], min(len(array)-1, seg.level_pos[1]+1))] if 360 in seg.doors and not array[right[1]][right[0]]: array[right[1]][right[0]] = self.suitable_segment(array, seg.level_pos, right, None, None) if 180 in seg.doors and not array[left[1]][left[0]]: array[left[1]][left[0]] = self.suitable_segment(array, seg.level_pos, left, None, None) if 90 in seg.doors and not array[up[1]][up[0]]: array[up[1]][up[0]] = self.suitable_segment(array, seg.level_pos, up, None, None) if 270 in seg.doors and not array[down[1]][down[0]]: array[down[1]][down[0]] = self.suitable_segment(array, seg.level_pos, down, None, None) #Place emptyness where there should be emptyness... empty = [] for i in range(self.all_segs[0].height): empty.append([0] * self.all_segs[0].width) for row in range(len(array)): for col in range(len(array[row])): if not array[row][col]: array[row][col] = SEGMENTS.Segment({'id' : None, 'items' : [], 'npcs' : [], 'array' : empty, 'doors' : [], 'type' : 'empty'}) self.place_random_items() self.spawn_random_npcs() self.translate_map(self.kill_dead_ends(array), size) def rotate_segment(self, segment): #Rotate array rotseg = copy.deepcopy(segment) rotseg.array = list(zip(*reversed(rotseg.array))) for i in range(len(rotseg.array)): rotseg.array[i] = list(rotseg.array[i]) t = 0 for tile in rotseg.array[i]: if SETTINGS.texture_type[tile] == 'hdoor' or SETTINGS.texture_type[tile] == 'vdoor': for y in range(len(TEXTURES.all_textures)): if y != tile and os.path.samefile(TEXTURES.all_textures[y], TEXTURES.all_textures[tile]): rotseg.array[i][t] = y t += 1 #Rotate doors for i in range(len(rotseg.doors)): rotseg.doors[i] -= 90 if rotseg.doors[i] <= 0: rotseg.doors[i] += 360 #Rotate items origin = [int(len(rotseg.array[0])/2), int(len(rotseg.array)/2)] x = 0 for item in rotseg.items: tempx = item[0][0] - origin[0] tempy = item[0][1] - origin[1] tempx1 = math.cos(math.radians(90)) * tempx - math.sin(math.radians(90)) * tempy tempy1 = math.sin(math.radians(90)) * tempx + math.cos(math.radians(90)) * tempy tempx1 += origin[0] tempy1 += origin[1] rotseg.items[x] = ([int(tempx1), int(tempy1)], item[1]) x += 1 #rotate npc x = 0 for npc in rotseg.npcs: tempx = npc[0][0] - origin[0] tempy = npc[0][1] - origin[1] tempx1 = math.cos(math.radians(90)) * tempx - math.sin(math.radians(90)) * tempy tempy1 = math.sin(math.radians(90)) * tempx + math.cos(math.radians(90)) * tempy tempx1 += origin[0] tempy1 += origin[1] tempnpc = npc[1] tempnpc -= 90 if tempnpc < 0: tempnpc += 360 rotseg.npcs[x] = ([int(tempx1), int(tempy1)], tempnpc, npc[2]) x += 1 if rotseg.player_pos: tempx = rotseg.player_pos[0] - origin[0] tempy = rotseg.player_pos[1] - origin[1] tempx1 = math.cos(math.radians(90)) * tempx - math.sin(math.radians(90)) * tempy tempy1 = math.sin(math.radians(90)) * tempx + math.cos(math.radians(90)) * tempy tempx1 += origin[0] tempy1 += origin[1] rotseg.player_pos = [int(tempx1), int(tempy1)] return rotseg def suitable_segment(self, array, prev, current, nex, special): #Convert coords to degrees. enter, leave, segment = None, None, None if nex: if nex[0] == current[0] + 1: #right enter = 360 elif nex[0] == current[0] - 1: #left enter = 180 elif nex[1] == current[1] + 1: #down enter = 270 elif nex[1] == current[1] - 1: #up enter = 90 if prev: if prev[0] == current[0] + 1: #right leave = 360 elif prev[0] == current[0] - 1: #left leave = 180 elif prev[1] == current[1] + 1: #down leave = 270 elif prev[1] == current[1] - 1: #up leave = 90 #Find suitable adjacents if enter and leave: segment = [x for x in self.all_segs if enter in x.doors and leave in x.doors and x.type == 'normal'] elif enter and not leave and not special: segment = [x for x in self.all_segs if enter in x.doors and x.type == 'normal'] elif leave and not enter and not special: segment = [x for x in self.all_segs if leave in x.doors and x.type == 'normal'] #Place start/stop segments. Enter and leave seems to be swapped, but who cares? elif enter and not leave and special: segment = [x for x in self.all_segs if enter in x.doors and x.type == 'start'] elif leave and not enter and special: segment = [x for x in self.all_segs if leave in x.doors and x.type == 'end'] #Check if segment doors hit dead ends. if segment: temppos = [None, None] opposite = None for seg in segment: for door in seg.doors: if door == 360: opposite = 180 temppos = [current[0]+1, current[1]] elif door == 90: opposite = 270 temppos = [current[0], current[1]-1] elif door == 180: opposite = 360 temppos = [current[0]-1, current[1]] elif door == 270: opposite = 90 temppos = [current[0], current[1]+1] temppos[0] = max(0 , min(len(array[0])-1, temppos[0])) temppos[1] = max(0, min(len(array)-1, temppos[1])) if array[temppos[1]][temppos[0]]: if opposite not in array[temppos[1]][temppos[0]].doors: segment.remove(seg) break #Return segment if segment: finalsegment = copy.deepcopy(random.choice(segment)) finalsegment.array = copy.deepcopy(finalsegment.array) finalsegment.level_pos = current self.segpath.append(finalsegment) return finalsegment else: print("Did not have a segment with enter: ", enter, "and leave: ", leave) def kill_dead_ends(self, array): #Change segments with doors leading nowhere. changesegs = [] i = 0 for seg in self.segpath: #Compare segment with adjacent segments. rightseg = [x for x in self.segpath if x.level_pos == [seg.level_pos[0]+1, seg.level_pos[1]]] leftseg = [x for x in self.segpath if x.level_pos == [seg.level_pos[0]-1, seg.level_pos[1]]] upseg = [x for x in self.segpath if x.level_pos == [seg.level_pos[0], seg.level_pos[1]-1]] downseg = [x for x in self.segpath if x.level_pos == [seg.level_pos[0], seg.level_pos[1]+1]] access = [] for deg in seg.doors: if deg == 360 and rightseg: if 180 in rightseg[0].doors: access.append(360) elif deg == 90 and upseg: if 270 in upseg[0].doors: access.append(90) elif deg == 180 and leftseg: if 360 in leftseg[0].doors: access.append(180) elif deg == 270 and downseg: if 90 in downseg[0].doors: access.append(270) #Find a suitable segment to replace with if necessary. if set(access) != set(seg.doors): newseg = [x for x in self.all_segs if set(access) == set(x.doors) and seg.type == x.type] if newseg: newseg = copy.deepcopy(random.choice(newseg)) newseg.level_pos = seg.level_pos changesegs.append((newseg, i)) #Gritty way of doing it... else: print("WARNING: No segment with doors: ", access, " of type ", x.type) i += 1 #Finally replace array and self.segpath segments with new segments. for x in changesegs: newseg = x[0] i = x[1] npcs = self.segpath[i].npcs self.segpath[i] = newseg newseg.npcs = npcs array[newseg.level_pos[1]][newseg.level_pos[0]] = newseg return array def translate_map(self, segs, size): array = [] offset = 0 self.segpath[0].array[0][0] = 0 #Place all segment grids in one, big grid. for i in range(size): offset = (segs[0][0].height * i) for seg in segs[i]: for row in range(len(seg.array)): array.append([]) for tile in seg.array[row]: array[row + offset].append(tile) newarray = [] for x in array: if x: newarray.append(x) #Translate player pos calcstart = [0,0] calcstart[0] = self.segpath[0].player_pos[0] + self.segpath[0].width * self.segpath[0].level_pos[0] calcstart[1] = self.segpath[0].player_pos[1] + self.segpath[0].height * self.segpath[0].level_pos[1] #Translate item pos translated_items = [] for seg in self.segpath: for item in seg.items: x = item[0][0] + seg.width * seg.level_pos[0] y = item[0][1] + seg.height * seg.level_pos[1] translated_items.append(((x,y), item[1])) #Translate NPC pos translated_npcs = [] for seg in self.segpath: for npc in seg.npcs: x = npc[0][0] + seg.width * seg.level_pos[0] y = npc[0][1] + seg.height * seg.level_pos[1] translated_npcs.append(((x,y), npc[1], npc[2])) SETTINGS.glevels_list.append(LEVELS.Level({ 'items' : translated_items, 'ground_color' : random.choice(self.ground_colors), 'sky_color' : random.choice(self.sky_colors), 'array' : newarray, 'lvl_number' : len(SETTINGS.glevels_list), 'npcs' : translated_npcs, 'player_pos' : calcstart, 'shade' : (bool(random.getrandbits(1)), random.choice(self.shade_colors), random.randint(150, 1000)) })) def place_random_items(self): items = 0 seed = SETTINGS.current_level + self.seed #If there are too many items, return for seg in self.segpath: for item in seg.items: items += 1 if items >= self.max_item_amount * len(self.segpath): return #Higher chance of spawning items in dead ends. for i in range(len(self.segpath)): seg = self.segpath[i] for y in range(self.max_items_per_segment): random.seed(seed) if (len(seg.doors) <= 1 and self.spawn_chance_high >= random.randint(0,100)) or (len(seg.doors) > 1 and self.spawn_chance >= random.randint(0,100)): is_good = False while not is_good: randomx = random.randint(0, len(seg.array)-1) randomy = random.randint(0, len(seg.array)-1) occupied = [x for x in seg.items if list(x[0]) == [randomx, randomy]] if not SETTINGS.tile_solid[seg.array[randomy][randomx]] and not occupied: item = random.choice(self.item_probability) self.segpath[i].items.append(((randomx, randomy), item)) #Higher chance of ammo spawning next to weapons if SETTINGS.item_types[item]['type'] in ['primary', 'secondary']: ammo = [x for x in SETTINGS.item_types if x['type'] == SETTINGS.item_types[item]['effect'].ammo_type][0] adjacents = [[randomx+1, randomy], [randomx, randomy+1], [randomx-1, randomy], [randomx, randomy-1]] for pos in adjacents: occupied = [x for x in seg.items if x[0] == (max(0, min(pos[0], len(seg.array)-1)), max(0, min(pos[1], len(seg.array)-1)))] if self.ammo_spawn_chance >= random.randint(0, 100) and not SETTINGS.tile_solid[self.segpath[i].array[max(0, min(pos[0], len(seg.array)-1))][max(0, min(pos[1], len(seg.array)-1))]] and not occupied: self.segpath[i].items.append(((pos[0], pos[1]), ammo['id'])) is_good = True seed += 0.001 def spawn_random_npcs(self): seed = SETTINGS.current_level + self.seed npcs = 0 degrees = [90, 180, 270, 360] for seg in self.segpath: for npc in seg.npcs: npcs += 1 if npcs >= self.max_npc_amount * len(self.segpath): return spawned_npcs = 0 #Spawn NPCs randomly for i in range(len(self.segpath)): seg = self.segpath[i] if seg.type != 'start': for y in range(self.max_npcs_per_segment): seed += 0.001 random.seed(seed) if self.npc_spawn_chance + len(self.segpath) >= random.randint(0,100): is_good = False while not is_good: randomx = random.randint(0, len(seg.array)-1) randomy = random.randint(0, len(seg.array)-1) occupied = [x for x in seg.npcs if list(x[0]) == [randomx, randomy]] if not SETTINGS.tile_solid[seg.array[randomy][randomx]] and not occupied: npc = random.choice(self.npc_probability) self.segpath[i].npcs.append(((randomx, randomy), random.choice(degrees), npc)) is_good = True spawned_npcs += 1 #If there were no NPCs, place minimum amount npc_amount = 0 for i in self.segpath: for npc in i.npcs: npc_amount += 1 while npc_amount < self.min_npcs_per_level: seed += 0.00001 random.seed(seed) segment = random.choice(self.segpath) index = self.segpath.index(segment) randomx = random.randint(0, len(segment.array)-1) randomy = random.randint(0, len(segment.array)-1) occupied = [x for x in segment.npcs if list(x[0]) == [randomx, randomy]] if not SETTINGS.tile_solid[segment.array[randomy][randomx]] and not occupied: npc = random.choice(self.npc_probability) self.segpath[index].npcs.append(((randomx, randomy), random.choice(degrees), npc)) npc_amount += 1 ================================================ FILE: GUNS.py ================================================ #GUN CLASS - See self.stats structure in the bottom of script. import SETTINGS import SOUND import pygame import random import math import os class Gun: '''== Create a weapon ==\nspritesheet -> .png | stats -> explained in GUNS.py\nsounds -> explained in GUNS.py | aim_pos = Sight pos in px''' #Aim pos is the position of the sight on the individual sprite. Reload_speed is seconds. #accuracy is the how much spread the gun has. Hit percent is how big chance there is for an NPC to be hit within the accuracy. 1-100 def __init__(self, textures, stats, sounds, aim_pos):# damage, spread, hit_percent, magazine_size, reload_speed, zoom, aim_pos): #Configure sprite sheet self.spritesheet = pygame.image.load(textures['spritesheet']) self.itemtexture = textures['item'] self.subitemtexture = pygame.transform.scale(pygame.image.load(self.itemtexture).subsurface(0,112,64,16).convert_alpha(), (256, 64)) self.rect = self.spritesheet.get_rect() self.spritesheet = pygame.transform.scale(self.spritesheet, (int(self.rect.width * 6), int(self.rect.height * 6))) self.aim = [self.spritesheet.subsurface(0,0,420,360).convert_alpha(), self.spritesheet.subsurface(0,360,420,360).convert_alpha(), self.spritesheet.subsurface(0 ,720,420,360).convert_alpha()] self.hipfire = [self.spritesheet.subsurface(420,0,420,360).convert_alpha(), self.spritesheet.subsurface(420,360,420,360).convert_alpha(), self.spritesheet.subsurface(420,720,420,360).convert_alpha()] self.aimdown = [self.spritesheet.subsurface(840,0,420,360).convert_alpha(), self.spritesheet.subsurface(840,360,420,360).convert_alpha(), self.spritesheet.subsurface(840,720,420,360).convert_alpha()] self.reload = [self.spritesheet.subsurface(0,0,420,360).convert_alpha(), self.spritesheet.subsurface(1260,0,420,360).convert_alpha(), self.spritesheet.subsurface(1260,360,420,360).convert_alpha(), self.spritesheet.subsurface(1260,720,420,360).convert_alpha(), self.spritesheet.subsurface(1260,1080,420,360).convert_alpha(), self.spritesheet.subsurface(1260,1440,420,360).convert_alpha()] self.sounds = sounds self.hit_marker = pygame.mixer.Sound(os.path.join('sounds', 'other', 'hitmarker.ogg')) #Weapon stats self.dmg = stats['dmg'] self.accuracy = stats['spread']*2 self.firerate = stats['firerate'] if stats['hitchance'] > 100 or stats['hitchance'] < 0: print("###HIT CHANCE ON A GUN IS ABOVE 100 OR BELOW 0!###") quit() self.hit_percent = stats['hitchance'] self.rlspeed = stats['rlspeed'] self.aim_pos = [SETTINGS.canvas_actual_width/2 - aim_pos[0] * 6, SETTINGS.canvas_target_height/2 - aim_pos[1] * 6] self.OG_aim_pos = [SETTINGS.canvas_actual_width/2 - aim_pos[0] * 6, SETTINGS.canvas_target_height/2 - aim_pos[1] * 6] self.raw_aim_pos = aim_pos self.mag_size = stats['magsize'] if stats['magsize'] == 3.1415: self.mag_size = 2 self.zoom = stats['zoom'] self.ammo_type = stats['ammotype'] self.guntype = stats['guntype'] self.name = stats['name'] self.stats = stats if self.guntype != 'melee': self.range = stats['range']*SETTINGS.tile_size else: self.range = SETTINGS.tile_size * 0.9 self.hit_rect = pygame.Rect((SETTINGS.canvas_actual_width/2)-(self.accuracy/2), 0, self.accuracy, 600) #setup self.current_img = self.aim[0] self.current_mag = 0 self.timer = 0 self.firetimer = self.firerate self.aim_busy = False self.aim_is_up = False self.shoot_busy = False self.reload_busy = False self.go_reload_next_my_gun = False self.have_shot = False self.sintemp = 0 self.swing = 10 self.wobble = 20 self.ID = hash(self) if self.guntype != 'melee': self.shoottime = 0.03 else: self.shoottime = 0.1 def update_rect(self, accuracy_added): self.hit_rect.width = self.hit_rect.width * accuracy_added self.hit_rect.centerx = SETTINGS.canvas_actual_width / 2 def aim_animation(self): if self.guntype != 'melee': if not self.aim_is_up: #Raise the gun self.aim_busy = True if self.current_img != self.aim[-1] and self.timer >= 0.08: x = self.aim.index(self.current_img)+1 self.current_img = self.aim[x] SETTINGS.fov -= self.zoom self.timer = 0 elif self.current_img == self.aim[-1]: self.aim_busy = False self.aim_is_up = True self.update_rect(0.5) self.hit_percent += 20 SETTINGS.aiming = True elif self.aim_is_up: #Lower the gun self.aim_busy = True if self.current_img != self.aim[0] and self.timer >= 0.10: x = self.aim.index(self.current_img)-1 self.current_img = self.aim[x] SETTINGS.fov += self.zoom self.timer = 0 elif self.current_img == self.aim[0]: self.aim_busy = False self.aim_is_up = False self.update_rect(2) self.hit_percent -= 20 SETTINGS.aiming = False else: SETTINGS.aiming = False def shoot_animation(self): if self.current_mag > 0 or self.guntype == 'melee': if self.firetimer >= self.firerate: #Hitscan gun animation if not self.aim_is_up and self.guntype != 'melee': #Hip fire if self.current_img not in self.hipfire: self.current_img = self.hipfire[random.randint(0,1)] self.shoot_busy = True SOUND.play_sound(random.choice(self.sounds['shot']), 0) SETTINGS.screen_shake = self.dmg * 2 self.damage() self.timer = 0 elif self.hipfire.index(self.current_img) <= 1 and self.timer >= self.shoottime: self.current_img = self.hipfire[-1] self.timer = 0 elif self.current_img == self.hipfire[-1] and self.timer >= self.shoottime: self.current_img = self.aim[0] self.shoot_busy = False self.current_mag -= 1 SETTINGS.statistics['last shots'] += 1 if self.stats['magsize'] == 3.1415: self.current_mag -= 1 SETTINGS.statistics['last shots'] += 1 self.firetimer = 0 #Melee weapon animation elif self.guntype == 'melee': if self.current_img not in self.hipfire: self.current_img = self.hipfire[0] self.shoot_busy = True SOUND.play_sound(random.choice(self.sounds['shot']), 0) self.damage() self.timer = 0 elif self.hipfire.index(self.current_img) < 2 and self.timer >= self.shoottime: self.current_img = self.hipfire[self.hipfire.index(self.current_img)+1] self.timer = 0 elif self.current_img == self.hipfire[-1] and self.timer >= self.shoottime: self.current_img = self.aim[0] self.shoot_busy = False self.firetimer = 0 elif self.aim_is_up: #ADS fire if self.current_img not in self.aimdown: self.current_img = self.aimdown[random.randint(0,1)] self.shoot_busy = True SOUND.play_sound(random.choice(self.sounds['shot']), 0) SETTINGS.screen_shake = self.dmg * 2 self.damage() self.timer = 0 elif self.aimdown.index(self.current_img) <= 1 and self.timer >= self.shoottime: self.current_img = self.aimdown[-1] self.timer = 0 elif self.current_img == self.aimdown[-1] and self.timer >= self.shoottime: self.current_img = self.aim[-1] self.shoot_busy = False self.current_mag -= 1 SETTINGS.statistics['last shots'] += 1 if self.stats['magsize'] == 3.1415: self.current_mag -= 1 SETTINGS.statistics['last shots'] += 1 self.firetimer = 0 else: if self.firetimer >= self.firerate: SOUND.play_sound(random.choice(self.sounds['click']), 0) self.firetimer = 0 def damage(self): if SETTINGS.middle_slice_len: target_npcs = [x for x in SETTINGS.npc_list if x.hit_rect.colliderect(self.hit_rect) and x.dist < SETTINGS.middle_slice_len] else: target_npcs = [x for x in SETTINGS.npc_list if x.hit_rect.colliderect(self.hit_rect)] if len(target_npcs) > 3: target_npcs = sorted(target_npcs, key=lambda x: x.sprite.theta)[:3] for npc in target_npcs: if npc.dist <= self.range and not npc.dead: if npc.dist <= SETTINGS.tile_size*2: cap = 100 else: cap = (self.hit_percent * 0.96 ** (npc.dist*((100-self.hit_percent)/100))) if cap >= random.randint(0,int(npc.dist*(1/self.range))): SOUND.play_sound(self.hit_marker, 0) #Damage less if NPC is far away from center. if self.hit_rect.width < 120 or (npc.hit_rect.centerx > self.hit_rect.left + self.hit_rect.width/3 and npc.hit_rect.centerx < self.hit_rect.right - self.hit_rect.width/3): #Critical hit if (npc.state == 'idle' or npc.state == 'patrouling') and not npc.player_in_view: npc.health -= self.dmg * 2 SETTINGS.statistics['last ddealt'] += self.dmg*2 else: npc.health -= self.dmg SETTINGS.statistics['last ddealt'] += self.dmg else: if (npc.state == 'idle' or npc.state == 'patrouling') and not npc.player_in_view: npc.health -= self.dmg SETTINGS.statistics['last ddealt'] += self.dmg*2 else: npc.health -= self.dmg / 2 SETTINGS.statistics['last ddealt'] += self.dmg npc.timer = 0 npc.hurting = True if npc.health <= 0: npc.knockback = self.dmg * (SETTINGS.tile_size/2) def reload_animation(self): if SETTINGS.held_ammo[self.ammo_type] > 0 or SETTINGS.unlimited_ammo: #Change sprite list to reload. if self.current_img not in self.reload: self.current_img = self.reload[0] SOUND.play_sound(random.choice(self.sounds['magout']), 0) self.reload_busy = True #Make sure the magazine is out of view for some time. if (self.current_img == self.reload[3] and self.timer > self.rlspeed) or (self.current_img != self.reload[-1] and self.current_img != self.reload[3] and self.timer >= 0.15): x = self.reload.index(self.current_img)+1 self.current_img = self.reload[x] self.timer = 0 elif self.current_img == self.reload[-1] and self.timer >= 0.15: #Done reloading self.current_img = self.aim[0] self.reload_busy = False self.timer = 0 SOUND.play_sound(random.choice(self.sounds['magin']), 0) #Change actual ammo if not SETTINGS.unlimited_ammo: taken_ammo = self.mag_size - self.current_mag if SETTINGS.held_ammo[self.ammo_type] >= taken_ammo: self.current_mag = self.mag_size SETTINGS.held_ammo[self.ammo_type] -= taken_ammo elif SETTINGS.held_ammo[self.ammo_type] < taken_ammo: self.current_mag = SETTINGS.held_ammo[self.ammo_type] + self.current_mag SETTINGS.held_ammo[self.ammo_type] = 0 else: self.current_mag = self.mag_size def draw(self, canvas): swing = self.swing wobble = self.wobble if not SETTINGS.player_states['dead']: self.timer += SETTINGS.dt self.firetimer += SETTINGS.dt #Reload gun if aimed if SETTINGS.reload_key_active and self.aim_is_up and (self.current_mag < self.mag_size or SETTINGS.unlimited_ammo): self.aim_animation() self.go_reload_next_my_gun = True #Aim gun elif (SETTINGS.mouse2_btn_active or self.aim_busy) and (not self.shoot_busy and not self.reload_busy): self.aim_animation() #Shoot gun elif (SETTINGS.mouse_btn_active or self.shoot_busy) and (not self.aim_busy and not self.reload_busy): self.shoot_animation() swing *= 2 wobble /= 2 #Reload gun elif ((SETTINGS.reload_key_active or self.reload_busy) and (not self.aim_busy and not self.shoot_busy)) or self.go_reload_next_my_gun: if not self.aim_is_up and self.current_mag < self.mag_size: self.reload_animation() self.go_reload_next_my_gun = False swing *= 2 wobble /= 2 if self.aim_is_up: swing *= 20 wobble /= 20 #Move gun from side to side when walking if SETTINGS.player_states['cspeed'] > 0 and SETTINGS.next_gun == SETTINGS.current_gun: self.sintemp += math.pi/14 * (25 * SETTINGS.dt) self.aim_pos[0] = math.sin(self.sintemp)*(SETTINGS.canvas_actual_width/swing) + self.OG_aim_pos[0] self.aim_pos[1] = math.sin(self.sintemp*2) * wobble + (self.OG_aim_pos[1]+10) #Return gun to default pos elif SETTINGS.player_states['cspeed'] == 0: if self.aim_pos[0] > self.OG_aim_pos[0]: self.aim_pos[0] -= int((self.aim_pos[0] - self.OG_aim_pos[0])/2) if int((self.aim_pos[0] - self.OG_aim_pos[0])/2) == 0: self.aim_pos[0] = self.OG_aim_pos[0] elif self.aim_pos[0] < self.OG_aim_pos[0]: self.aim_pos[0] += int((self.OG_aim_pos[0] - self.aim_pos[0])/2) if int((self.OG_aim_pos[0] - self.aim_pos[0])/2) == 0: self.aim_pos[0] = self.OG_aim_pos[0] if self.sintemp != 0 or self.sintemp!= -math.pi: self.sintemp = random.choice([0, -math.pi]) if self.aim_pos[1] > self.OG_aim_pos[1] and SETTINGS.next_gun == SETTINGS.current_gun: self.aim_pos[1] -= int((self.aim_pos[1] - self.OG_aim_pos[1])/2) if int((self.aim_pos[1] - self.OG_aim_pos[1])/2) == 0: self.aim_pos[1] = self.OG_aim_pos[1] #Move gun down when player is dead if SETTINGS.player_states['dead'] and self.aim_pos[1] <= SETTINGS.canvas_target_height: self.aim_pos[1] += 10 if not SETTINGS.current_gun: SETTINGS.current_gun = self SETTINGS.prev_gun = self #Change gun elif SETTINGS.next_gun != SETTINGS.current_gun: if not self.aim_is_up and not self.reload_busy and not self.shoot_busy: if self.aim_pos[1] <= SETTINGS.canvas_target_height: self.aim_pos[1] += 80 else: SETTINGS.prev_gun = SETTINGS.current_gun SETTINGS.current_gun = SETTINGS.next_gun elif self.aim_is_up: self.aim_animation() elif SETTINGS.prev_gun != SETTINGS.current_gun and self.aim_pos[1] != self.OG_aim_pos[1]: #Gennemse if self.aim_pos[1] > self.OG_aim_pos[1]: self.aim_pos[1] -= 80 if self.aim_pos[1] < self.OG_aim_pos[1]: self.aim_pos[1] = self.OG_aim_pos[1] else: SETTINGS.prev_gun = SETTINGS.current_gun canvas.blit(self.current_img, self.aim_pos) def re_init(self): self.aim_pos = [SETTINGS.canvas_actual_width/2 - self.raw_aim_pos[0] * 6, SETTINGS.canvas_target_height/2 - self.raw_aim_pos[1] * 6] self.OG_aim_pos = [SETTINGS.canvas_actual_width/2 - self.raw_aim_pos[0] * 6, SETTINGS.canvas_target_height/2 - self.raw_aim_pos[1] * 6] self.hit_rect = pygame.Rect((SETTINGS.canvas_actual_width/2)-(self.accuracy/2), 0, self.accuracy, 600) #Textures: #{ #'spritesheet' : spritesheet file path, #'item': item texture file path #} # #stats #{ # 'dmg' : int(damage), # 'spread' : int(spread - lower = better), # 'hitchance': int(1 to 100), # 'firerate': seconds between shots, # 'range': range - higher = better, # 'magsize': int(magazine size), # 'rlspeed': reload speed, # 'zoom': int(FOV zoom), # 'ammotype': 'bullet' / 'shell' / ???, # 'guntype': 'primary'/'secondary'/'melee', # 'name': 'name for the weapon' # } #All sounds are lists of loaded sounds - if you want more sounds for the same thing #pygame.mixer.Sound(path) #{ # 'shot' : [shooting], # 'click' : [mag empty], # 'magout' : [reloading mag], # 'magin' : [reloaded mag] # } ================================================ FILE: HUD.py ================================================ import SETTINGS import TEXT import pygame import os class hud: def __init__(self): self.health = SETTINGS.player_health self.armor = SETTINGS.player_armor self.ammo = 0 self.sprite = pygame.image.load(os.path.join('graphics', 'hud.png')).convert() self.sprite = pygame.transform.scale(self.sprite, (SETTINGS.canvas_actual_width, SETTINGS.window_height-SETTINGS.canvas_target_height)) self.rect = self.sprite.get_rect() self.rect.topleft = (0, SETTINGS.canvas_target_height) self.text = [TEXT.Text(int(self.rect.width/35), self.rect.y + int(self.rect.height/2.5), 'PLAYER ARMOR', SETTINGS.DARKGRAY, 'DUGAFONT.ttf', 35), TEXT.Text(int(self.rect.width/3.4), self.rect.y + int(self.rect.height/2.5), 'PLAYER HEALTH', SETTINGS.DARKGRAY, 'DUGAFONT.ttf', 35), TEXT.Text(int(self.rect.width/1.8), self.rect.y + int(self.rect.height/2.5), 'AMMUNITION', SETTINGS.DARKGRAY, 'DUGAFONT.ttf', 35)] self.arrow_spritesheet = pygame.image.load(os.path.join('graphics', 'arrows.png')).convert_alpha() self.arrow = self.arrow_spritesheet.subsurface(0,0,17,17).convert_alpha() self.arrow = pygame.transform.scale(self.arrow, (50,50)) self.original_arrow = self.arrow self.arrow_rect = self.arrow.get_rect() self.arrow_rect.center = (self.rect.topright[0] - 46, self.rect.topright[1] + 66) self.arrow_center = (self.arrow_rect.centerx - self.arrow_rect.width / 2, self.arrow_rect.centery - self.arrow_rect.height / 2) self.arrow2 = self.arrow_spritesheet.subsurface(0,17,17,17).convert_alpha() self.arrow2 = pygame.transform.scale(self.arrow2, (50,50)) self.original_arrow2 = self.arrow2 self.arrow3 = self.arrow_spritesheet.subsurface(0,34,17,17).convert_alpha() self.arrow3 = pygame.transform.scale(self.arrow3, (50,50)) self.original_arrow3 = self.arrow3 def render(self, canvas): canvas.blit(self.sprite, self.rect) self.text[0].update_string('%s / 100' % SETTINGS.player_armor) self.text[1].update_string('%s / 100' % SETTINGS.player_health) if SETTINGS.current_gun and SETTINGS.current_gun.ammo_type: self.text[2].update_string('%s / %s' % (SETTINGS.current_gun.current_mag, SETTINGS.held_ammo[SETTINGS.current_gun.ammo_type])) else: self.text[2].update_string('-- / --') for string in self.text: string.draw(canvas) self.arrow = pygame.transform.rotate(self.original_arrow, SETTINGS.end_angle) self.arrow_rect.topleft = (self.arrow_center[0] - self.arrow.get_rect().width /2, self.arrow_center[1] - self.arrow.get_rect().height /2) canvas.blit(self.arrow, self.arrow_rect) #test self.arrow2 = pygame.transform.rotate(self.original_arrow2, SETTINGS.end_angle) self.arrow3 = pygame.transform.rotate(self.original_arrow3, SETTINGS.end_angle) canvas.blit(self.arrow2, (self.arrow_rect[0], self.arrow_rect[1] - 4)) canvas.blit(self.arrow3, (self.arrow_rect[0], self.arrow_rect[1] - 8)) ================================================ FILE: INVENTORY.py ================================================ #This class / script is not a part of the engine itself. This means it will not #Be as easy to modify as the other parts. (As if they were easy) import SETTINGS import TEXT import ITEMS import pygame import os class inventory: #ammo_dict is dict with the max amout of each type of ammo. def __init__(self, ammo_dict): self.bg = pygame.image.load(os.path.join('graphics', 'inventory.png')).convert_alpha() self.rect = self.bg.get_rect() self.rect.center = (int(SETTINGS.canvas_actual_width/2), int(SETTINGS.canvas_target_height/2)) self.held_ammo = {} for x in ammo_dict: self.held_ammo[x] = 0 SETTINGS.held_ammo = self.held_ammo SETTINGS.max_ammo = ammo_dict #Menu self.menu = pygame.Surface((160, 220)).convert() self.menu_rect = self.menu.get_rect() self.menudraw = False self.selected = None self.submenus = [] self.submenu_rects = [] for i in range(0, 10): if i == 0 or i == 9: self.submenus.append(self.menu.subsurface(0, 0, self.menu_rect.width, 30).convert()) self.submenu_rects.append(self.submenus[i].get_rect()) else: self.submenus.append(self.menu.subsurface(0, 0, self.menu_rect.width, 20).convert()) self.submenu_rects.append(self.submenus[i].get_rect()) if i % 2 == 0: self.submenus[i].fill((65,65,65)) else: self.submenus[i].fill((55,55,55)) #Close button self.closebtn = pygame.Surface((176, 64)).convert_alpha() self.closebtn_rect = self.closebtn.get_rect() self.closebtn_rect.topleft = (self.rect.x + 353, self.rect.y + 353) self.closebtn.fill((100,100,100,100)) #Primary weapon self.primaryslot = pygame.Surface((272, 80)).convert_alpha() self.primaryslot_rect = self.primaryslot.get_rect() self.primaryslot_rect.topleft = (self.rect.x + 33, self.rect.y + 33) self.primaryslot.fill((100,100,100,100)) #Secondary weapon self.secondslot = pygame.Surface((176, 81)).convert_alpha() self.secondslot_rect = self.secondslot.get_rect() self.secondslot_rect.topleft = (self.rect.x + 33, self.rect.y + 129) self.secondslot.fill((100,100,100,100)) #Melee weapon self.meleeslot = pygame.Surface((176, 81)).convert_alpha() self.meleeslot_rect = self.meleeslot.get_rect() self.meleeslot_rect.topleft = (self.rect.x + 33, self.rect.y + 225) self.meleeslot.fill((100,100,100,100)) #Ammo textures self.ammotexture1 = pygame.image.load(os.path.join(*[x for x in SETTINGS.item_types if x['type'] == 'bullet'][0]['filepath'])).subsurface(0,112,64,16).convert_alpha() self.ammotexture2 = pygame.image.load(os.path.join(*[x for x in SETTINGS.item_types if x['type'] == 'shell'][0]['filepath'])).subsurface(0,112,64,16).convert_alpha() self.ammotexture3 = pygame.image.load(os.path.join(*[x for x in SETTINGS.item_types if x['type'] == 'ferromag'][0]['filepath'])).subsurface(0,112,64,16).convert_alpha() self.ammotexture1 = pygame.transform.scale(self.ammotexture1, (128, 32)) self.ammotexture2 = pygame.transform.scale(self.ammotexture2, (128, 32)) self.ammotexture3 = pygame.transform.scale(self.ammotexture3, (128, 32)) #Ammo 1 self.ammoslot1 = pygame.Surface((191,79)).convert_alpha() self.ammoslot1_rect = self.ammoslot1.get_rect() self.ammoslot1_rect.topleft = (self.rect.x + 320, self.rect.y + 33) self.ammoslot1.fill((100,100,100,100)) #Ammo 2 self.ammoslot2 = pygame.Surface((191,79)).convert_alpha() self.ammoslot2_rect = self.ammoslot2.get_rect() self.ammoslot2_rect.topleft = (self.rect.x + 320, self.rect.y + 129) self.ammoslot2.fill((100,100,100,100)) #Ammo 3 self.ammoslot3 = pygame.Surface((191,79)).convert_alpha() self.ammoslot3_rect = self.ammoslot3.get_rect() self.ammoslot3_rect.topleft = (self.rect.x + 320, self.rect.y + 225) self.ammoslot3.fill((100,100,100,100)) #Ground weapon self.groundslot = pygame.Surface((272, 80)).convert_alpha() self.groundslot_rect = self.groundslot.get_rect() self.groundslot_rect.topleft = (self.rect.x + 33, self.rect.y + 336) self.groundslot.fill((100,100,100,75)) #Stuff self.mousepos = pygame.mouse.get_pos() self.timer = 0 self.closing = False self.text = [TEXT.Text(0, 0, 'NAME', SETTINGS.WHITE, 'DUGAFONT.ttf', 18), TEXT.Text(0, 0, 'DAMAGE: --', SETTINGS.LIGHTGRAY, 'DUGAFONT.ttf', 15), TEXT.Text(0, 0, 'SPREAD: --', SETTINGS.LIGHTGRAY, 'DUGAFONT.ttf', 15), TEXT.Text(0, 0, 'ACCURACY: --', SETTINGS.LIGHTGRAY, 'DUGAFONT.ttf', 15), TEXT.Text(0, 0, 'RANGE: --', SETTINGS.LIGHTGRAY, 'DUGAFONT.ttf', 15), TEXT.Text(0, 0, 'MAGAZINE SIZE: --', SETTINGS.LIGHTGRAY, 'DUGAFONT.ttf', 15), TEXT.Text(0, 0, 'RELOAD TIME: --', SETTINGS.LIGHTGRAY, 'DUGAFONT.ttf', 15), TEXT.Text(0, 0, 'FIRE RATE: --', SETTINGS.LIGHTGRAY, 'DUGAFONT.ttf', 15), TEXT.Text(0, 0, 'AMMO TYPE: --', SETTINGS.LIGHTGRAY, 'DUGAFONT.ttf', 15), TEXT.Text(0, 0, 'DROP', SETTINGS.WHITE, 'DUGAFONT.ttf', 18)] self.ammotext = [TEXT.Text(480, 116, '-- / --', SETTINGS.DARKGRAY, 'DUGAFONT.ttf', 24), TEXT.Text(480, 212, '-- / --', SETTINGS.DARKGRAY, 'DUGAFONT.ttf', 24), TEXT.Text(480, 308, '-- / --', SETTINGS.DARKGRAY, 'DUGAFONT.ttf', 24)] def draw(self, canvas): canvas.blit(self.bg, self.rect) self.timer += SETTINGS.dt #Ammo text if SETTINGS.held_ammo['bullet'] and not SETTINGS.inv_strings_updated: self.ammotext[0].update_string('%s / %s' % (SETTINGS.held_ammo['bullet'], SETTINGS.max_ammo['bullet'])) if SETTINGS.held_ammo['shell'] and not SETTINGS.inv_strings_updated: self.ammotext[1].update_string('%s / %s' % (SETTINGS.held_ammo['shell'], SETTINGS.max_ammo['shell'])) if SETTINGS.held_ammo['ferromag'] and not SETTINGS.inv_strings_updated: self.ammotext[2].update_string('%s / %s' % (SETTINGS.held_ammo['ferromag'], SETTINGS.max_ammo['ferromag'])) for string in self.ammotext: string.draw(canvas) SETTINGS.inv_strings_updated = True #Mouse on close button if self.closebtn_rect.collidepoint(pygame.mouse.get_pos()): canvas.blit(self.closebtn, self.closebtn_rect) if pygame.mouse.get_pressed()[0]: self.timer = 0 self.closing = True #Mouse on primary elif self.primaryslot_rect.collidepoint(pygame.mouse.get_pos()) and (not self.menudraw or self.selected == 'primary'): canvas.blit(self.primaryslot, self.primaryslot_rect) self.selected = 'primary' if pygame.mouse.get_pressed()[0] and SETTINGS.inventory['primary']: self.menudraw = True self.mousepos = pygame.mouse.get_pos() #Mouse on secondary elif self.secondslot_rect.collidepoint(pygame.mouse.get_pos()) and (not self.menudraw or self.selected == 'secondary'): canvas.blit(self.secondslot, self.secondslot_rect) self.selected = 'secondary' if pygame.mouse.get_pressed()[0] and SETTINGS.inventory['secondary']: self.menudraw = True self.mousepos = pygame.mouse.get_pos() #Mouse on melee elif self.meleeslot_rect.collidepoint(pygame.mouse.get_pos()) and (not self.menudraw or self.selected == 'melee'): canvas.blit(self.meleeslot, self.meleeslot_rect) self.selected = 'melee' if pygame.mouse.get_pressed()[0] and SETTINGS.inventory['melee']: self.menudraw = True self.mousepos = pygame.mouse.get_pos() #Mouse on ground weapon slot elif self.groundslot_rect.collidepoint(pygame.mouse.get_pos()) and (not self.menudraw or self.selected == 'ground') and not (self.submenu_rects[-1].collidepoint(pygame.mouse.get_pos()) and self.menudraw): canvas.blit(self.groundslot, self.groundslot_rect) self.selected = 'ground' if pygame.mouse.get_pressed()[0] and SETTINGS.ground_weapon: self.menudraw = True self.mousepos = pygame.mouse.get_pos() #Mouse on ammo1 - run function elif self.ammoslot1_rect.collidepoint(pygame.mouse.get_pos()): self.ammo_selection(1, canvas) #Mouse on ammo2 - run function elif self.ammoslot2_rect.collidepoint(pygame.mouse.get_pos()): self.ammo_selection(2, canvas) #Mouse on ammo3 - run function elif self.ammoslot3_rect.collidepoint(pygame.mouse.get_pos()): self.ammo_selection(3, canvas) #Mouse on menu elif self.menu_rect.collidepoint(pygame.mouse.get_pos()) and self.menudraw: self.menudraw = True else: self.menudraw = False self.selected = None #Draw items - high layer if SETTINGS.inventory['primary']: canvas.blit(SETTINGS.inventory['primary'].subitemtexture, (self.primaryslot_rect.x, self.primaryslot_rect.y + 5)) if SETTINGS.inventory['secondary']: canvas.blit(SETTINGS.inventory['secondary'].subitemtexture, (self.secondslot_rect.x - self.secondslot_rect.width/4, self.secondslot_rect.y)) if SETTINGS.inventory['melee']: canvas.blit(SETTINGS.inventory['melee'].subitemtexture, (self.meleeslot_rect.x - self.secondslot_rect.width/4, self.meleeslot_rect.y)) if SETTINGS.ground_weapon: canvas.blit(SETTINGS.ground_weapon.subitemtexture, (self.groundslot_rect.x, self.groundslot_rect.y + 5)) #Ammo if SETTINGS.held_ammo['bullet']: canvas.blit(self.ammotexture1, (self.ammoslot1_rect.x - self.ammoslot1_rect.width/8, self.ammoslot1_rect.y + 20)) if SETTINGS.held_ammo['shell']: canvas.blit(self.ammotexture2, (self.ammoslot2_rect.x - self.ammoslot2_rect.width/8, self.ammoslot2_rect.y + 20)) if SETTINGS.held_ammo['ferromag']: canvas.blit(self.ammotexture3, (self.ammoslot3_rect.x - self.ammoslot3_rect.width/8, self.ammoslot3_rect.y + 20)) #Draw menu top layer if (self.menudraw and SETTINGS.ground_weapon) or (self.menudraw and SETTINGS.inventory[self.selected]): self.draw_menu(canvas) #Close menu without shooting if self.closing and self.timer >= 0.2: SETTINGS.player_states['invopen'] = False self.closing = False self.timer = 0 SETTINGS.inv_strings_updated = False def draw_menu(self, canvas): if self.selected != 'ground': self.menu_rect.topleft = self.mousepos #Draw menu i = 0 for menu in self.submenus: if i == 0: self.submenu_rects[i].topleft = (self.mousepos[0], self.mousepos[1]) else: self.submenu_rects[i].topleft = (self.mousepos[0], self.mousepos[1]+i*20+10) canvas.blit(menu, self.submenu_rects[i]) i += 1 #Update weapon stats - updates strings, even though it is not needed. Might want to change later, if needed. if SETTINGS.inventory[self.selected]: self.text[0].update_string('%s' % SETTINGS.inventory[self.selected].name) self.text[1].update_string('DAMAGE : %s' % SETTINGS.inventory[self.selected].dmg) self.text[2].update_string('SPREAD : %s' % SETTINGS.inventory[self.selected].accuracy) self.text[3].update_string('ACCURACY : %s / 100' % SETTINGS.inventory[self.selected].hit_percent) self.text[4].update_string('RANGE : %s' % SETTINGS.inventory[self.selected].range) self.text[5].update_string('MAG SIZE : %s' % SETTINGS.inventory[self.selected].mag_size) self.text[6].update_string('REL TIME : %s' % SETTINGS.inventory[self.selected].rlspeed) self.text[7].update_string('FIR RATE : %s' % SETTINGS.inventory[self.selected].firerate) self.text[8].update_string('AMMO TYP : %s' % SETTINGS.inventory[self.selected].ammo_type) self.text[9].update_string('DROP') #Update text x = 0 for string in self.text: if x == 0: string.update_pos(self.mousepos[0]+5, self.mousepos[1]+8) elif x == 9: string.update_pos(self.mousepos[0]+55, self.mousepos[1]+x*20+15) else: string.update_pos(self.mousepos[0]+5, self.mousepos[1]+x*20+12) string.draw(canvas) x += 1 else: self.menu_rect.bottomleft = self.mousepos #Draw menu i = 0 for menu in self.submenus: if i == 0: self.submenu_rects[i].topleft = (self.mousepos[0], self.mousepos[1] - self.menu_rect.height) else: self.submenu_rects[i].topleft = (self.mousepos[0], self.mousepos[1]+i*20+10 - self.menu_rect.height) canvas.blit(menu, self.submenu_rects[i]) i += 1 self.text[0].update_string('%s' % SETTINGS.ground_weapon.name) self.text[1].update_string('DAMAGE : %s %s' % (SETTINGS.ground_weapon.dmg, self.compare_weapons('dmg'))) self.text[2].update_string('SPREAD : %s %s' % (SETTINGS.ground_weapon.accuracy, self.compare_weapons('spr'))) self.text[3].update_string('ACCURACY : %s / 100 %s' % (SETTINGS.ground_weapon.hit_percent, self.compare_weapons('acc'))) self.text[4].update_string('RANGE : %s %s' % (SETTINGS.ground_weapon.range, self.compare_weapons('ran'))) self.text[5].update_string('MAG SIZE : %s %s' % (SETTINGS.ground_weapon.mag_size, self.compare_weapons('mag'))) self.text[6].update_string('REL TIME : %s %s' % (SETTINGS.ground_weapon.rlspeed, self.compare_weapons('rel'))) self.text[7].update_string('FIR RATE : %s %s' % (SETTINGS.ground_weapon.firerate, self.compare_weapons('fir'))) self.text[8].update_string('AMMO TYP : %s' % SETTINGS.ground_weapon.ammo_type) self.text[9].update_string('SWAP') #Update text x = 0 for string in self.text: if x == 0: string.update_pos(self.mousepos[0]+5, self.mousepos[1]+8 - self.menu_rect.height) elif x == 9: string.update_pos(self.mousepos[0]+55, self.mousepos[1]+x*20+15 - self.menu_rect.height) else: string.update_pos(self.mousepos[0]+5, self.mousepos[1]+x*20+12 - self.menu_rect.height) string.draw(canvas) x += 1 #Drop weapon if self.submenu_rects[-1].collidepoint(pygame.mouse.get_pos()): self.submenus[-1].fill((45,45,45)) if pygame.mouse.get_pressed()[0] and self.timer >= 0.5: self.timer = 0 #Find a place to drop item if [x for x in SETTINGS.all_tiles if x.map_pos == [SETTINGS.player_map_pos[0]+1, SETTINGS.player_map_pos[1]] and not SETTINGS.tile_solid[x.ID]] and not [x for x in SETTINGS.all_items if x.map_pos == [SETTINGS.player_map_pos[0]+1, SETTINGS.player_map_pos[1]]]: itempos = [SETTINGS.player_map_pos[0]+1, SETTINGS.player_map_pos[1]] elif [x for x in SETTINGS.all_tiles if x.map_pos == [SETTINGS.player_map_pos[0]-1, SETTINGS.player_map_pos[1]] and not SETTINGS.tile_solid[x.ID]] and not [x for x in SETTINGS.all_items if x.map_pos == [SETTINGS.player_map_pos[0]-1, SETTINGS.player_map_pos[1]]]: itempos = [SETTINGS.player_map_pos[0]-1, SETTINGS.player_map_pos[1]] elif [x for x in SETTINGS.all_tiles if x.map_pos == [SETTINGS.player_map_pos[0], SETTINGS.player_map_pos[1]+1] and not SETTINGS.tile_solid[x.ID]] and not [x for x in SETTINGS.all_items if x.map_pos == [SETTINGS.player_map_pos[0], SETTINGS.player_map_pos[1]+1]]: itempos = [SETTINGS.player_map_pos[0], SETTINGS.player_map_pos[1]+1] elif [x for x in SETTINGS.all_tiles if x.map_pos == [SETTINGS.player_map_pos[0], SETTINGS.player_map_pos[1]-1] and not SETTINGS.tile_solid[x.ID]] and not [x for x in SETTINGS.all_items if x.map_pos == [SETTINGS.player_map_pos[0], SETTINGS.player_map_pos[1]-1]]: itempos = [SETTINGS.player_map_pos[0], SETTINGS.player_map_pos[1]-1] else: itempos = SETTINGS.player_map_pos if self.selected == 'ground': self.selected = SETTINGS.ground_weapon.guntype SETTINGS.all_items.append(ITEMS.Item(itempos, SETTINGS.inventory[self.selected].itemtexture, SETTINGS.inventory[self.selected].guntype, SETTINGS.inventory[self.selected])) if SETTINGS.inventory[self.selected].ammo_type: SETTINGS.held_ammo[SETTINGS.inventory[self.selected].ammo_type] += SETTINGS.current_gun.current_mag SETTINGS.inventory[self.selected].current_mag = 0 if self.selected == SETTINGS.current_gun or SETTINGS.next_gun: SETTINGS.current_gun = None SETTINGS.next_gun = None SETTINGS.inventory[self.selected] = None SETTINGS.inv_strings_updated = False else: self.submenus[-1].fill((55,55,55)) def ammo_selection(self, slot, canvas): bulletlist, shelllist, ferrolist = [], [], [] if SETTINGS.inventory['primary']: if SETTINGS.inventory['primary'].ammo_type == 'bullet': bulletlist.append([self.primaryslot, self.primaryslot_rect]) elif SETTINGS.inventory['primary'].ammo_type == 'shell': shelllist.append([self.primaryslot, self.primaryslot_rect]) elif SETTINGS.inventory['primary'].ammo_type == 'ferromag': ferrolist.append([self.primaryslot, self.primaryslot_rect]) if SETTINGS.inventory['secondary']: if SETTINGS.inventory['secondary'].ammo_type == 'bullet': bulletlist.append([self.secondslot, self.secondslot_rect]) elif SETTINGS.inventory['secondary'].ammo_type == 'shell': shelllist.append([self.secondslot, self.secondslot_rect]) elif SETTINGS.inventory['secondary'].ammo_type == 'ferromag': ferrolist.append([self.secondslot, self.secondslot_rect]) if slot == 1: canvas.blit(self.ammoslot1, self.ammoslot1_rect) if SETTINGS.held_ammo['bullet']: for i in bulletlist: canvas.blit(i[0], i[1]) elif slot == 2: canvas.blit(self.ammoslot2, self.ammoslot2_rect) if SETTINGS.held_ammo['shell']: for i in shelllist: canvas.blit(i[0], i[1]) elif slot == 3: canvas.blit(self.ammoslot3, self.ammoslot3_rect) if SETTINGS.held_ammo['ferromag']: for i in ferrolist: canvas.blit(i[0], i[1]) def compare_weapons(self, comp): stat = None if comp == 'dmg': stat = 'dmg' elif comp == 'spr': stat = 'spread' elif comp == 'acc': stat = 'hitchance' elif comp == 'ran': stat = 'range' elif comp == 'mag': stat = 'magsize' elif comp == 'rel': stat = 'rlspeed' elif comp == 'fir': stat = 'firerate' if SETTINGS.ground_weapon.stats[stat] > SETTINGS.inventory[SETTINGS.ground_weapon.guntype].stats[stat]: if stat != 'rlspeed' and stat != 'firerate': return '+' else: return '-' elif SETTINGS.ground_weapon.stats[stat] < SETTINGS.inventory[SETTINGS.ground_weapon.guntype].stats[stat]: if stat != 'rlspeed' and stat != 'firerate': return '-' else: return '+' else: return '=' #Ammo types: bullet, shell, ferromag #gun types: primary, secondary, melee ================================================ FILE: ITEMS.py ================================================ import SETTINGS import SPRITES import SOUND import pygame import os class Item: def __init__(self, pos, sprite, item_type, effect): '''Item that can be picked up by the player\npos -> tile pos | sprite -> texture path | item_type -> health, armor, *ammo*, gun\neffect -> relative''' self.pos = (pos[0] * SETTINGS.tile_size, pos[1] * SETTINGS.tile_size) self.map_pos = pos self.item_type = item_type self.rect = pygame.Rect(self.pos[0], self.pos[1], int(SETTINGS.tile_size), int(SETTINGS.tile_size)) self.rect.center = (self.pos[0] + SETTINGS.tile_size/2, self.pos[1] + SETTINGS.tile_size/2) self.sprite = SPRITES.Sprite(pygame.image.load(sprite), hash(item_type), self.rect.center, 'sprite') self.effect = effect self.sound = pygame.mixer.Sound(os.path.join('sounds', 'other', 'blub.ogg')) def update(self): remove = False if self.rect: if SETTINGS.player_rect.colliderect(self.rect): if self.item_type == 'health': if SETTINGS.player_health < 100: SETTINGS.player_health += self.effect if SETTINGS.player_health > 100: SETTINGS.player_health = 100 SETTINGS.player_states['heal'] = True remove = True elif self.item_type == 'armor': if SETTINGS.player_armor < 100: SETTINGS.player_armor += self.effect if SETTINGS.player_armor > 100: SETTINGS.player_armor = 100 SETTINGS.player_states['armor'] = True remove = True elif self.item_type == 'bullet' or self.item_type == 'shell' or self.item_type == 'ferromag': if SETTINGS.held_ammo[self.item_type] < SETTINGS.max_ammo[self.item_type]: SETTINGS.held_ammo[self.item_type] += self.effect if SETTINGS.held_ammo[self.item_type] > SETTINGS.max_ammo[self.item_type]: SETTINGS.held_ammo[self.item_type] = SETTINGS.max_ammo[self.item_type] #Same effect as armor SETTINGS.player_states['armor'] = True remove = True elif self.item_type == 'primary': if not SETTINGS.inventory['primary']: SETTINGS.inventory['primary'] = self.effect SETTINGS.next_gun = self.effect SETTINGS.player_states['armor'] = True remove = True else: SETTINGS.ground_weapon = self.effect elif self.item_type == 'secondary': if not SETTINGS.inventory['secondary']: SETTINGS.inventory['secondary'] = self.effect SETTINGS.next_gun = self.effect SETTINGS.player_states['armor'] = True remove = True else: SETTINGS.ground_weapon = self.effect elif self.item_type == 'melee': if not SETTINGS.inventory['melee']: SETTINGS.inventory['melee'] = self.effect SETTINGS.next_gun = self.effect SETTINGS.player_states['armor'] = True remove = True else: SETTINGS.ground_weapon = self.effect #Remove sprite and rect if self.sprite in SETTINGS.all_sprites and remove: SOUND.play_sound(self.sound, 0) SETTINGS.all_sprites.remove(self.sprite) self.rect = None ================================================ FILE: LEVELS.py ================================================ #Level classes for DUGA import SETTINGS class Level: def __init__(self, stats): self.stats = stats self.lvl_number = stats['lvl_number'] self.sky_color = stats['sky_color'] self.ground_color = stats['ground_color'] self.npcs = stats['npcs'] self.items = stats['items'] self.player_pos = stats['player_pos'] self.array = stats['array'] self.shade = stats['shade'][0] self.shade_rgba = stats['shade'][1] self.shade_visibility = stats['shade'][2] if 'name' in stats: self.name = stats['name'] if 'author' in stats: self.author = stats['author'] ##SETTINGS.levels_list.append(Level({ ##'ground_color' : (255, 255, 255), ##'npcs' : [((4, 4), 90, 7)], ##'player_pos' : [1,2], ##'name' : None, ##'sky_color' : (255, 255, 255), ##'lvl_number' : 0, ##'shade' : (False, (0, 0, 0, 0), 0), ##'array' : [[1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 0, 0, 0, 0, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 0, 0, 0, 0, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]], ##'items' : [((3,3), 13)], ##})) ##SETTINGS.levels_list.append(Level({ ## 'lvl_number' : 0, ## 'sky_color' : SETTINGS.GRAY, ## 'ground_color': SETTINGS.LIGHTGRAY, ## 'npcs' : [([2,3], 270, 4), ([3,3], 270, 5)],#, ([3,3], 270, 3), ([4,3], 270, 3), ([1,3], 270, 3)], ## 'items' : [([1,1], 2), ([2,1], 3), ([3,1], 7), ([4, 1], 6), ([4,2], 8), ([3,2], 9), ([1,2], 11)], ## 'player_pos' : [2,2], ## 'shade' : (False, (0,0,0,0), 0), ## 'array' : [ ## #0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 ## [1,1,1,1,1,1], ## [1,0,0,0,0,1], ## [1,0,0,0,0,1], ## [1,0,0,0,0,1], ## [1,0,0,0,0,1], ## [1,1,5,1,1,1] ## ]})) ##SETTINGS.levels_list.append(Level({ ## 'lvl_number' : 1, ## 'sky_color' : SETTINGS.GRAY, ## 'ground_color': SETTINGS.BROWN, ## 'npcs' : [([2,6], 270, 4), ([10,10], 180, 1), ([17, 9], 180, 0)], ## 'items' : [([8,2], 8), ([2,7], 5), ([13,4], 0), ([10, 5], 2), ([12, 12], 2), ([16, 8], 1), ([16, 11], 0)], ## 'player_pos' : [6,1], ## 'shade' : (True, (0,0,0,0), 500), ## 'array' : [ ## #0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 ## [0,0,0,0,0,1,1,2,1,0,0,0,0,0,0,0,0,0,0], ## [0,0,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0], ## [0,0,0,0,1,0,0,8,0,1,0,0,0,0,0,0,0,0,0], ## [0,0,0,0,0,1,1,6,1,1,1,2,1,1,0,0,0,0,0], ## [0,0,0,0,1,0,0,0,0,0,0,1,0,0,1,0,0,0,0], ## [0,5,5,5,1,0,8,0,0,0,0,1,8,0,1,0,0,0,0], ## [1,0,0,0,1,0,0,1,1,6,1,1,10,1,0,0,0,0,0], ## [2,0,8,0,1,0,0,1,7,0,0,0,0,0,1,5,2,5,0], ## [1,0,0,0,1,0,0,1,0,0,0,0,0,0,1,0,0,0,5], ## [0,1,1,0,1,0,0,1,1,1,3,1,1,1,1,0,0,0,3], ## [0,0,1,0,7,0,0,0,0,0,8,0,0,0,7,0,8,0,5], ## [0,0,1,1,1,7,0,1,1,1,1,1,1,6,1,0,0,0,5], ## [0,0,0,0,0,1,1,0,5,7,0,8,0,0,1,5,5,5,0], ## [0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0], ## [0,0,0,0,0,0,0,0,0,0,1,2,1,1,0,0,0,0,0], ## ]})) ##SETTINGS.levels_list.append(Level({ ##'items' : [((2, 1), 7), ((1, 2), 1), ((2, 2), 3), ((3, 2), 3)], ##'npcs' : [((1, 4), 0, 5), ((2, 4), 90, 5), ((3, 4), 180, 5), ((4, 4), 180, 5), ((5, 4), 180, 5), ((1, 5), 90, 5), ((2, 5), 90, 5), ((3, 5), 180, 5), ((4, 5), 180, 5), ((5, 5), 180, 5), ((2, 6), 90, 5), ((3, 6), 90, 5), ((4, 6), 180, 5), ((5, 6), 180, 5)], ##'name' : None, ##'array' : [[0, 16, 17, 16, 0, 11, 11, 12, 11, 0], [16, 0, 0, 15, 16, 13, 19, 0, 21, 11], [16, 0, 0, 0, 18, 11, 19, 0, 0, 13], [0, 16, 10, 16, 16, 16, 11, 15, 0, 11], [5, 0, 0, 0, 0, 0, 16, 11, 0, 11], [5, 0, 0, 8, 0, 0, 9, 0, 0, 12], [5, 20, 0, 0, 0, 0, 16, 11, 0, 11], [0, 16, 16, 16, 16, 16, 11, 15, 0, 11], [0, 0, 0, 0, 0, 0, 11, 21, 0, 11], [0, 0, 0, 0, 0, 0, 0, 11, 14, 0]], ##'ground_color' : (255, 255, 255), ##'lvl_number' : None, ##'shade' : (False, (0,0,0,0), 0), ##'player_pos' : [1, 1], ##'sky_color' : (255, 255, 255), ##})) ## ##SETTINGS.levels_list.append(Level({ ## 'lvl_number' : 2, ## 'sky_color': SETTINGS.LIGHTGRAY, ## 'ground_color': SETTINGS.LIGHTGRAY, ## 'npcs' : [([3,15], 90, 0), ([8,8], 0, 0), ([17,14], 270, 0), ([17, 11], 0, 1), ([5,11], 270, 3)], ## 'items' : [([5,15],2), ([6,5], 1), ([10, 1], 6),([9,1], 2), ([11,1], 2), ([1, 10], 2), ([2, 19], 2), ([10, 9], 0), ([15, 6], 1), ([15, 7], 3), ([7, 18], 0), ([7, 19], 2)], ## 'player_pos' : [6,2], ## 'array' : [ ## #0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 ## [0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0], ## [0,0,0,0,0,0,1,0,1,0,8,0,1,0,0,0,0,0,0,0,0], ## [0,0,0,0,0,1,0,1,1,0,0,0,1,0,0,0,0,0,0,0,0], ## [0,1,1,1,1,1,10,1,1,1,10,1,0,0,0,0,0,0,0,0,0], ## [1,0,0,0,0,0,0,0,0,0,0,7,1,0,1,1,0,0,0,0,0], ## [1,0,8,0,0,0,0,0,0,0,0,0,1,1,0,0,1,0,0,0,0], ## [1,0,0,1,1,1,1,1,1,1,10,1,1,1,0,0,1,0,0,0,0], ## [1,0,0,1,0,0,1,0,0,0,0,0,0,9,0,0,1,0,0,0,0], ## [1,0,0,1,0,0,9,0,0,0,8,0,0,1,0,0,1,0,0,0,0], ## [1,0,0,1,0,0,1,7,0,0,0,0,0,1,1,1,0,0,4,0,0], ## [1,0,0,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,10,1,0], ## [1,0,8,0,0,0,0,0,0,8,0,0,0,0,1,1,0,0,0,0,1], ## [1,0,0,0,0,0,0,0,0,0,0,0,0,7,1,1,0,0,0,0,1], ## [0,1,1,10,1,1,1,1,1,1,1,1,10,1,1,0,1,1,10,1,0], ## [1,0,0,0,0,0,1,0,0,1,0,0,0,0,0,1,0,0,0,0,1], ## [1,0,0,8,0,0,1,0,0,1,0,0,8,0,0,1,0,0,8,0,1], ## [0,1,10,1,1,1,1,1,1,1,1,1,0,1,1,1,0,0,0,0,1], ## [1,0,0,0,0,7,1,0,0,9,0,0,0,0,0,0,0,0,0,0,1], ## [1,0,0,8,0,0,1,0,0,1,1,1,1,1,1,1,1,1,1,1,0], ## [1,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0], ## [0,1,1,1,1,1,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0], ## ]})) ## ##SETTINGS.levels_list.append(Level({ ## 'lvl_number' : 3, ## 'sky_color' : SETTINGS.GRAY, ## 'ground_color' : SETTINGS.DARKRED, ## 'npcs' : [([5,18], 90, 4), ([4,2], 270, 0), ([5,7], 180, 3)], ## 'items' : [([10,3],1), ([8,2],4), ([5,10],7), ([1,9],3), ([1,10],3), ([1,11],3), ([10,9],2), ([10,10],2), ([10,11],2), ([5,14],0), ([2,10],0), ([9,10],1),([1,18],0),([9,18],0)], ## 'player_pos' : [1,2], ## 'array' : [ ## #0 1 2 3 4 5 6 7 8 9 0 11 ## [0,0,0,1,2,1,0,1,1,0,5,0],#0 ## [0,1,1,0,0,0,1,0,0,1,0,5],#1 ## [1,0,9,0,8,0,9,0,0,9,0,5],#2 ## [0,1,1,0,0,0,1,0,0,1,0,5],#3 ## [0,0,0,1,1,1,1,0,0,1,5,0],#4 ## [0,0,0,1,7,0,0,0,0,1,0,0],#5 ## [0,0,0,2,0,8,0,1,1,0,0,0],#6 ## [0,0,0,1,0,0,0,1,0,0,0,0],#7 ## [0,1,1,0,1,10,1,0,1,1,1,0],#8 ## [1,0,0,1,0,0,0,1,0,0,0,1],#9 ## [3,0,8,9,0,8,0,9,0,8,0,2],#10 ## [1,0,0,1,0,0,0,1,0,0,0,1],#11 ## [0,1,1,0,1,10,1,0,1,3,1,0],#12 ## [0,0,0,1,0,0,0,1,0,0,0,0],#13 ## [0,0,0,1,0,8,0,1,0,0,0,0],#14 ## [0,0,0,1,1,10,1,1,0,0,0,0],#15 ## [0,1,1,0,0,8,0,0,1,1,0,0],#16 ## [1,0,1,0,6,0,6,0,1,0,1,0],#17 ## [2,0,9,8,0,8,0,8,9,0,3,0],#18 ## [1,0,1,0,6,0,6,0,1,0,1,0],#19 ## [0,1,1,0,0,8,0,0,1,1,0,0],#20 ## [0,0,0,1,1,10,1,1,0,0,0,0],#21 ## [0,0,0,5,0,0,0,5,0,0,0,0],#22 ## [0,0,0,5,0,8,0,5,0,0,0,0],#23 ## [0,0,0,0,1,10,1,0,0,0,0,0],#24 ## [0,0,0,0,1,0,1,0,0,0,0,0],#25 ## [0,0,0,0,0,4,0,0,0,0,0,0],#26 ## ]})) ## ##SETTINGS.levels_list.append(Level({ ##'items' : [((1, 1), 2), ((2, 1), 6), ((3, 1), 2)], ##'npcs' : [((8, 8), 180, 0), ((3, 10), 90, 2), ((8, 10), 180, 0)], ##'player_pos' : [3, 2], ##'sky_color' : SETTINGS.LIGHTGRAY, ##'array' : [[1, 1, 1, 1, 1, 0, 0, 0, 0, 0], [1, 0, 0, 0, 2, 0, 0, 0, 0, 0], [1, 0, 0, 0, 1, 0, 0, 0, 0, 0], [1, 1, 10, 1, 1, 1, 1, 1, 1, 1], [1, 0, 0, 0, 0, 0, 0, 0, 7, 1], [1, 0, 0, 0, 0, 0, 0, 0, 0, 1], [1, 0, 8, 0, 0, 0, 0, 0, 0, 1], [3, 0, 0, 0, 1, 1, 1, 2, 1, 1], [1, 0, 0, 0, 1, 0, 0, 0, 0, 5], [1, 0, 0, 0, 9, 0, 0, 8, 0, 4], [1, 0, 0, 0, 1, 0, 0, 0, 0, 5], [1, 2, 1, 1, 1, 5, 5, 5, 5, 5]], ##'ground_color' : SETTINGS.DARKGRAY, ##'lvl_number' : 4, ##})) #NPC spawn syntax: [([map pos], face, id)] #Item spawn syntax: [([map pos], id)] ================================================ FILE: LICENSE.txt ================================================ Mozilla Public License Version 2.0 ================================== 1. Definitions -------------- 1.1. "Contributor" means each individual or legal entity that creates, contributes to the creation of, or owns Covered Software. 1.2. "Contributor Version" means the combination of the Contributions of others (if any) used by a Contributor and that particular Contributor's Contribution. 1.3. "Contribution" means Covered Software of a particular Contributor. 1.4. "Covered Software" means Source Code Form to which the initial Contributor has attached the notice in Exhibit A, the Executable Form of such Source Code Form, and Modifications of such Source Code Form, in each case including portions thereof. 1.5. "Incompatible With Secondary Licenses" means (a) that the initial Contributor has attached the notice described in Exhibit B to the Covered Software; or (b) that the Covered Software was made available under the terms of version 1.1 or earlier of the License, but not also under the terms of a Secondary License. 1.6. "Executable Form" means any form of the work other than Source Code Form. 1.7. "Larger Work" means a work that combines Covered Software with other material, in a separate file or files, that is not Covered Software. 1.8. "License" means this document. 1.9. "Licensable" means having the right to grant, to the maximum extent possible, whether at the time of the initial grant or subsequently, any and all of the rights conveyed by this License. 1.10. "Modifications" means any of the following: (a) any file in Source Code Form that results from an addition to, deletion from, or modification of the contents of Covered Software; or (b) any new file in Source Code Form that contains any Covered Software. 1.11. "Patent Claims" of a Contributor means any patent claim(s), including without limitation, method, process, and apparatus claims, in any patent Licensable by such Contributor that would be infringed, but for the grant of the License, by the making, using, selling, offering for sale, having made, import, or transfer of either its Contributions or its Contributor Version. 1.12. "Secondary License" means either the GNU General Public License, Version 2.0, the GNU Lesser General Public License, Version 2.1, the GNU Affero General Public License, Version 3.0, or any later versions of those licenses. 1.13. "Source Code Form" means the form of the work preferred for making modifications. 1.14. "You" (or "Your") means an individual or a legal entity exercising rights under this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with You. For purposes of this definition, "control" means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of more than fifty percent (50%) of the outstanding shares or beneficial ownership of such entity. 2. License Grants and Conditions -------------------------------- 2.1. Grants Each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license: (a) under intellectual property rights (other than patent or trademark) Licensable by such Contributor to use, reproduce, make available, modify, display, perform, distribute, and otherwise exploit its Contributions, either on an unmodified basis, with Modifications, or as part of a Larger Work; and (b) under Patent Claims of such Contributor to make, use, sell, offer for sale, have made, import, and otherwise transfer either its Contributions or its Contributor Version. 2.2. Effective Date The licenses granted in Section 2.1 with respect to any Contribution become effective for each Contribution on the date the Contributor first distributes such Contribution. 2.3. Limitations on Grant Scope The licenses granted in this Section 2 are the only rights granted under this License. No additional rights or licenses will be implied from the distribution or licensing of Covered Software under this License. Notwithstanding Section 2.1(b) above, no patent license is granted by a Contributor: (a) for any code that a Contributor has removed from Covered Software; or (b) for infringements caused by: (i) Your and any other third party's modifications of Covered Software, or (ii) the combination of its Contributions with other software (except as part of its Contributor Version); or (c) under Patent Claims infringed by Covered Software in the absence of its Contributions. This License does not grant any rights in the trademarks, service marks, or logos of any Contributor (except as may be necessary to comply with the notice requirements in Section 3.4). 2.4. Subsequent Licenses No Contributor makes additional grants as a result of Your choice to distribute the Covered Software under a subsequent version of this License (see Section 10.2) or under the terms of a Secondary License (if permitted under the terms of Section 3.3). 2.5. Representation Each Contributor represents that the Contributor believes its Contributions are its original creation(s) or it has sufficient rights to grant the rights to its Contributions conveyed by this License. 2.6. Fair Use This License is not intended to limit any rights You have under applicable copyright doctrines of fair use, fair dealing, or other equivalents. 2.7. Conditions Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in Section 2.1. 3. Responsibilities ------------------- 3.1. Distribution of Source Form All distribution of Covered Software in Source Code Form, including any Modifications that You create or to which You contribute, must be under the terms of this License. You must inform recipients that the Source Code Form of the Covered Software is governed by the terms of this License, and how they can obtain a copy of this License. You may not attempt to alter or restrict the recipients' rights in the Source Code Form. 3.2. Distribution of Executable Form If You distribute Covered Software in Executable Form then: (a) such Covered Software must also be made available in Source Code Form, as described in Section 3.1, and You must inform recipients of the Executable Form how they can obtain a copy of such Source Code Form by reasonable means in a timely manner, at a charge no more than the cost of distribution to the recipient; and (b) You may distribute such Executable Form under the terms of this License, or sublicense it under different terms, provided that the license for the Executable Form does not attempt to limit or alter the recipients' rights in the Source Code Form under this License. 3.3. Distribution of a Larger Work You may create and distribute a Larger Work under terms of Your choice, provided that You also comply with the requirements of this License for the Covered Software. If the Larger Work is a combination of Covered Software with a work governed by one or more Secondary Licenses, and the Covered Software is not Incompatible With Secondary Licenses, this License permits You to additionally distribute such Covered Software under the terms of such Secondary License(s), so that the recipient of the Larger Work may, at their option, further distribute the Covered Software under the terms of either this License or such Secondary License(s). 3.4. Notices You may not remove or alter the substance of any license notices (including copyright notices, patent notices, disclaimers of warranty, or limitations of liability) contained within the Source Code Form of the Covered Software, except that You may alter any license notices to the extent required to remedy known factual inaccuracies. 3.5. Application of Additional Terms You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Covered Software. However, You may do so only on Your own behalf, and not on behalf of any Contributor. You must make it absolutely clear that any such warranty, support, indemnity, or liability obligation is offered by You alone, and You hereby agree to indemnify every Contributor for any liability incurred by such Contributor as a result of warranty, support, indemnity or liability terms You offer. You may include additional disclaimers of warranty and limitations of liability specific to any jurisdiction. 4. Inability to Comply Due to Statute or Regulation --------------------------------------------------- If it is impossible for You to comply with any of the terms of this License with respect to some or all of the Covered Software due to statute, judicial order, or regulation then You must: (a) comply with the terms of this License to the maximum extent possible; and (b) describe the limitations and the code they affect. Such description must be placed in a text file included with all distributions of the Covered Software under this License. Except to the extent prohibited by statute or regulation, such description must be sufficiently detailed for a recipient of ordinary skill to be able to understand it. 5. Termination -------------- 5.1. The rights granted under this License will terminate automatically if You fail to comply with any of its terms. However, if You become compliant, then the rights granted under this License from a particular Contributor are reinstated (a) provisionally, unless and until such Contributor explicitly and finally terminates Your grants, and (b) on an ongoing basis, if such Contributor fails to notify You of the non-compliance by some reasonable means prior to 60 days after You have come back into compliance. Moreover, Your grants from a particular Contributor are reinstated on an ongoing basis if such Contributor notifies You of the non-compliance by some reasonable means, this is the first time You have received notice of non-compliance with this License from such Contributor, and You become compliant prior to 30 days after Your receipt of the notice. 5.2. If You initiate litigation against any entity by asserting a patent infringement claim (excluding declaratory judgment actions, counter-claims, and cross-claims) alleging that a Contributor Version directly or indirectly infringes any patent, then the rights granted to You by any and all Contributors for the Covered Software under Section 2.1 of this License shall terminate. 5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user license agreements (excluding distributors and resellers) which have been validly granted by You or Your distributors under this License prior to termination shall survive termination. ************************************************************************ * * * 6. Disclaimer of Warranty * * ------------------------- * * * * Covered Software is provided under this License on an "as is" * * basis, without warranty of any kind, either expressed, implied, or * * statutory, including, without limitation, warranties that the * * Covered Software is free of defects, merchantable, fit for a * * particular purpose or non-infringing. The entire risk as to the * * quality and performance of the Covered Software is with You. * * Should any Covered Software prove defective in any respect, You * * (not any Contributor) assume the cost of any necessary servicing, * * repair, or correction. This disclaimer of warranty constitutes an * * essential part of this License. No use of any Covered Software is * * authorized under this License except under this disclaimer. * * * ************************************************************************ ************************************************************************ * * * 7. Limitation of Liability * * -------------------------- * * * * Under no circumstances and under no legal theory, whether tort * * (including negligence), contract, or otherwise, shall any * * Contributor, or anyone who distributes Covered Software as * * permitted above, be liable to You for any direct, indirect, * * special, incidental, or consequential damages of any character * * including, without limitation, damages for lost profits, loss of * * goodwill, work stoppage, computer failure or malfunction, or any * * and all other commercial damages or losses, even if such party * * shall have been informed of the possibility of such damages. This * * limitation of liability shall not apply to liability for death or * * personal injury resulting from such party's negligence to the * * extent applicable law prohibits such limitation. Some * * jurisdictions do not allow the exclusion or limitation of * * incidental or consequential damages, so this exclusion and * * limitation may not apply to You. * * * ************************************************************************ 8. Litigation ------------- Any litigation relating to this License may be brought only in the courts of a jurisdiction where the defendant maintains its principal place of business and such litigation shall be governed by laws of that jurisdiction, without reference to its conflict-of-law provisions. Nothing in this Section shall prevent a party's ability to bring cross-claims or counter-claims. 9. Miscellaneous ---------------- This License represents the complete agreement concerning the subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. Any law or regulation which provides that the language of a contract shall be construed against the drafter shall not be used to construe this License against a Contributor. 10. Versions of the License --------------------------- 10.1. New Versions Mozilla Foundation is the license steward. Except as provided in Section 10.3, no one other than the license steward has the right to modify or publish new versions of this License. Each version will be given a distinguishing version number. 10.2. Effect of New Versions You may distribute the Covered Software under the terms of the version of the License under which You originally received the Covered Software, or under the terms of any subsequent version published by the license steward. 10.3. Modified Versions If you create software not governed by this License, and you want to create a new license for such software, you may create and use a modified version of this License if you rename the license and remove any references to the name of the license steward (except to note that such modified license differs from this License). 10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses If You choose to distribute Source Code Form that is Incompatible With Secondary Licenses under the terms of this version of the License, the notice described in Exhibit B of this License must be attached. Exhibit A - Source Code Form License Notice ------------------------------------------- This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice. You may add additional accurate notices of copyright ownership. Exhibit B - "Incompatible With Secondary Licenses" Notice --------------------------------------------------------- This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0. ================================================ FILE: LevelEditor.py ================================================ #This is the map editor. It does not have anything to do with the game. #It needs access to LEVELS.py, TEXTURES.py, ENTITIES.py, TEXT.py and SETTINGS.py import pygame import copy import os import sys import pickle import LEVELS import ENTITIES import SETTINGS import TEXTURES import TEXT pygame.init() pygame.font.init() current_id = 1 current_item = 0 current_npc = 0 npc_face = 90 doors = [] mode = 'tile' ltype = None segtypes = ['normal', 'start', 'end'] def determine_mode(): ENTITIES.load_guns() ENTITIES.load_item_types() ENTITIES.load_npc_types() print("What would you like to do?") print("new / load") pick = input("> ").lower() if pick == "new" or pick == 'n': determine_type() determine_size() elif pick == "load" or pick == 'l': loader.load_map() else: print("Invalid argument.") determine_mode() editorCanvas.load_items() editorCanvas.load_npcs() def determine_type(): global ltype print("What type of map would you like to create?") print("level / segment") pick = input("> ").lower() if pick == "level" or pick == 'l': ltype = "level" elif pick == "segment" or pick == 's': ltype = "segment" else: print("Invalid argument!") determine_type() def determine_size(): global ltype if ltype == 'level': width = input("Map width in tiles: ") height = input("Map height in tiles: ") else: width = 9 height = 9 try: editorCanvas.__init__(int(width)*32 + 170, int(height)*32 + 170) currentMap.__init__(int(width), int(height)) except Exception as e: print("Invalid arguments.") print(e) determine_size() def what_now(): global ltype isgood = False sc = gc = (SETTINGS.WHITE) print() while not isgood: isgood = True if ltype == 'level': print("What sky colour should your level have? Leave blank for white.") sc = input("R,G,B > ") if sc: try: sc = sc.replace(' ', '') sc = sc.split(',') for i in range(3): sc[i] = int(sc[i]) sc = tuple(sc) except: print("Error") isgood = False sc = (0,0,0) else: sc = (255,255,255) print() print("What ground colour should your level have? Leave blank for white.") gc = input("R,G,B > ") if gc: try: gc = gc.replace(' ', '') gc = gc.split(',') for i in range(3): gc[i] = int(gc[i]) gc = tuple(gc) except: print("Error") isgood = False gc = (0,0,0) else: gc = (255,255,255) for i in gc+sc: if i < 0 or i > 255: isgood = False if len(gc+sc) > 6: isgood = False editorCanvas.dict['ground_color'] = gc editorCanvas.dict['sky_color'] = sc print() print("Would you like to save the map?") yn = input("Y/N > ").lower() if yn == 'y' or yn == 'yes': if isgood: loader.save_map(gc, sc) print() print("Map saved!") elif yn == 'n' or yn == 'no': sys.exit(1) else: isgood = False if not isgood: if ltype == 'level': print("You made an error somewhere. Let's try again. R,G,B must be three values between 0-255 seperated by commas.") else: print("You made an error somewhere. Please try again.") print() class Canvas: def __init__(self, width, height): global mode, ltype, segtypes if width > 0 and height > 0: self.width = max(width, 438) self.height = max(height, 438) else: self.width = 1 self.height = 1 self.canvas = pygame.display.set_mode((self.width, self.height)) self.segtype = 0 pygame.display.set_caption("DUGA Map Editor") self.stop = False self.items = [] self.showauthor = False self.exit = False self.tile_textures = [] for i in range(len(TEXTURES.all_textures)): texture = TEXTURES.all_textures[i] t = pygame.image.load(texture).convert_alpha() t = pygame.transform.scale(t, (64,64)) if SETTINGS.texture_type[i] == 'vdoor': t = pygame.transform.rotate(t, 90) self.tile_textures.append(t) #Author self.authortext = TEXT.Text(self.width - 135, 5, 'Author: %s', SETTINGS.LIGHTGRAY, 'DUGAFONT.ttf', 11) #Export self.exporttext = TEXT.Text(20, self.height-32, 'EXPORT', SETTINGS.BLACK, 'DUGAFONT.ttf', 14) self.exportbtn = pygame.Surface((64,32)) self.exportrct = self.exportbtn.get_rect() self.exportrct.topleft = (15, self.height-40) self.exportbtn.fill(SETTINGS.WHITE) #Selected mode self.selecttext = TEXT.Text(100, self.height-32, mode, SETTINGS.GREEN, 'DUGAFONT.ttf', 24) #Tile self.nxttext = TEXT.Text(self.width-28, 60, '>', SETTINGS.BLACK, 'DUGAFONT.ttf', 20) self.nexttile = pygame.Surface((30, 40)) self.nexttilerct = self.nexttile.get_rect() self.nexttilerct.topleft = (self.width-40, 50) self.nexttile.fill(SETTINGS.WHITE) self.prvtext = TEXT.Text(self.width-135, 60, '<', SETTINGS.BLACK, 'DUGAFONT.ttf', 20) self.prevtile = pygame.Surface((30, 40)) self.prevtilerct = self.prevtile.get_rect() self.prevtilerct.topleft = (self.width-140, 50) self.prevtile.fill(SETTINGS.WHITE) self.tiletypetext = TEXT.Text(self.width-95, 15, 'TEXTURE TYPE', SETTINGS.WHITE, 'DUGAFONT.ttf', 20) self.tilerect = self.tile_textures[current_id].get_rect() self.tilerect.topleft = (self.width-107, 40) #startpos self.startpossurf = pygame.Surface((64,64)) self.startposrct = self.startpossurf.get_rect() self.startposrct.topleft = (self.width-105, 335) self.startpossurf.fill(SETTINGS.DARKRED) self.startpostxt = TEXT.Text(self.width-95, 345, 'START', SETTINGS.WHITE, 'DUGAFONT.ttf', 15) self.startpostxt2 = TEXT.Text(self.width-95, 370, 'POS', SETTINGS.WHITE, 'DUGAFONT.ttf', 15) #Segment? if ltype == "segment": #Doors self.doortxt = TEXT.Text(30, self.height-110, 'ENTRANCES', SETTINGS.BLUE, 'DUGAFONT.ttf', 20) #up self.doorup = pygame.Surface((20,20)) self.dooruprct = self.doorup.get_rect() self.dooruprct.topleft = (30, self.height-80) self.doorup.fill(SETTINGS.WHITE) self.dooruptxt = TEXT.Text(36, self.height-76, '^', SETTINGS.BLACK, 'DUGAFONT.ttf', 20) #down self.doordown = pygame.Surface((20, 20)) self.doordownrct = self.doordown.get_rect() self.doordownrct.topleft = (55, self.height-80) self.doordown.fill(SETTINGS.WHITE) self.doordowntxt = TEXT.Text(60, self.height-80, 'v', SETTINGS.BLACK, 'DUGAFONT.ttf', 20) #left self.doorleft = pygame.Surface((20, 20)) self.doorleftrct = self.doorleft.get_rect() self.doorleftrct.topleft = (80, self.height-80) self.doorleft.fill(SETTINGS.WHITE) self.doorlefttxt = TEXT.Text(85, self.height-80, '<', SETTINGS.BLACK, 'DUGAFONT.ttf', 20) #right self.doorright = pygame.Surface((20, 20)) self.doorrightrct = self.doorright.get_rect() self.doorrightrct.topleft = (105, self.height-80) self.doorright.fill(SETTINGS.WHITE) self.doorrighttxt = TEXT.Text(110, self.height-80, '>', SETTINGS.BLACK, 'DUGAFONT.ttf', 20) #typeleft self.segtypeleft = pygame.Surface((20,20)) self.segtypeleftrct = self.segtypeleft.get_rect() self.segtypeleftrct.topleft = (20, self.height-140) self.segtypeleft.fill(SETTINGS.WHITE) self.segtypelefttxt = TEXT.Text(25, self.height-140, '<', SETTINGS.BLACK, 'DUGAFONT.ttf', 20) #segtype self.segtypetxt = TEXT.Text(20, self.height-160, 'SEGMENT TYPE', SETTINGS.DARKGREEN, 'DUGAFONT.ttf', 20) self.csegtypetxt = TEXT.Text(45, self.height-140, segtypes[self.segtype], SETTINGS.LIGHTGREEN, 'DUGAFONT.ttf', 20) #typeright self.segtyperight = pygame.Surface((20,20)) self.segtyperightrct = self.segtypeleft.get_rect() self.segtyperightrct.topleft = (130, self.height-140) self.segtyperight.fill(SETTINGS.WHITE) self.segtyperighttxt = TEXT.Text(135, self.height-140, '>', SETTINGS.BLACK, 'DUGAFONT.ttf', 20) def load_items(self): print("Loading") self.item_names = [] self.item_ids = [] for i in SETTINGS.item_types: img = pygame.image.load(os.path.join(*i['filepath'])) img = pygame.transform.scale(img, (64, 64)) self.items.append(img) self.item_ids.append(i['id']) self.item_names.append(i['type']) self.nextitemtext = TEXT.Text(self.width-28, 165, '>', SETTINGS.BLACK, 'DUGAFONT.ttf', 20) self.nextitem = pygame.Surface((30, 40)) self.nextitemrct = self.nextitem.get_rect() self.nextitemrct.topleft = (self.width-40, 150) self.nextitem.fill(SETTINGS.WHITE) self.previtemtext = TEXT.Text(self.width-135, 165, '<', SETTINGS.BLACK, 'DUGAFONT.ttf', 20) self.previtem = pygame.Surface((30, 40)) self.previtemrct = self.previtem.get_rect() self.previtemrct.topleft = (self.width-140, 150) self.previtem.fill(SETTINGS.WHITE) self.itemtypetext = TEXT.Text(self.width-140, 110, 'ITEM TYPE', SETTINGS.WHITE, 'DUGAFONT.ttf', 20) self.itemrect = self.items[current_item].get_rect() self.itemrect.topleft = (self.width-107, 110) def load_npcs(self): global current_npc, npc_face self.npc_stats = [] self.npc_textures = [] for i in SETTINGS.npc_types: self.npc_stats.append(i) img = pygame.image.load(os.path.join(*i['filepath'])) img2 = img.subsurface(0,0,64,128).convert_alpha() img2 = pygame.transform.scale(img2, (64,64)) self.npc_textures.append(img2) self.npcrect = self.npc_textures[current_npc].get_rect() self.npcrect.topleft = (self.width-107, 210) self.npctypetext = TEXT.Text(self.width-140, 195, self.npc_stats[current_npc]['name'], SETTINGS.WHITE, 'DUGAFONT.ttf', 20) self.nextnpctext = TEXT.Text(self.width-28, 235, '>', SETTINGS.BLACK, 'DUGAFONT.ttf', 20) self.nextnpc = pygame.Surface((30, 40)) self.nextnpcrct = self.nextnpc.get_rect() self.nextnpcrct.topleft = (self.width-40, 230) self.nextnpc.fill(SETTINGS.WHITE) self.prevnpctext = TEXT.Text(self.width-135, 235, '<', SETTINGS.BLACK, 'DUGAFONT.ttf', 20) self.prevnpc = pygame.Surface((30, 40)) self.prevnpcrct = self.prevnpc.get_rect() self.prevnpcrct.topleft = (self.width-135, 230) self.prevnpc.fill(SETTINGS.WHITE) #face self.npcfacetext = TEXT.Text(self.width-100, 290, str(npc_face), SETTINGS.WHITE, 'DUGAFONT.ttf', 24) self.npcfrect = pygame.Rect(self.width-100, 280, 64, 64) self.nextnpcftext = TEXT.Text(self.width-28, 295, '>', SETTINGS.BLACK, 'DUGAFONT.ttf', 20) self.nextnpcf = pygame.Surface((30, 40)) self.nextnpcfrct = self.nextnpcf.get_rect() self.nextnpcfrct.topleft = (self.width-40, 280) self.nextnpcf.fill(SETTINGS.WHITE) self.prevnpcftext = TEXT.Text(self.width-135, 295, '<', SETTINGS.BLACK, 'DUGAFONT.ttf', 20) self.prevnpcf = pygame.Surface((30, 40)) self.prevnpcfrct = self.prevnpcf.get_rect() self.prevnpcfrct.topleft = (self.width-135, 280) self.prevnpcf.fill(SETTINGS.WHITE) def draw(self): global current_id, current_item, mode, current_npc self.canvas.fill(SETTINGS.BLACK) #Author if self.showauthor: self.authortext.draw(self.canvas) #Export self.canvas.blit(self.exportbtn, self.exportrct) self.exporttext.draw(self.canvas) #Mode self.selecttext.update_string("PLACING: " + mode) self.selecttext.draw(self.canvas) #Tile self.canvas.blit(self.nexttile, self.nexttilerct) self.nxttext.draw(self.canvas) self.canvas.blit(self.prevtile, self.prevtilerct) self.prvtext.draw(self.canvas) self.tiletypetext.draw(self.canvas) self.canvas.blit(self.tile_textures[current_id], self.tilerect) #Item self.canvas.blit(self.items[current_item], self.itemrect) self.canvas.blit(self.nextitem, self.nextitemrct) self.nextitemtext.draw(self.canvas) self.canvas.blit(self.previtem, self.previtemrct) self.previtemtext.draw(self.canvas) self.itemtypetext.draw(self.canvas) currentMap.add_item(self.items[current_item], self.item_ids[current_item]) #NPC self.canvas.blit(self.npc_textures[current_npc], self.npcrect) self.canvas.blit(self.nextnpc, self.nextnpcrct) self.nextnpctext.draw(self.canvas) self.canvas.blit(self.prevnpc, self.prevnpcrct) self.prevnpctext.draw(self.canvas) self.npctypetext.draw(self.canvas) #Npc face self.npcfacetext.draw(self.canvas) self.canvas.blit(self.nextnpcf, self.nextnpcfrct) self.nextnpcftext.draw(self.canvas) self.canvas.blit(self.prevnpcf, self.prevnpcfrct) self.prevnpcftext.draw(self.canvas) currentMap.add_npc(self.npc_textures[current_npc], current_npc) #Doors if ltype == "segment": self.doortxt.draw(self.canvas) self.canvas.blit(self.doorup, self.dooruprct) self.dooruptxt.draw(self.canvas) self.canvas.blit(self.doordown, self.doordownrct) self.doordowntxt.draw(self.canvas) self.canvas.blit(self.doorleft, self.doorleftrct) self.doorlefttxt.draw(self.canvas) self.canvas.blit(self.doorright, self.doorrightrct) self.doorrighttxt.draw(self.canvas) #seg types self.canvas.blit(self.segtypeleft, self.segtypeleftrct) self.segtypelefttxt.draw(self.canvas) self.segtypetxt.draw(self.canvas) self.canvas.blit(self.segtyperight, self.segtyperightrct) self.segtyperighttxt.draw(self.canvas) self.csegtypetxt.draw(self.canvas) #startpos if (ltype =='segment' and segtypes[self.segtype] == 'start') or ltype == 'level': self.canvas.blit(self.startpossurf, self.startposrct) self.startpostxt.draw(self.canvas) self.startpostxt2.draw(self.canvas) def export(self, gc, sc, printit): global ltype, doors, segtypes if ltype == 'level': self.dict = { 'lvl_number' : None, 'sky_color' : sc, 'ground_color' : gc, 'npcs' : [], 'items' : [], 'player_pos' : None, 'array' : None, 'name' : None, 'shade' : (False, (0,0,0,0), 0)} else: self.dict = { 'id' : None, 'npcs' : [], 'items' : [], 'array' : None, 'doors' : doors, 'type' : str(segtypes[self.segtype]), 'name' : None, 'shade' : (False, (0,0,0,0), 0)} if not printit or (self.exportrct.collidepoint(pygame.mouse.get_pos()) and pygame.mouse.get_pressed()[0] and not self.stop): for tile in currentMap.tiles: if tile.item: self.dict['items'].append((tile.map_pos, tile.item_id)) if tile.npc: self.dict['npcs'].append((tile.map_pos, tile.npc_face, tile.npc_id)) if tile.player_pos: self.dict['player_pos'] = tile.player_pos self.dict['array'] = currentMap.array if printit: self.exit = True self.stop = True elif not pygame.mouse.get_pressed()[0]: self.stop = False def change_id(self): global current_id, mode if self.nexttilerct.collidepoint(pygame.mouse.get_pos()) and pygame.mouse.get_pressed()[0] and not self.stop: if current_id < len(TEXTURES.all_textures)-1: current_id += 1 self.stop = True mode = 'tile' elif self.prevtilerct.collidepoint(pygame.mouse.get_pos()) and pygame.mouse.get_pressed()[0] and not self.stop: if current_id > 1: current_id -= 1 self.stop = True mode = 'tile' elif self.tilerect.collidepoint(pygame.mouse.get_pos()) and pygame.mouse.get_pressed()[0]: mode = 'tile' elif self.startposrct.collidepoint(pygame.mouse.get_pos()) and pygame.mouse.get_pressed()[0]: mode = 'start pos' elif not pygame.mouse.get_pressed()[0]: self.stop = False self.tiletypetext.update_string(SETTINGS.texture_type[current_id]) def change_item(self): global current_item, mode if self.nextitemrct.collidepoint(pygame.mouse.get_pos()) and pygame.mouse.get_pressed()[0] and not self.stop: if current_item < len(SETTINGS.item_types)-1: current_item += 1 self.stop = True mode = 'item' elif self.previtemrct.collidepoint(pygame.mouse.get_pos()) and pygame.mouse.get_pressed()[0] and not self.stop: if current_item > 0: current_item -= 1 self.stop = True mode = 'item' elif self.itemrect.collidepoint(pygame.mouse.get_pos()) and pygame.mouse.get_pressed()[0]: mode = 'item' elif not pygame.mouse.get_pressed()[0]: self.stop = False self.itemtypetext.update_string(self.item_names[current_item]) def change_npc(self): global current_npc, mode if self.nextnpcrct.collidepoint(pygame.mouse.get_pos()) and pygame.mouse.get_pressed()[0] and not self.stop: if current_npc < len(self.npc_stats)-1: current_npc += 1 self.stop = True mode = 'npc' elif self.prevnpcrct.collidepoint(pygame.mouse.get_pos()) and pygame.mouse.get_pressed()[0] and not self.stop: if current_npc > 0: current_npc -= 1 self.stop = True mode = 'npc' elif self.npcrect.collidepoint(pygame.mouse.get_pos()) and pygame.mouse.get_pressed()[0]: mode = 'npc' elif not pygame.mouse.get_pressed()[0]: self.stop = False self.npctypetext.update_string(self.npc_stats[current_npc]['name']) def change_face(self): global npc_face, mode if self.nextnpcfrct.collidepoint(pygame.mouse.get_pos()) and pygame.mouse.get_pressed()[0] and not self.stop: if npc_face < 270: npc_face += 90 self.stop = True mode = 'npc' elif self.prevnpcfrct.collidepoint(pygame.mouse.get_pos()) and pygame.mouse.get_pressed()[0] and not self.stop: if npc_face > 0: npc_face -= 90 self.stop = True mode = 'npc' elif self.npcfrect.collidepoint(pygame.mouse.get_pos()) and pygame.mouse.get_pressed()[0]: mode = 'npc' elif not pygame.mouse.get_pressed()[0]: self.stop = False self.npcfacetext.update_string(str(npc_face)) def click_doors(self): global doors #up if self.dooruprct.collidepoint(pygame.mouse.get_pos()) and pygame.mouse.get_pressed()[0] and not self.stop: if 90 not in doors: doors.append(90) self.doorup.fill(SETTINGS.RED) else: doors.remove(90) self.doorup.fill(SETTINGS.WHITE) self.stop = True elif self.doordownrct.collidepoint(pygame.mouse.get_pos()) and pygame.mouse.get_pressed()[0] and not self.stop: if 270 not in doors: doors.append(270) self.doordown.fill(SETTINGS.RED) else: doors.remove(270) self.doordown.fill(SETTINGS.WHITE) self.stop = True elif self.doorleftrct.collidepoint(pygame.mouse.get_pos()) and pygame.mouse.get_pressed()[0] and not self.stop: if 180 not in doors: doors.append(180) self.doorleft.fill(SETTINGS.RED) else: doors.remove(180) self.doorleft.fill(SETTINGS.WHITE) self.stop = True elif self.doorrightrct.collidepoint(pygame.mouse.get_pos()) and pygame.mouse.get_pressed()[0] and not self.stop: if 360 not in doors: doors.append(360) self.doorright.fill(SETTINGS.RED) else: doors.remove(360) self.doorright.fill(SETTINGS.WHITE) self.stop = True elif not pygame.mouse.get_pressed()[0]: self.stop = False def change_segtype(self): global segtypes if self.segtypeleftrct.collidepoint(pygame.mouse.get_pos()) and pygame.mouse.get_pressed()[0] and not self.stop: if self.segtype > 0: self.segtype -= 1 self.csegtypetxt.update_string(segtypes[self.segtype]) self.stop = True elif self. segtyperightrct.collidepoint(pygame.mouse.get_pos()) and pygame.mouse.get_pressed()[0] and not self.stop: if self.segtype < len(segtypes)-1: self.segtype += 1 self.csegtypetxt.update_string(segtypes[self.segtype]) self.stop = True elif not pygame.mouse.get_pressed()[0]: self.stop = False class Map: def __init__(self, width, height): self.width = width self.height = height self.window = pygame.Surface((width*32, height*32)) self.player_pos_set = False self.renderitems = [] self.tiles = [] self.array = [] #Create empty array for i in range(self.height): self.array.append([0] * self.width) #Add tiles to array for row in range(len(self.array)): for column in range(len(self.array[row])): self.tiles.append(Tile(self.array[row][column], (column, row))) def draw(self, canvas): self.window.fill((255,255,255)) canvas.blit(self.window, (0,0)) for tile in self.tiles: if tile.ID != 0: canvas.blit(tile.texture, tile.rect) if tile.item: canvas.blit(tile.item, tile.rect) if tile.npc: canvas.blit(tile.npc, tile.rect) if tile.player_pos: pygame.draw.rect(canvas, SETTINGS.DARKRED, tile.rect) def add_tile(self): global current_id, mode if pygame.mouse.get_pressed()[0] and mode == 'tile': x = 0 for tile in self.tiles: if tile.rect.collidepoint(pygame.mouse.get_pos()): tile = Tile(current_id, tile.map_pos) self.tiles[x] = tile self.array[tile.map_pos[1]][tile.map_pos[0]] = tile.ID x += 1 def remove_tile(self): if pygame.mouse.get_pressed()[2]: x = 0 for tile in self.tiles: if tile.rect.collidepoint(pygame.mouse.get_pos()): if tile.player_pos: self.player_pos_set = False tile = Tile(0, tile.map_pos) self.tiles[x] = tile self.array[tile.map_pos[1]][tile.map_pos[0]] = 0 x += 1 def add_item(self, item, item_id): global mode if pygame.mouse.get_pressed()[0] and mode == 'item': for tile in self.tiles: if tile.rect.collidepoint(pygame.mouse.get_pos()): tile.item = pygame.transform.scale(item, (32,32)) tile.item_id = item_id def add_npc(self, npc, npc_id): global mode, npc_face if pygame.mouse.get_pressed()[0] and mode == 'npc': for tile in self.tiles: if tile.rect.collidepoint(pygame.mouse.get_pos()) and not SETTINGS.tile_solid[tile.ID]: tile.npc = pygame.transform.scale(npc, (32,32)) tile.npc = pygame.transform.rotate(tile.npc, (npc_face)-90) tile.npc_id = npc_id tile.npc_face = npc_face def add_start(self): global mode if pygame.mouse.get_pressed()[0] and mode == 'start pos': for tile in self.tiles: if tile.rect.collidepoint(pygame.mouse.get_pos()) and not SETTINGS.tile_solid[tile.ID] and not self.player_pos_set: tile.player_pos = list(tile.map_pos) self.player_pos_set = True class Tile: def __init__(self, ID, pos): self.ID = ID self.pos = (pos[0]*32, pos[1]*32) self.map_pos = pos self.texture = pygame.image.load(TEXTURES.all_textures[ID]).convert_alpha() if SETTINGS.texture_type[ID] == 'vdoor': self.texture = pygame.transform.rotate(self.texture, 90) self.texture = pygame.transform.scale(self.texture, (32, 32)) self.rect = self.texture.get_rect() self.rect.topleft = self.pos self.item = None self.item_id = None self.npc = None self.npc_id = None self.npc_face = None self.player_pos = None class SaveLoad: def __init__(self): #open customLevels.dat with open(os.path.join('data', 'customLevels.dat'), 'rb') as file: try: self.levels = pickle.load(file) except: self.levels = [] #open customSegments.dat with open(os.path.join('data', 'customSegments.dat'), 'rb') as file: try: self.segments = pickle.load(file) except: self.segments = [] def print_slots(self, mtype): array = None if mtype == 'level': array = self.levels elif mtype == 'segment': array = self.segments if array: for i in array: print("[%s] - %s" % (array.index(i), i['name'])) else: print("No occupied slots") def save_map(self, gc, sc): global ltype editorCanvas.export(gc, sc, False) print() self.done = False print("In which slot do you want to save?") self.print_slots(ltype) while not self.done: print("Leave blank to add a new slot.") location = input("> ") if not location: done = True break else: try: location = int(location) self.done = True break except: print("Invalid argument.") print("Do you want to name your map?") name = input("> ") if name: editorCanvas.dict['name'] = name else: name = '' print("Who made this map?") author = input('> ') if author: editorCanvas.dict['author'] = author if ltype == 'level': if (location and location < len(self.levels)) or location == 0: editorCanvas.dict['lvl_number'] = location self.levels[location] = editorCanvas.dict elif not location: editorCanvas.dict['lvl_number'] = len(self.levels) self.levels.append(editorCanvas.dict) else: print("Invalid save location!") elif ltype == 'segment': if (location and location < len(self.segments)) or location == 0: editorCanvas.dict['id'] = location self.segments[location] = editorCanvas.dict elif not location: editorCanvas.dict['id'] = len(self.segments) self.segments.append(editorCanvas.dict) else: print("Invalid save location!") with open(os.path.join('data', 'customLevels.dat'), 'wb') as file: pickle.dump(self.levels, file) with open(os.path.join('data', 'customSegments.dat'), 'wb') as file2: pickle.dump(self.segments, file2) self.__init__() def load_map(self): global ltype self.done = False while not self.done: print() print("What type of map would you like to load?") mtype = input("level / segment > ").lower() if mtype == 'level' or mtype == 'l': mtype == 'l' self.done = True elif mtype == 'segment' or mtype == 's': mtype == 's' self.done = True else: print("Invalid argument!") if mtype == 's' and self.segments or mtype == 'l' and self.levels: self.done = False while not self.done: print() print("What slot would you like to load?") print("type \"d [slot]\" to delte a slot") if mtype == 's': self.print_slots('segment') else: self.print_slots('level') slot = input("Slot > ") if slot[0:2] == 'd ': self.del_map(slot[2], mtype) else: try: slot = int(slot) if mtype == 's' and slot < len(self.segments): self.done = True elif mtype == 'l' and slot < len(self.levels): self.done = True else: print("Number is too high!") except: print("Invalid argument!") else: print("There are no saved maps of this kind.") determine_mode() if mtype == 'l': loadedmap = self.levels[slot] ltype = 'level' else: loadedmap = self.segments[slot] ltype = 'segment' #transfer to editorCanvas width = len(loadedmap['array'][0]) height = len(loadedmap['array']) editorCanvas.__init__(int(width)*32 + 170, int(height)*32 + 170) currentMap.__init__(int(width), int(height)) #add tiles currentMap.array = loadedmap['array'] currentMap.tiles = [] y = 0 for row in loadedmap['array']: x = 0 for col in row: currentMap.tiles.append(Tile(col, (x, y))) x += 1 y += 1 #add items if loadedmap['items']: editorCanvas.load_items() for item in loadedmap['items']: tile = [x for x in currentMap.tiles if tuple(x.map_pos) == item[0]][0] tile.item = pygame.transform.scale(editorCanvas.items[item[1]], (32,32)) tile.item_id = item[1] #add NPCs if loadedmap['npcs']: editorCanvas.load_npcs() for npc in loadedmap['npcs']: tile = [x for x in currentMap.tiles if tuple(x.map_pos) == npc[0]][0] tile.npc = pygame.transform.scale(editorCanvas.npc_textures[npc[2]], (32,32)) tile.npc = pygame.transform.rotate(tile.npc, npc[1]-90) tile.npc_id = npc[2] tile.npc_face = npc[1] #add start pos try: if loadedmap['player_pos']: start_tile = [x for x in currentMap.tiles if list(x.map_pos) == loadedmap['player_pos']][0] start_tile.player_pos = loadedmap['player_pos'] currentMap.player_pos_set = True except: pass #show author try: if loadedmap['author']: editorCanvas.showauthor = True editorCanvas.authortext.update_string('Author: %s' % loadedmap['author']) except: pass def del_map(self, index, mtype): try: index = int(index) except: return if mtype == 'l': array = self.levels else: array = self.segments print() print("Are you sure you want to delete this map?") print(array[index]['name']) yn = input("Y/N > ").lower() if yn == 'y' or yn == 'yes': if mtype == 'l': del self.levels[index] else: del self.segments[index] elif yn == 'n' or yn == 'no': return else: self.del_map(index, mtype) with open(os.path.join('data', 'customLevels.dat'), 'wb') as file: pickle.dump(self.levels, file) with open(os.path.join('data', 'customSegments.dat'), 'wb') as file2: pickle.dump(self.segments, file2) def main_loop(): global ltype editor_exit = False clock = pygame.time.Clock() while not editor_exit: for event in pygame.event.get(): if event.type == pygame.QUIT or editorCanvas.exit: editor_exit = True pygame.quit() break if not editor_exit: editorCanvas.draw() editorCanvas.export((0,0,0), (0,0,0,), True) editorCanvas.change_id() editorCanvas.change_item() editorCanvas.change_npc() editorCanvas.change_face() if ltype == 'segment': editorCanvas.click_doors() editorCanvas.change_segtype() currentMap.draw(editorCanvas.canvas) currentMap.add_tile() currentMap.add_start() currentMap.remove_tile() pygame.display.update() editor_exit = False def new(): main_loop() def start(): main_loop() if __name__ == '__main__': editorCanvas = Canvas(0,0) currentMap = Map(1, 1) loader = SaveLoad() determine_mode() main_loop() what_now() sys.exit(1) '''How to create a level or segment: Levels are entire hand crafted maps for the game and segments are bits of map, which will be used in level generation. 1) Choose in command prompt if you want to create a level or segment and determine its size. Segments will be square and must all be the same size. (default = 9x9) 2) When the window is ready, draw your map. Tiles, NPC's and items can be changed in the right. 3) If you create a segment, remember to specify what type of segment you make. A start, normal or end segment. Start must have start pos for the player and end must have an exit. Also remember to specify where the doors to other segments are. 4) When everything is done, hit export and add to LEVELS.py - This last step should be temporary and is made for developers. Note: There are two kinds of doors: horizontal and vertical (hdoor, vdoor) Place them respectively. NPC's have a direction they are facing when spawning. Specify that under NPC area.''' ================================================ FILE: MAIN.py ================================================ #This is the MAIN script of DUGA. This is where the main loop is located and this is where all resources are loaded. #All the classes will be located at the bottom of this script. import pygame import math import os import pickle import logging import sys #-- Engine imports-- import SETTINGS import PLAYER import TEXTURES import MAP import RAYCAST import SPRITES import NPC import LEVELS import GUNS import PATHFINDING import TEXT #-- Game imports -- import EFFECTS import HUD import ITEMS import INVENTORY import ENTITIES import SEGMENTS import GENERATION import MENU import MUSIC import TUTORIAL pygame.init() pygame.font.init() pygame.display.set_mode((1,1)) #Load resources class Load: def load_resources(self): ID = 0 current_texture = 0 self.timer = 0 for texture in TEXTURES.all_textures: if SETTINGS.texture_type[ID] == 'sprite': SETTINGS.texture_list.append(pygame.image.load(texture)) else: SETTINGS.texture_list.append(Texture(texture, ID)) ID += 1 #Update the dictionary in SETTINGS for texture in SETTINGS.texture_list: SETTINGS.tile_texture.update({current_texture : texture}) current_texture += 1 #Mixer goes under here as well pygame.mixer.init() #Load custom settings with open(os.path.join('data', 'settings.dat'), 'rb') as settings_file: settings = pickle.load(settings_file) SETTINGS.fov = settings['fov'] SETTINGS.sensitivity = settings['sensitivity'] SETTINGS.volume = settings['volume'] SETTINGS.music_volume = settings['music volume'] SETTINGS.resolution = settings['graphics'][0] SETTINGS.render = settings['graphics'][1] SETTINGS.fullscreen = settings['fullscreen'] #Load statistics with open(os.path.join('data', 'statistics.dat'), 'rb') as stats_file: stats = pickle.load(stats_file) SETTINGS.statistics = stats def get_canvas_size(self): SETTINGS.canvas_map_width = len(SETTINGS.levels_list[SETTINGS.current_level].array[0])*SETTINGS.tile_size SETTINGS.canvas_map_height = len(SETTINGS.levels_list[SETTINGS.current_level].array)*SETTINGS.tile_size SETTINGS.canvas_actual_width = int(SETTINGS.canvas_target_width / SETTINGS.resolution) * SETTINGS.resolution SETTINGS.player_map_pos = SETTINGS.levels_list[SETTINGS.current_level].player_pos SETTINGS.player_pos[0] = int((SETTINGS.levels_list[SETTINGS.current_level].player_pos[0] * SETTINGS.tile_size) + SETTINGS.tile_size/2) SETTINGS.player_pos[1] = int((SETTINGS.levels_list[SETTINGS.current_level].player_pos[1] * SETTINGS.tile_size) + SETTINGS.tile_size/2) if len(SETTINGS.gun_list) != 0: for gun in SETTINGS.gun_list: gun.re_init() def load_entities(self): ENTITIES.load_guns() ENTITIES.load_npc_types() ENTITIES.load_item_types() def load_custom_levels(self): if not os.stat(os.path.join('data', 'customLevels.dat')).st_size == 0: with open(os.path.join('data', 'customLevels.dat'), 'rb') as file: custom_levels = pickle.load(file) for level in custom_levels: SETTINGS.clevels_list.append(LEVELS.Level(level)) with open(os.path.join('data', 'tutorialLevels.dat'), 'rb') as file: tutorial_levels = pickle.load(file) for level in tutorial_levels: SETTINGS.tlevels_list.append(LEVELS.Level(level)) def load_new_level(self): #Remove old level info SETTINGS.npc_list = [] SETTINGS.all_items = [] SETTINGS.walkable_area = [] SETTINGS.all_tiles = [] SETTINGS.all_doors = [] SETTINGS.all_solid_tiles = [] SETTINGS.all_sprites = [] #Retrieve new level info self.get_canvas_size() gameMap.__init__(SETTINGS.levels_list[SETTINGS.current_level].array) SETTINGS.player_rect.center = (SETTINGS.levels_list[SETTINGS.current_level].player_pos[0]*SETTINGS.tile_size, SETTINGS.levels_list[SETTINGS.current_level].player_pos[1]*SETTINGS.tile_size) SETTINGS.player_rect.centerx += SETTINGS.tile_size/2 SETTINGS.player_rect.centery += SETTINGS.tile_size/2 gamePlayer.real_x = SETTINGS.player_rect.centerx gamePlayer.real_y = SETTINGS.player_rect.centery if SETTINGS.shade and SETTINGS.levels_list[SETTINGS.current_level].shade: SETTINGS.shade_rgba = SETTINGS.levels_list[SETTINGS.current_level].shade_rgba SETTINGS.shade_visibility = SETTINGS.levels_list[SETTINGS.current_level].shade_visibility if SETTINGS.current_level > 0: SETTINGS.changing_level = False SETTINGS.player_states['fade'] = True else: SETTINGS.player_states['fade'] = True SETTINGS.player_states['black'] = True SETTINGS.player_states['title'] = True SETTINGS.walkable_area = list(PATHFINDING.pathfind(SETTINGS.player_map_pos, SETTINGS.all_tiles[-1].map_pos)) gameMap.move_inaccessible_entities() ENTITIES.spawn_npcs() ENTITIES.spawn_items() #Texturing class Texture: def __init__(self, file_path, ID): self.slices = [] self.texture = pygame.image.load(file_path).convert() self.rect = self.texture.get_rect() self.ID = ID self.create_slices() def create_slices(self): # Fills list - Nothing else row = 0 for row in range(self.rect.width): self.slices.append(row) row += 1 #Canvas class Canvas: '''== Create game canvas ==\nwidth -> px\nheight -> px''' def __init__(self, width, height): self.width = width self.height = height self.res_width = 0 if SETTINGS.mode == 1: self.width = int(SETTINGS.canvas_target_width / SETTINGS.resolution) * SETTINGS.resolution self.height = SETTINGS.canvas_target_height self.res_width = SETTINGS.canvas_actual_width if SETTINGS.fullscreen: self.window = pygame.display.set_mode((self.width, int(self.height+(self.height*0.15))), pygame.HWSURFACE + pygame.SCALED + pygame.NOFRAME + pygame.FULLSCREEN, 32, vsync=1) else: self.window = pygame.display.set_mode((self.width, int(self.height+(self.height*0.15)))) self.canvas = pygame.Surface((self.width, self.height)) pygame.display.set_caption("DUGA") self.shade = [pygame.Surface((self.width, self.height)).convert_alpha(), pygame.Surface((self.width, self.height/1.2)).convert_alpha(), pygame.Surface((self.width, self.height/2)).convert_alpha(), pygame.Surface((self.width, self.height/4)).convert_alpha(), pygame.Surface((self.width, self.height/8)).convert_alpha(), pygame.Surface((self.width, self.height/18)).convert_alpha()] self.rgba = [SETTINGS.shade_rgba[0], SETTINGS.shade_rgba[1], SETTINGS.shade_rgba[2], int(min(255, SETTINGS.shade_rgba[3]*(50/SETTINGS.shade_visibility)))] def change_mode(self): if SETTINGS.mode == 1: #1 - 3D / 0 - 2D SETTINGS.mode = 0 self.__init__(SETTINGS.canvas_actual_width, SETTINGS.canvas_target_height) else: SETTINGS.mode = 1 self.__init__(self.res_width, SETTINGS.canvas_target_height) SETTINGS.switch_mode = False def draw(self): if SETTINGS.mode == 1: self.canvas.fill(SETTINGS.levels_list[SETTINGS.current_level].sky_color) self.window.fill(SETTINGS.BLACK) pygame.draw.rect(self.canvas, SETTINGS.levels_list[SETTINGS.current_level].ground_color, (0, self.height/2, self.width, self.height/2)) if SETTINGS.shade: for i in range(len(self.shade)): if i != 5: self.shade[i].fill((self.rgba[0], self.rgba[1], self.rgba[2], self.rgba[3])) else: self.shade[i].fill((self.rgba[0], self.rgba[1], self.rgba[2], SETTINGS.shade_rgba[3])) self.canvas.blit(self.shade[i], (0, self.height/2 - self.shade[i].get_height()/2)) else: self.window.fill(SETTINGS.WHITE) def sort_distance(x): if x == None: return 0 else: return x.distance def sort_atan(x): if SETTINGS.middle_ray_pos: pos = SETTINGS.middle_ray_pos else: pos = SETTINGS.player_rect.center #find the position on each tile that is closest to middle_ray_pos xpos = max(x.rect.left, min(pos[0], x.rect.right)) - SETTINGS.player_rect.centerx ypos = SETTINGS.player_rect.centery - max(x.rect.top, min(pos[1], x.rect.bottom)) theta = math.atan2(ypos, xpos) theta = math.degrees(theta) theta -= SETTINGS.player_angle if theta < 0: theta += 360 if theta > 180: theta -= 360 if x.type == 'end': SETTINGS.end_angle = theta theta = abs(theta) return(theta) def render_screen(canvas): '''render_screen(canvas) -> Renders everything but NPC\'s''' SETTINGS.rendered_tiles = [] #Get sprite positions for sprite in SETTINGS.all_sprites: sprite.get_pos(canvas) #Sort zbuffer and solid tiles SETTINGS.zbuffer = sorted(SETTINGS.zbuffer, key=sort_distance, reverse=True) SETTINGS.all_solid_tiles = sorted(SETTINGS.all_solid_tiles, key=lambda x: (x.type, sort_atan(x), x.distance)) #Calculate which tiles are visible for tile in SETTINGS.all_solid_tiles: if tile.distance and SETTINGS.tile_visible[tile.ID]: if sort_atan(tile) <= SETTINGS.fov: if tile.distance < SETTINGS.render * SETTINGS.tile_size: SETTINGS.rendered_tiles.append(tile) elif tile.distance <= SETTINGS.tile_size * 1.5: SETTINGS.rendered_tiles.append(tile) #Render all items in zbuffer for item in SETTINGS.zbuffer: if item == None: pass elif item.type == 'slice': canvas.blit(item.tempslice, (item.xpos, item.rect.y)) if item.vh == 'v': #Make vertical walls slightly darker canvas.blit(item.darkslice, (item.xpos, item.rect.y)) if SETTINGS.shade: canvas.blit(item.shade_slice, (item.xpos, item.rect.y)) else: if item.new_rect.right > 0 and item.new_rect.x < SETTINGS.canvas_actual_width and item.distance < (SETTINGS.render * SETTINGS.tile_size): item.draw(canvas) #Draw weapon if it is there if SETTINGS.current_gun: SETTINGS.current_gun.draw(gameCanvas.canvas) elif SETTINGS.next_gun: SETTINGS.next_gun.draw(gameCanvas.canvas) #Draw Inventory and effects if SETTINGS.player_states['invopen']: gameInv.draw(gameCanvas.canvas) EFFECTS.render(gameCanvas.canvas) SETTINGS.zbuffer = [] #Draw HUD and canvas gameCanvas.window.blit(canvas, (SETTINGS.axes)) gameHUD.render(gameCanvas.window) #Draw tutorial strings if SETTINGS.levels_list == SETTINGS.tlevels_list: tutorialController.control(gameCanvas.window) def update_game(): if SETTINGS.npc_list: for npc in SETTINGS.npc_list: if not npc.dead: npc.think() SETTINGS.ground_weapon = None for item in SETTINGS.all_items: item.update() if (SETTINGS.changing_level and SETTINGS.player_states['black']) or SETTINGS.player_states['dead']: if SETTINGS.current_level < len(SETTINGS.levels_list)-1 and SETTINGS.changing_level: SETTINGS.current_level += 1 SETTINGS.statistics['last levels'] += 1 gameLoad.load_new_level() elif (SETTINGS.current_level == len(SETTINGS.levels_list)-1 or SETTINGS.player_states['dead']) and gameLoad.timer < 4 and not SETTINGS.player_states['fade']: if not SETTINGS.player_states['dead'] and SETTINGS.current_level == len(SETTINGS.levels_list)-1 and text.string != 'YOU WON': text.update_string('YOU WON') elif SETTINGS.player_states['dead'] and text.string != 'GAME OVER': text.update_string('GAME OVER') text.draw(gameCanvas.window) if not SETTINGS.game_won: gameLoad.timer = 0 SETTINGS.game_won = True gameLoad.timer += SETTINGS.dt #Reset for future playthroughs elif SETTINGS.game_won and gameLoad.timer >= 4: gameLoad.timer = 0 SETTINGS.game_won = False menuController.current_type = 'main' menuController.current_menu = 'score' calculate_statistics() SETTINGS.menu_showing = True SETTINGS.current_level = 0 def calculate_statistics(): #Update 'all' stats SETTINGS.statistics['all enemies'] += SETTINGS.statistics['last enemies'] SETTINGS.statistics['all ddealt'] += SETTINGS.statistics['last ddealt'] SETTINGS.statistics['all dtaken'] += SETTINGS.statistics['last dtaken'] SETTINGS.statistics['all shots'] += SETTINGS.statistics['last shots'] SETTINGS.statistics['all levels'] += SETTINGS.statistics['last levels'] #Update 'best' stats if SETTINGS.statistics['best enemies'] < SETTINGS.statistics['last enemies']: SETTINGS.statistics['best enemies'] = SETTINGS.statistics['last enemies'] if SETTINGS.statistics['best ddealt'] < SETTINGS.statistics['last ddealt']: SETTINGS.statistics['best ddealt'] = SETTINGS.statistics['last ddealt'] if SETTINGS.statistics['best dtaken'] < SETTINGS.statistics['last dtaken']: SETTINGS.statistics['best dtaken'] = SETTINGS.statistics['last dtaken'] if SETTINGS.statistics['best shots'] < SETTINGS.statistics['last shots']: SETTINGS.statistics['best shots'] = SETTINGS.statistics['last shots'] if SETTINGS.statistics['best levels'] < SETTINGS.statistics['last levels']: SETTINGS.statistics['best levels'] = SETTINGS.statistics['last levels'] #'last' statistics will be cleared when starting new game in menu. with open(os.path.join('data', 'statistics.dat'), 'wb') as saved_stats: pickle.dump(SETTINGS.statistics, saved_stats) #Main loop def main_loop(): game_exit = False clock = pygame.time.Clock() logging.basicConfig(filename = os.path.join('data', 'CrashReport.log'), level=logging.WARNING) ## allfps = [] while not game_exit: SETTINGS.zbuffer = [] if SETTINGS.play_seconds >= 60: SETTINGS.statistics['playtime'] += 1 SETTINGS.play_seconds = 0 else: SETTINGS.play_seconds += SETTINGS.dt ## allfps.append(clock.get_fps()) for event in pygame.event.get(): if event.type == pygame.QUIT or SETTINGS.quit_game: game_exit = True ## b = 0 ## for x in allfps: ## b += x ## print(b/len(allfps)) menuController.save_settings() calculate_statistics() pygame.quit() sys.exit(0) try: #Music musicController.control_music() if SETTINGS.menu_showing and menuController.current_type == 'main': gameCanvas.window.fill(SETTINGS.WHITE) menuController.control() #Load custom maps if SETTINGS.playing_customs: SETTINGS.levels_list = SETTINGS.clevels_list gameLoad.get_canvas_size() gameLoad.load_new_level() #Load generated maps elif SETTINGS.playing_new: mapGenerator.__init__() mapGenerator.generate_levels(SETTINGS.glevels_amount, SETTINGS.glevels_size) SETTINGS.levels_list = SETTINGS.glevels_list gameLoad.get_canvas_size() gameLoad.load_new_level() #Or.. If they are playing the tutorial elif SETTINGS.playing_tutorial: SETTINGS.levels_list = SETTINGS.tlevels_list gameLoad.get_canvas_size() gameLoad.load_new_level() elif SETTINGS.menu_showing and menuController.current_type == 'game': menuController.control() else: #Update logic gamePlayer.control(gameCanvas.canvas) if SETTINGS.fov >= 100: SETTINGS.fov = 100 elif SETTINGS.fov <= 10: SETTINGS.fov = 10 if SETTINGS.switch_mode: gameCanvas.change_mode() #Render - Draw gameRaycast.calculate() gameCanvas.draw() if SETTINGS.mode == 1: render_screen(gameCanvas.canvas) #BETA # beta.draw(gameCanvas.window) elif SETTINGS.mode == 0: gameMap.draw(gameCanvas.window) gamePlayer.draw(gameCanvas.window) for x in SETTINGS.raylines: pygame.draw.line(gameCanvas.window, SETTINGS.RED, (x[0][0]/4, x[0][1]/4), (x[1][0]/4, x[1][1]/4)) SETTINGS.raylines = [] for i in SETTINGS.npc_list: if i.rect and i.dist <= SETTINGS.render * SETTINGS.tile_size * 1.2: pygame.draw.rect(gameCanvas.window, SETTINGS.RED, (i.rect[0]/4, i.rect[1]/4, i.rect[2]/4, i.rect[3]/4)) elif i.rect: pygame.draw.rect(gameCanvas.window, SETTINGS.DARKGREEN, (i.rect[0]/4, i.rect[1]/4, i.rect[2]/4, i.rect[3]/4)) update_game() except Exception as e: menuController.save_settings() calculate_statistics() logging.warning("DUGA has crashed. Please send this report to MaxwellSalmon, so he can fix it.") logging.exception("Error message: ") pygame.quit() sys.exit(0) #Update Game pygame.display.update() delta_time = clock.tick(SETTINGS.fps) SETTINGS.dt = delta_time / 1000.0 SETTINGS.cfps = int(clock.get_fps()) #pygame.display.set_caption(SETTINGS.caption % SETTINGS.cfps) # allfps.append(clock.get_fps()) #Probably temporary object init #SETTINGS.current_level = 5 #temporary if __name__ == '__main__': gameLoad = Load() gameLoad.load_resources() gameLoad.load_entities() gameLoad.load_custom_levels() mapGenerator = GENERATION.Generator() mapGenerator.generate_levels(1,2) SETTINGS.levels_list = SETTINGS.glevels_list gameLoad.get_canvas_size() #Setup and classes text = TEXT.Text(0,0,"YOU WON", SETTINGS.WHITE, "DUGAFONT.ttf", 48) beta = TEXT.Text(5,5,"DUGA BETA BUILD V. 1.3", SETTINGS.WHITE, "DUGAFONT.ttf", 20) text.update_pos(SETTINGS.canvas_actual_width/2 - text.layout.get_width()/2, SETTINGS.canvas_target_height/2 - text.layout.get_height()/2) #Classes for later use gameMap = MAP.Map(SETTINGS.levels_list[SETTINGS.current_level].array) gameCanvas = Canvas(SETTINGS.canvas_map_width, SETTINGS.canvas_map_height) gamePlayer = PLAYER.Player(SETTINGS.player_pos) gameRaycast = RAYCAST.Raycast(gameCanvas.canvas, gameCanvas.window) gameInv = INVENTORY.inventory({'bullet': 150, 'shell':25, 'ferromag' : 50}) gameHUD = HUD.hud() #More loading - Level specific gameLoad.load_new_level() #Controller classes menuController = MENU.Controller(gameCanvas.window) musicController = MUSIC.Music() tutorialController = TUTORIAL.Controller() #Run at last main_loop() ================================================ FILE: MAIN.spec ================================================ # -*- mode: python -*- block_cipher = None a = Analysis(['MAIN.py'], pathex=['C:\\Python34\\Portfolio\\PyGame\\DUGA'], binaries=[], datas=[], hiddenimports=[], hookspath=[], runtime_hooks=[], excludes=[], win_no_prefer_redirects=False, win_private_assemblies=False, cipher=block_cipher) pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher) exe = EXE(pyz, a.scripts, a.binaries, a.zipfiles, a.datas, name='MAIN', debug=False, strip=False, upx=True, console=False , icon='icon.ico') ================================================ FILE: MAP.py ================================================ #This is the script that controls the game maps. It makes the array and the tiles. import SETTINGS import SPRITES import SOUND import pygame import math import random import os class Map: '''== Create the map ==\narray -> Level to be loaded''' def __init__(self, array): self.array = array self.tile_size = SETTINGS.tile_size self.width = len(self.array[0])-1 self.height = len(self.array)-1 SETTINGS.current_level_size = (self.width, self.height) for row in range(len(self.array)): for column in range(len(self.array[row])): SETTINGS.all_tiles.append(Tile(self.array[row][column], [column*self.tile_size, row*self.tile_size], [column, row])) for tile in SETTINGS.all_tiles: if SETTINGS.tile_solid[tile.ID]: SETTINGS.all_solid_tiles.append(tile) if tile.type == 'trigger': SETTINGS.trigger_tiles.append(tile) #Add a tile that is always outside the walkable area (air) SETTINGS.all_tiles.append(Tile(0, [column+1 * self.tile_size, row+1 * self.tile_size],[column+1, row+1])) def draw(self, canvas): for tile in SETTINGS.all_solid_tiles: if SETTINGS.tile_visible[tile.ID]: tile.draw(canvas) def move_inaccessible_entities(self): wa = [] for i in SETTINGS.walkable_area: if i.type != 'hdoor' and i.type != 'vdoor': wa.append(i.map_pos) move_items = [x for x in SETTINGS.levels_list[SETTINGS.current_level].items if list(x[0]) not in wa] move_npcs = [x for x in SETTINGS.levels_list[SETTINGS.current_level].npcs if list(x[0]) not in wa] item_positions = [x[0] for x in SETTINGS.levels_list[SETTINGS.current_level].items if list(x[0]) in wa] npc_positions = [x[0] for x in SETTINGS.levels_list[SETTINGS.current_level].npcs if list(x[0]) in wa] possible_item_positions = [x for x in wa if tuple(wa) not in item_positions] temp_possible_npc_positions = [x for x in wa if tuple(wa) not in npc_positions] possible_npc_positions = [] #Remove npc positions too close to the player for pos in temp_possible_npc_positions: x = abs(SETTINGS.player_map_pos[0] - pos[0]) y = abs(SETTINGS.player_map_pos[1] - pos[1]) if math.sqrt(x**2 + y**2) >= 8: #Length of vector between player and NPC possible_npc_positions.append(pos) for i in range(len(move_items)): #print("Moved item from ", move_items[i][0]) index = SETTINGS.levels_list[SETTINGS.current_level].items.index(move_items[i]) #Get item index SETTINGS.levels_list[SETTINGS.current_level].items[index] = ((random.choice(possible_item_positions)), move_items[i][1]) #Choose new location for item possible_item_positions.remove(list(SETTINGS.levels_list[SETTINGS.current_level].items[index][0])) #Remove possible location #print("to ", SETTINGS.levels_list[SETTINGS.current_level].items[index][0]) for i in range(len(move_npcs)): #print("Moved NPC from ", move_npcs[i][0]) index = SETTINGS.levels_list[SETTINGS.current_level].npcs.index(move_npcs[i]) SETTINGS.levels_list[SETTINGS.current_level].npcs[index] = ((random.choice(possible_npc_positions)), move_npcs[i][1], move_npcs[i][2]) possible_npc_positions.remove(list(SETTINGS.levels_list[SETTINGS.current_level].npcs[index][0])) #print("to ", SETTINGS.levels_list[SETTINGS.current_level].npcs[index][0]) #print("This level has %s items and %s NPC's" % (len(SETTINGS.levels_list[SETTINGS.current_level].items), len(SETTINGS.levels_list[SETTINGS.current_level].npcs))) class Tile: def __init__(self, ID, pos, map_pos): self.ID = ID #position in pixels self.pos = pos self.type = SETTINGS.texture_type[self.ID] #position in tiles self.map_pos = map_pos self.distance = None self.solid = SETTINGS.tile_solid[self.ID] #For doors opening self.state = None self.timer = 0 if self.type == 'sprite': current_number = len(SETTINGS.all_sprites) #Need some weird coordinates to make it centered. self.texture = SPRITES.Sprite(SETTINGS.tile_texture[self.ID], self.ID, (self.pos[0]+SETTINGS.tile_size/3, self.pos[1]+SETTINGS.tile_size/3), 'sprite') self.rect = pygame.Rect(pos[0], pos[1], SETTINGS.tile_size/2, SETTINGS.tile_size/2) else: self.texture = SETTINGS.tile_texture[self.ID].texture self.icon = pygame.transform.scale(self.texture, (16,16)).convert() self.texture = pygame.transform.scale(self.texture, (SETTINGS.tile_size, SETTINGS.tile_size)).convert() self.rect = self.texture.get_rect() self.rect.x = pos[0] self.rect.y = pos[1] if self.type == 'vdoor' or self.type == 'hdoor': self.open = 0 self.state = 'closed' #states: closed, opening, open, closing self.open_sound = pygame.mixer.Sound(os.path.join('sounds', 'other', 'door_open.ogg')) self.close_sound = pygame.mixer.Sound(os.path.join('sounds', 'other', 'door_close.ogg')) SETTINGS.all_doors.append(self) def draw(self, canvas): canvas.blit(self.icon, (self.rect.x/4, self.rect.y/4)) def get_dist(self, pos, *called): xpos = self.rect.center[0] - pos[0] ypos = pos[1] - self.rect.center[1] self.distance = math.sqrt(xpos*xpos + ypos*ypos) if (self.state and self.state != 'closed') and called != ('npc',): #lol self.sesam_luk_dig_op() return self.distance def sesam_luk_dig_op(self): if self.open > SETTINGS.tile_size: self.open = SETTINGS.tile_size elif self.open < 0: self.open = 0 if self.state == 'closed': self.state = 'opening' elif self.state == 'opening': if self.open == 0: SOUND.play_sound(self.open_sound, self.distance) if self.open < SETTINGS.tile_size: self.open += SETTINGS.tile_size * SETTINGS.dt else: self.state = 'open' self.solid = False if self.open > SETTINGS.tile_size/1.4: self.solid = False elif self.state == 'open': self.timer += SETTINGS.dt if self.timer > 5 and not self.rect.colliderect(SETTINGS.player_rect): for i in SETTINGS.npc_list: if self.rect.colliderect(i.rect): break else: self.state = 'closing' self.solid = True self.timer = 0 elif self.state == 'closing': if self.open >= SETTINGS.tile_size: SOUND.play_sound(self.close_sound, self.distance) if self.open > 0: self.open -= SETTINGS.tile_size * SETTINGS.dt else: self.state = 'closed' ================================================ FILE: MENU.py ================================================ import pygame import pickle import os import copy import random import SETTINGS import TEXT import SOUND SETTINGS.menu_showing = True class Controller: def __init__(self, canvas): self.current_menu = 'main' self.current_type = 'main' self.canvas = canvas self.shut_up = False self.load_settings() self.esc_pressed = False self.new_pressed = False self.mainMenu = MainMenu() self.newMenu = NewMenu(self.current_settings) self.optionsMenu = OptionsMenu(self.current_settings) self.creditsMenu = CreditsMenu() self.gMainMenu = GMainMenu() self.supportSplash = SupportSplash() self.scoreMenu = ScoreMenu() def load_settings(self): #This script does not change the settings themselves, but only the settings.dat with open(os.path.join('data', 'settings.dat'), 'rb') as file1: settings = pickle.load(file1) self.current_settings = settings #self.current_settings = {'fov': 60, 'fullscreen': False, 'sensitivity': 0.25, 'graphics': (140, 12), 'volume': 0.5, 'music volume' : 0, 'shut up' : False} self.shut_up = self.current_settings['shut up'] def save_settings(self): current_settings = self.optionsMenu.current_settings current_settings['shut up'] = self.shut_up with open(os.path.join('data', 'settings.dat'), 'wb') as file2: pickle.dump(current_settings, file2) def check_mouse(self): pygame.event.set_grab(False) pygame.mouse.set_visible(True) def control(self): self.check_mouse() if self.current_type == 'main': if self.current_menu == 'main': self.mainMenu.draw(self.canvas) if self.mainMenu.new_button.get_clicked(): self.current_menu = 'new' elif self.mainMenu.options_button.get_clicked(): self.current_menu = 'options' elif self.mainMenu.score_button.get_clicked(): self.current_menu = 'score' elif self.mainMenu.credits_button.get_clicked(): self.current_menu = 'credits' elif self.mainMenu.quit_button.get_clicked(): SETTINGS.quit_game = True #Splash screen if SETTINGS.statistics['playtime'] >= 120 and not self.shut_up: self.supportSplash.draw(self.canvas) if self.supportSplash.button.get_clicked(): self.shut_up = True self.save_settings() elif self.current_menu == 'new': self.newMenu.draw(self.canvas) if self.newMenu.back_button.get_clicked(): self.current_menu = 'main' #Play generated maps elif self.newMenu.new_button.get_clicked(): self.newMenu.reset_inventory() self.newMenu.loading.draw(self.canvas) self.new_pressed = True #Check if new levels have been loaded and loading string is showing elif self.new_pressed: SETTINGS.playing_new = True self.new_pressed = False elif SETTINGS.playing_new: self.current_type = 'game' self.current_menu = 'main' SETTINGS.current_level = 0 SETTINGS.menu_showing = False SETTINGS.playing_new = False #Play custom maps elif self.newMenu.custom_button.get_clicked(): if SETTINGS.clevels_list: self.newMenu.reset_inventory() SETTINGS.playing_customs = True else: self.newMenu.no_levels_on = True #Check if custom levels have been loaded elif SETTINGS.playing_customs: self.current_type = 'game' self.current_menu = 'main' SETTINGS.current_level = 0 SETTINGS.menu_showing = False SETTINGS.playing_customs = False #Play tutorial elif self.newMenu.tutorial_button.get_clicked(): self.newMenu.reset_inventory() SETTINGS.playing_tutorial = True elif SETTINGS.playing_tutorial: self.current_type = 'game' self.current_menu = 'main' SETTINGS.current_level = 0 SETTINGS.menu_showing = False SETTINGS.playing_tutorial = False elif self.current_menu == 'options': self.optionsMenu.draw(self.canvas) if self.optionsMenu.back_button.get_clicked(): self.current_menu = 'main' if self.optionsMenu.save: self.save_settings() self.optionsMenu.save = False elif self.current_menu == 'score': self.scoreMenu.draw(self.canvas) if self.scoreMenu.back_button.get_clicked(): self.current_menu = 'main' elif self.current_menu == 'credits': self.creditsMenu.draw(self.canvas, self.shut_up) if self.creditsMenu.back_button.get_clicked(): self.current_menu = 'main' #Show menu in game elif self.current_type == 'game': key = pygame.key.get_pressed() if self.current_menu == 'main': self.gMainMenu.draw(self.canvas) if self.gMainMenu.resume_button.get_clicked() or (self.esc_pressed and not key[pygame.K_ESCAPE]): SETTINGS.menu_showing = False self.esc_pressed = False elif self.gMainMenu.exit_button.get_clicked(): self.current_type = 'main' if key[pygame.K_ESCAPE]: self.esc_pressed = True class Menu: def __init__(self, title): self.title = TEXT.Text(0,0, title, SETTINGS.BLACK, "DUGAFONT.ttf", 120) self.title.update_pos((SETTINGS.canvas_actual_width/2)-(self.title.layout.get_width()/2)+8, 20) self.background_image = None class MainMenu(Menu): def __init__(self): Menu.__init__(self, '') self.new_button = Button((SETTINGS.canvas_actual_width/2, 200, 200, 60), "NEW GAME") self.options_button = Button((SETTINGS.canvas_actual_width/2, 270, 200, 60), "OPTIONS") self.score_button = Button((SETTINGS.canvas_actual_width/2, 340, 200, 60), "STATISTICS") self.credits_button = Button((SETTINGS.canvas_actual_width/2, 410, 200, 60), "CREDITS") self.quit_button = Button((SETTINGS.canvas_actual_width/2, 500, 200, 60), "QUIT") self.logo = pygame.image.load(os.path.join('graphics', 'logo_cutout.png')).convert_alpha() self.logo_rect = self.logo.get_rect() self.logo_surface = pygame.Surface(self.logo.get_size()).convert() self.logo_surface_rect = self.logo_surface.get_rect() self.logo_surface_rect.center = (SETTINGS.canvas_actual_width/2, 90) #(image, x-position) self.stone_tiles = [[pygame.image.load(os.path.join('graphics', 'tiles', 'walls', 'stone_wall.png')).convert(), self.logo_surface_rect.left], [pygame.image.load(os.path.join('graphics', 'tiles', 'walls', 'stone_wall_crack.png')).convert(), self.logo_surface_rect.left + 160], [pygame.image.load(os.path.join('graphics', 'tiles', 'walls', 'stone_wall.png')).convert(), self.logo_surface_rect.left + (2*160)], [pygame.image.load(os.path.join('graphics', 'tiles', 'walls', 'stone_wall.png')).convert(), self.logo_surface_rect.left + (3*160)], [pygame.image.load(os.path.join('graphics', 'tiles', 'walls', 'stone_vent.png')).convert(), self.logo_surface_rect.left + (4*160)], [pygame.image.load(os.path.join('graphics', 'tiles', 'walls', 'stone_wall.png')).convert(), self.logo_surface_rect.left + (5*160)], [pygame.image.load(os.path.join('graphics', 'tiles', 'walls', 'stone_vase.png')).convert(), self.logo_surface_rect.left + (6*160)], [pygame.image.load(os.path.join('graphics', 'tiles', 'walls', 'stone_wall.png')).convert(), self.logo_surface_rect.left + (7*160)], [pygame.image.load(os.path.join('graphics', 'tiles', 'walls', 'stone_wall.png')).convert(), self.logo_surface_rect.left + (8*160)]] self.baroque_tiles = [[pygame.image.load(os.path.join('graphics', 'tiles', 'walls', 'baroque.png')).convert(), self.logo_surface_rect.left], [pygame.image.load(os.path.join('graphics', 'tiles', 'walls', 'baroque.png')).convert(), self.logo_surface_rect.left + 160], [pygame.image.load(os.path.join('graphics', 'tiles', 'walls', 'baroque_lamps.png')).convert(), self.logo_surface_rect.left + (2*160)], [pygame.image.load(os.path.join('graphics', 'tiles', 'walls', 'baroque.png')).convert(), self.logo_surface_rect.left + (3*160)], [pygame.image.load(os.path.join('graphics', 'tiles', 'walls', 'baroque.png')).convert(), self.logo_surface_rect.left + (4*160)], [pygame.image.load(os.path.join('graphics', 'tiles', 'walls', 'baroque_worn.png')).convert(), self.logo_surface_rect.left + (5*160)], [pygame.image.load(os.path.join('graphics', 'tiles', 'walls', 'baroque.png')).convert(), self.logo_surface_rect.left + (6*160)]] self.wood_tiles = [[pygame.image.load(os.path.join('graphics', 'tiles', 'walls', 'wood_wall.png')).convert(), self.logo_surface_rect.left], [pygame.image.load(os.path.join('graphics', 'tiles', 'walls', 'wood_painting.png')).convert(), self.logo_surface_rect.left + 160], [pygame.image.load(os.path.join('graphics', 'tiles', 'walls', 'wood_wall.png')).convert(), self.logo_surface_rect.left + (2*160)], [pygame.image.load(os.path.join('graphics', 'tiles', 'walls', 'wood_wall.png')).convert(), self.logo_surface_rect.left + (3*160)], [pygame.image.load(os.path.join('graphics', 'tiles', 'walls', 'wood_books.png')).convert(), self.logo_surface_rect.left + (4*160)], [pygame.image.load(os.path.join('graphics', 'tiles', 'walls', 'wood_fireplace.png')).convert(), self.logo_surface_rect.left + (5*160)], [pygame.image.load(os.path.join('graphics', 'tiles', 'walls', 'wood_wall.png')).convert(), self.logo_surface_rect.left + (6*160)]] self.tiles = random.choice((self.stone_tiles, self.baroque_tiles, self.wood_tiles)) for i in range(len(self.tiles)): self.tiles[i][0] = pygame.transform.scale(self.tiles[i][0], (160,160)) #?? def draw(self, canvas): self.logo_animation(canvas) self.new_button.draw(canvas) self.options_button.draw(canvas) self.score_button.draw(canvas) self.credits_button.draw(canvas) self.quit_button.draw(canvas) def logo_animation(self, canvas): for tile in self.tiles: self.logo_surface.blit(tile[0], (tile[1], self.logo_rect.top)) tile[1] -= 1 if tile[1] < self.logo_surface_rect.left - 160: tile[1] += (160 * len(self.tiles)) self.logo_surface.blit(self.logo, (0,0)) canvas.blit(self.logo_surface, self.logo_surface_rect) class NewMenu(Menu): def __init__(self, settings): Menu.__init__(self, 'NEW GAME') self.new_button = Button((SETTINGS.canvas_actual_width/2, 200, 200, 60), "NEW GAME") self.custom_button = Button((SETTINGS.canvas_actual_width/2, 270, 200, 60), "CUSTOM MAPS") self.tutorial_button = Button((SETTINGS.canvas_actual_width/2, 325, 200, 30), "TUTORIAL") self.back_button = Button((SETTINGS.canvas_actual_width/2, 500, 200, 60), "BACK") self.loading = TEXT.Text(0,0, "LOADING...", SETTINGS.BLACK, "DUGAFONT.ttf", 74) self.loading.update_pos((SETTINGS.canvas_actual_width/2)-(self.loading.layout.get_width()/2)+8, (SETTINGS.canvas_target_height/2)-(self.loading.layout.get_height()/2)) self.nolevels = TEXT.Text(0,0, "NO CUSTOM LEVELS", SETTINGS.RED, "DUGAFONT.ttf", 50) self.nolevels.update_pos((SETTINGS.canvas_actual_width/2)-(self.nolevels.layout.get_width()/2)+8, (SETTINGS.canvas_target_height/2)-(self.nolevels.layout.get_height()/2)) self.timer = 0 self.no_levels_on = False self.settings = settings def draw(self, canvas): self.new_button.draw(canvas) self.custom_button.draw(canvas) self.tutorial_button.draw(canvas) self.back_button.draw(canvas) self.title.draw(canvas) if self.no_levels_on: self.draw_no_levels(canvas) else: self.timer = 0 def reset_inventory(self): for i in SETTINGS.inventory: SETTINGS.inventory[i] = None for i in SETTINGS.held_ammo: SETTINGS.held_ammo[i] = 0 for i in SETTINGS.gun_list: i.current_mag = 0 SETTINGS.current_gun = None SETTINGS.next_gun = None SETTINGS.player_health = SETTINGS.og_player_health SETTINGS.player_armor = SETTINGS.og_player_armor SETTINGS.current_level = 0 SETTINGS.player_states['dead'] = False SETTINGS.player_states['invopen'] = False SETTINGS.player_states['heal'] = False SETTINGS.player_states['armor'] = False SETTINGS.player_states['cspeed'] = 0 SETTINGS.statistics['last enemies'] = 0 SETTINGS.statistics['last dtaken'] = 0 SETTINGS.statistics['last ddealt'] = 0 SETTINGS.statistics['last shots'] = 0 SETTINGS.statistics['last levels'] = 0 SETTINGS.fov = self.settings['fov'] SETTINGS.player_states['cspeed'] = SETTINGS.player_speed SETTINGS.aiming = False SETTINGS.player.update_collide_list = True def draw_no_levels(self, canvas): if self.timer <= 1.2: self.nolevels.draw(canvas) else: self.no_levels_on = False self.timer += SETTINGS.dt class OptionsMenu(Menu): def __init__(self, settings): Menu.__init__(self, 'OPTIONS') self.save = False self.strings = ['LOW', 'MED', 'HIGH'] self.music_strings = ['OFF', 'MED', 'HIGH'] self.degrees = ['50', '60', '70'] self.onoff = ['ON', 'OFF'] self.strings_to_data = { #'graphics' : [(resolution, render), (), ()] 'graphics' : [(100, 10), (140, 12), (175, 14)], 'fov' : [50, 60, 70], 'sensitivity' : [0.15, 0.25, 0.35], #Tjek den her 'volume' : [0.1, 0.5, 1], 'music volume' : [0, 0.5, 1], 'fullscreen' : [True, False],} self.graphics_index = self.strings_to_data['graphics'].index(settings['graphics']) self.fov_index = self.strings_to_data['fov'].index(settings['fov']) self.sens_index = self.strings_to_data['sensitivity'].index(settings['sensitivity']) self.vol_index = self.strings_to_data['volume'].index(settings['volume']) self.music_index = self.strings_to_data['music volume'].index(settings['music volume']) self.fs_index = self.strings_to_data['fullscreen'].index(settings['fullscreen']) self.update_strings() def update_strings(self): self.graphics_button = Button((SETTINGS.canvas_actual_width/2, 150, 300, 30), "GRAPHICS: %s" % self.strings[self.graphics_index]) self.fov_button = Button((SETTINGS.canvas_actual_width/2, 200, 300, 30), "FOV: %s" % self.degrees[self.fov_index]) self.sensitivity_button = Button((SETTINGS.canvas_actual_width/2, 250, 300, 30), "SENSITIVITY: %s" % self.strings[self.sens_index]) self.volume_button = Button((SETTINGS.canvas_actual_width/2, 300, 300, 30), "MASTER VOLUME: %s" % self.strings[self.vol_index]) self.music_button = Button((SETTINGS.canvas_actual_width/2, 350, 300, 30), "MUSIC VOLUME: %s" % self.music_strings[self.music_index]) self.fullscreen_button = Button((SETTINGS.canvas_actual_width/2, 400, 300, 30), "FULLSCREEN: %s" % self.onoff[self.fs_index]) self.back_button = Button((SETTINGS.canvas_actual_width/2, 500, 200, 60), "BACK") self.restart = TEXT.Text(0,0, 'RESTART GAME TO APPLY CHANGES', SETTINGS.LIGHTGRAY, "DUGAFONT.ttf", 20) self.restart.update_pos((SETTINGS.canvas_actual_width/2)-(self.restart.layout.get_width()/2), 580) self.current_settings = { 'graphics' : self.strings_to_data['graphics'][self.graphics_index], 'fov' : self.strings_to_data['fov'][self.fov_index], 'sensitivity' : self.strings_to_data['sensitivity'][self.sens_index], 'volume' : self.strings_to_data['volume'][self.vol_index], 'music volume' : self.strings_to_data['music volume'][self.music_index], 'fullscreen' : self.strings_to_data['fullscreen'][self.fs_index],} self.save = True def control_options(self): if self.graphics_button.get_clicked(): self.graphics_index += 1 if self.graphics_index >= len(self.strings): self.graphics_index = 0 self.update_strings() elif self.fov_button.get_clicked(): self.fov_index += 1 if self.fov_index >= len(self.degrees): self.fov_index = 0 self.update_strings() elif self.sensitivity_button.get_clicked(): self.sens_index += 1 if self.sens_index >= len(self.strings): self.sens_index = 0 self.update_strings() elif self.volume_button.get_clicked(): self.vol_index += 1 if self.vol_index >= len(self.strings): self.vol_index = 0 self.update_strings() elif self.music_button.get_clicked(): self.music_index += 1 if self.music_index >= len(self.music_strings): self.music_index = 0 self.update_strings() elif self.fullscreen_button.get_clicked(): self.fs_index += 1 if self.fs_index >= len(self.onoff): self.fs_index = 0 self.update_strings() def draw(self, canvas): self.graphics_button.draw(canvas) self.fov_button.draw(canvas) self.sensitivity_button.draw(canvas) self.volume_button.draw(canvas) self.music_button.draw(canvas) self.fullscreen_button.draw(canvas) self.back_button.draw(canvas) self.title.draw(canvas) self.restart.draw(canvas) self.control_options() class ScoreMenu(Menu): def __init__(self): Menu.__init__(self, 'STATISTICS') self.area = pygame.Surface((600, 300)) self.area_rect = self.area.get_rect() self.area_rect.center = (SETTINGS.canvas_actual_width / 2, SETTINGS.canvas_target_height / 2) self.area.fill((200,200,200)) self.middle_area = pygame.Surface((200, 300)) self.middle_area.fill((180,180,180)) self.back_button = Button((SETTINGS.canvas_actual_width/2, 500, 200, 60), "BACK") self.score_testing = copy.copy(SETTINGS.statistics) self.highlights = [] for i in range(6): if i == 0: self.highlights.append(pygame.Surface((600, 35)).convert_alpha()) else: self.highlights.append(pygame.Surface((600, 30)).convert_alpha()) self.highlights[i].fill((0,0,0,20)) #High scores self.best_scores = ['HIGHEST SCORES', 'ENEMIES KILLED : %s' % SETTINGS.statistics['best enemies'], 'DAMAGE DEALT : %s' % SETTINGS.statistics['best ddealt'], 'DAMAGE TAKEN : %s' % SETTINGS.statistics['best dtaken'], 'SHOTS FIRED : %s' % SETTINGS.statistics['best shots'], 'LEVEL STREAK : %s' % SETTINGS.statistics['best levels']] self.texts = [] self.pos = 10 for i in range(len(self.best_scores)): if i == 0: self.texts.append(TEXT.Text(0, 0, self.best_scores[i], SETTINGS.DARKGRAY, "DUGAFONT.ttf", 18)) else: self.texts.append(TEXT.Text(0, 0, self.best_scores[i], SETTINGS.WHITE, "DUGAFONT.ttf", 18)) self.texts[i].update_pos(10, self.pos) self.pos += 30 #Last play scores self.last_scores = ['LAST PLAY', 'ENEMIES KILLED : %s' % SETTINGS.statistics['last enemies'], 'DAMAGE DEALT : %s' % SETTINGS.statistics['last ddealt'], 'DAMAGE TAKEN : %s' % SETTINGS.statistics['last dtaken'], 'SHOTS FIRED : %s' % SETTINGS.statistics['last shots'], 'LEVEL STREAK : %s' % SETTINGS.statistics['last levels']] self.last_texts = [] self.pos = 10 for i in range(len(self.last_scores)): if i == 0: self.last_texts.append(TEXT.Text(0, 0, self.last_scores[i], SETTINGS.DARKGRAY, "DUGAFONT.ttf", 18)) else: if self.last_scores[i] == self.best_scores[i] and self.last_scores[i].find(' 0') == -1: self.last_texts.append(TEXT.Text(0, 0, self.last_scores[i], (100,100,200), "DUGAFONT.ttf", 18)) else: self.last_texts.append(TEXT.Text(0, 0, self.last_scores[i], SETTINGS.WHITE, "DUGAFONT.ttf", 18)) self.last_texts[i].update_pos(210, self.pos) self.pos += 30 #all time statistics #format play time self.all_scores = ['ALL TIME', 'ENEMIES KILLED : %s' % SETTINGS.statistics['all enemies'], 'DAMAGE DEALT : %s' % SETTINGS.statistics['all ddealt'], 'DAMAGE TAKEN : %s' % SETTINGS.statistics['all dtaken'], 'SHOTS FIRED : %s' % SETTINGS.statistics['all shots'], 'LEVEL STREAK : %s' % SETTINGS.statistics['all levels'], 'TIME PLAYED : {:02d}h {:02d}m'.format(*divmod(SETTINGS.statistics['playtime'], 60))] self.all_texts = [] self.pos = 10 for i in range(len(self.all_scores)): if i == 0: self.all_texts.append(TEXT.Text(0, 0, self.all_scores[i], SETTINGS.DARKGRAY, "DUGAFONT.ttf", 18)) else: self.all_texts.append(TEXT.Text(0, 0, self.all_scores[i], SETTINGS.WHITE, "DUGAFONT.ttf", 18)) self.all_texts[i].update_pos(410, self.pos) self.pos += 30 def draw(self, canvas): if self.score_testing != SETTINGS.statistics: self.__init__() self.title.draw(canvas) self.back_button.draw(canvas) self.area.fill((200,200,200)) self.area.blit(self.middle_area, (200,0)) pos = 0 for i in self.highlights: self.area.blit(i, (0, pos)) if pos == 0: pos = 5 pos += 60 for i in self.texts: i.draw(self.area) for i in self.last_texts: i.draw(self.area) for i in self.all_texts: i.draw(self.area) canvas.blit(self.area, self.area_rect) class CreditsMenu(Menu): def __init__(self): Menu.__init__(self, 'CREDITS') self.back_button = Button((SETTINGS.canvas_actual_width/2, 500, 200, 60), "BACK") #Created by self.createdby = TEXT.Text(0,0, 'CREATED BY', SETTINGS.LIGHTGRAY, "DUGAFONT.ttf", 24) self.createdby.update_pos((SETTINGS.canvas_actual_width/2)-(self.createdby.layout.get_width()/2)+8, 130) self.maxwellsalmon = TEXT.Text(0,0, 'MAXWELLSALMON', SETTINGS.DARKGRAY, "DUGAFONT.ttf", 38) self.maxwellsalmon.update_pos((SETTINGS.canvas_actual_width/2)-(self.maxwellsalmon.layout.get_width()/2)+8, 160) #Music self.musicby = TEXT.Text(0,0, 'MUSIC BY', SETTINGS.LIGHTGRAY, "DUGAFONT.ttf", 20) self.musicby.update_pos((SETTINGS.canvas_actual_width/2)-(self.musicby.layout.get_width()/2)+8, 210) self.eli = TEXT.Text(0,0, 'HUD-LUM @ SOUNDCLOUD', SETTINGS.DARKGRAY, "DUGAFONT.ttf", 30) self.eli.update_pos((SETTINGS.canvas_actual_width/2)-(self.eli.layout.get_width()/2)+8, 240) #Maps self.contributions = TEXT.Text(0,0, 'THANKS TO', SETTINGS.LIGHTGRAY, "DUGAFONT.ttf", 20) self.contributions.update_pos((SETTINGS.canvas_actual_width/2)-(self.contributions.layout.get_width()/2)+8, 290) self.contributors = TEXT.Text(0,0, 'POELE, OLE, ROCKETTHEMINIFIG, ANDY BOY, J4CKINS' , SETTINGS.DARKGRAY, "DUGAFONT.ttf", 20) self.contributors.update_pos((SETTINGS.canvas_actual_width/2)-(self.contributors.layout.get_width()/2)+8, 320) self.contributors2 = TEXT.Text(0,0, 'THEFATHOBBITS, STARLITEPONY' , SETTINGS.DARKGRAY, "DUGAFONT.ttf", 20) self.contributors2.update_pos((SETTINGS.canvas_actual_width/2)-(self.contributors2.layout.get_width()/2)+8, 345) self.specialthanks = TEXT.Text(0,0, 'THANKS TO THE PYGAME COMMUNITY FOR HELP AND MOTIVATION', SETTINGS.DARKGRAY, "DUGAFONT.ttf", 15) self.specialthanks.update_pos((SETTINGS.canvas_actual_width/2)-(self.specialthanks.layout.get_width()/2)+8, 380) self.and_you = TEXT.Text(0,0, 'THANKS TO YOU FOR PLAYING!' , SETTINGS.GREEN, "DUGAFONT.ttf", 22) self.and_you.update_pos((SETTINGS.canvas_actual_width/2)-(self.and_you.layout.get_width()/2)+8, 410) def draw(self, canvas, show): self.back_button.draw(canvas) self.title.draw(canvas) self.createdby.draw(canvas) self.musicby.draw(canvas) self.eli.draw(canvas) self.contributions.draw(canvas) self.contributors.draw(canvas) self.contributors2.draw(canvas) self.specialthanks.draw(canvas) self.maxwellsalmon.draw(canvas) if show or SETTINGS.statistics['playtime'] >= 120: self.and_you.draw(canvas) class SupportSplash: def __init__(self): self.area = pygame.Surface((200, 300)).convert() self.rect = self.area.get_rect() self.rect.topleft = SETTINGS.canvas_actual_width - 220, SETTINGS.canvas_target_height - 280 self.area.fill((200,200,200)) self.title = TEXT.Text(0,0, 'THANKS FOR PLAYING', SETTINGS.DARKGRAY, "DUGAFONT.ttf", 19) self.title.update_pos((self.rect.width/2) - (self.title.layout.get_width()/2)+2, 5) self.pleas = ['You have been playing DUGA', 'for over two hours now. I', 'really hope you enjoy it.', 'If you do, please consider', 'buying it. If you have al-', 'ready bought it, thank you', 'very much! If you don\'t think', 'it is worth money, please let', 'me know what to improve.', 'Well, I\'m happy to have you', 'playing, so I added you to', 'the credits!'] self.texts = [] self.pos = 30 self.button = Button((SETTINGS.canvas_actual_width - 120, SETTINGS.canvas_target_height - 15, 192, 40), "LEAVE ME ALONE!") for i in range(len(self.pleas)): self.texts.append(TEXT.Text(0, 0, self.pleas[i], SETTINGS.WHITE, "DUGAFONT.ttf", 15)) self.texts[i].update_pos((self.rect.width/2) - (self.texts[i].layout.get_width()/2)+2, self.pos) self.pos += 17 def draw(self, canvas): self.title.draw(self.area) for text in self.texts: text.draw(self.area) canvas.blit(self.area, self.rect) self.button.draw(canvas) #---------------------------------------- IN-GAME MENUS ---------------------------------------------------------------------------- class GMainMenu(Menu): def __init__(self): Menu.__init__(self, 'DUGA') self.resume_button = Button((SETTINGS.canvas_actual_width/2, 200, 200, 60), "RESUME") self.exit_button = Button((SETTINGS.canvas_actual_width/2, 500, 200, 60), "EXIT GAME") self.background = pygame.Surface((SETTINGS.canvas_actual_width, SETTINGS.canvas_target_height)).convert_alpha() self.background.fill((100,100,100,10)) def draw(self, canvas): canvas.blit(self.background, (0,0)) self.resume_button.draw(canvas) self.exit_button.draw(canvas) self.title.draw(canvas) class Button: def __init__(self, xywh, text): #ADD CLICK SOUND self.surface = pygame.Surface((xywh[2], xywh[3])) self.rect = self.surface.get_rect() self.rect.center = (xywh[0], xywh[1]) self.clicked = False self.text = TEXT.Text(0,0, text, SETTINGS.WHITE, "DUGAFONT.ttf", 24) self.text.update_pos(xywh[0] - self.text.layout.get_width()/2, xywh[1] - (self.text.layout.get_height() / 2)+2) self.filling = SETTINGS.LIGHTGRAY self.sound = pygame.mixer.Sound(os.path.join('sounds', 'other', 'button.ogg')) def draw(self, canvas): self.surface.fill(self.filling) canvas.blit(self.surface, self.rect) self.text.draw(canvas) if self.rect.collidepoint(pygame.mouse.get_pos()): self.filling = SETTINGS.DARKGRAY else: self.filling = SETTINGS.LIGHTGRAY def get_clicked(self): if self.rect.collidepoint(pygame.mouse.get_pos()): if pygame.mouse.get_pressed()[0]: self.clicked = True if not pygame.mouse.get_pressed()[0] and self.clicked: self.clicked = False SOUND.play_sound(self.sound, 0) return True else: return False else: return False ================================================ FILE: MUSIC.py ================================================ import pygame import os import SETTINGS import SOUND class Music: def __init__(self): self.settings_volume = SETTINGS.volume * 0.8 self.base_track = pygame.mixer.Sound(os.path.join('sounds', 'music', 'soft_layer.ogg')) self.hard_track = pygame.mixer.Sound(os.path.join('sounds', 'music', 'hard_layer.ogg')) self.hard_volume = 0 self.menu_volume = self.settings_volume * 0.8 pygame.mixer.Sound.set_volume(self.base_track, max(0, (self.settings_volume - self.menu_volume) * SETTINGS.music_volume)) pygame.mixer.Sound.set_volume(self.hard_track, max(0, (self.hard_volume - self.menu_volume) * SETTINGS.music_volume)) if SETTINGS.music_volume > 0: pygame.mixer.Sound.play(self.base_track, loops=-1) pygame.mixer.Sound.play(self.hard_track, loops=-1) def control_music(self): if SETTINGS.music_volume > 0: if [x for x in SETTINGS.npc_list if x.state == 'attacking' and not x.dead] and not SETTINGS.menu_showing or SETTINGS.player_states['dead']: if self.hard_volume < self.settings_volume: self.hard_volume += 0.05 pygame.mixer.Sound.set_volume(self.hard_track, max(0, (self.hard_volume - self.menu_volume) * SETTINGS.music_volume)) else: if self.hard_volume > 0: self.hard_volume -= 0.005 pygame.mixer.Sound.set_volume(self.hard_track, max(0, (self.hard_volume - self.menu_volume) * SETTINGS.music_volume)) if SETTINGS.menu_showing: if self.menu_volume < self.settings_volume * 0.2: self.menu_volume += 0.05 pygame.mixer.Sound.set_volume(self.base_track, max(0, (self.settings_volume - self.menu_volume) * SETTINGS.music_volume)) pygame.mixer.Sound.set_volume(self.hard_track, max(0, (self.hard_volume - self.menu_volume) * SETTINGS.music_volume)) else: if self.menu_volume > 0: self.menu_volume -= 0.05 pygame.mixer.Sound.set_volume(self.base_track, max(0, (self.settings_volume - self.menu_volume) * SETTINGS.music_volume)) pygame.mixer.Sound.set_volume(self.hard_track, max(0, (self.hard_volume - self.menu_volume) * SETTINGS.music_volume)) ================================================ FILE: Manuals.txt ================================================ _____ _ _ _____ __ __ _ | __ \ | | | | / ____| /\ | \/ | | | | | | | | | | | | | __ / \ | \ / | __ _ _ __ _ _ __ _ | | ___ | | | | | | | | | | |_ | / /\ \ | |\/| | / _` | | '_ \ | | | | / _` | | | / __| | |__| | | |__| | | |__| | / ____ \ | | | | | (_| | | | | | | |_| | | (_| | | | \__ \ |_____/ \____/ \_____| /_/ \_\ |_| |_| \__,_| |_| |_| \__,_| \__,_| |_| |___/ ## CONTROLS FOR DUGA ## Move : WASD Aim : RMB Shoot : LMB Interact : E Inventory : I Pause: Esc ## USING THE LEVEL EDITOR ## Controls: Place entity: LMB Remove entiry: RMB ------------------------------------------------------------------------------------------------------------ If you want to make custom levels or segments for level generation, follow these instructions. The editor is quite rough around the edges, but it should be easy to use. When opening LevelEditor.exe, you will see a command prompt, which provides the instructions in order to make your own maps. You can either make custom levels or custom segments for level generation. They both have some slightly different properties, so here a some things to remember: --** Making custom levels **-- Tiles of the type "hdoor" means "horisontal door" and "vdoor" meand "vertical door". Place the doors respectively. All levels must have a start position and a tile of the type "end", which is where the player exits the level. The level should be a closed area, in order to calculate the walkable space for NPC's. Your custom levels are saved in the file "customLevels.dat" found within the folder called "data". --** Making custom segments **-- All segments are 9x9 tiles in size. They function as a piece in a jigsaw puzzle, meaning they make up one large map. This means, they must have entrances to other maps. Each entrance must be in the middle of an edge - being the fifth tile. By the area names "entrances" you must indicate, where you have placed the entrances, so the level generations knows where to place your segment. There are three different types of segments. "normal", "end" and "start". There are no special properties to "normal", there must be a tile of the type "end" in segments of the type "end" and there must be a player start position in segments of the type "start". The segments must also be a closed area in order to calculate the walkable space for NPC's. Your custom segments will automatically be used in the level generation and saved in the file "customSegments.dat" within the folder called "data" --------------------------------------------------------------------------------------------------------------------------- If you have any questions, feel free to contact MaxwellSalmon on maxwellsalmon.itch.io/duga or reddit.com/r/duga ================================================ FILE: NPC.py ================================================ import SETTINGS import SPRITES import PATHFINDING import ITEMS import SOUND import os import random import math import pygame #stats format in bottom of script #pos is in tiles, face in degrees, frame_interval is seconds between frames, speed is pixels/second class Npc: def __init__(self, stats, sounds, texture): #Technical settings self.stats = stats #Used for creating new NPCs self.sounds = sounds self.ID = stats['id'] self.map_pos = stats['pos'] self.pos = [self.map_pos[0]*SETTINGS.tile_size, self.map_pos[1]*SETTINGS.tile_size] self.face = stats['face'] self.frame_interval = stats['spf'] self.dda_list = SETTINGS.walkable_area + [x for x in SETTINGS.all_solid_tiles if x.type == 'sprite'] #Visual and rect settings self.rect = pygame.Rect((self.pos[0], self.pos[1]), (SETTINGS.tile_size/3, SETTINGS.tile_size/3)) self.rect.center = (self.pos[0] + SETTINGS.tile_size/2, self.pos[1] + SETTINGS.tile_size/2) self.real_x = self.rect.x self.real_y = self.rect.y #Initialise boring variables self.timer = 0 self.idle_timer = 0 self.die_animation = False self.running_animation = None self.add = 0 self.dist = None self.collide_list = SETTINGS.all_solid_tiles + SETTINGS.npc_list self.solid = True self.side = None self.in_canvas = False self.path = [] self.path_progress = 0 self.attack_move = False self.atckchance = 5 self.movechance = 10 self.knockback = 0 self.postheta = 0 self.mein_leben = False self.type = 'npc' #NPC Characteristics self.health = stats['health'] self.speed = stats['speed'] self.OG_speed = self.speed self.mind = stats['mind'] self.state = stats['state'] self.OG_state = self.state self.atcktype = stats['atcktype'] self.name = stats['name'] if stats['dmg'] != 3.1415: self.dmg = stats['dmg'] else: self.dmg = random.choice([1,2,3]) self.atckrate = stats['atckrate'] self.range = 5 #make first level easier if SETTINGS.current_level == 0 and SETTINGS.levels_list == SETTINGS.glevels_list: self.health = int(self.health * 0.8) self.dmg = int(self.dmg * 0.8) #Make late levels harder >:) elif SETTINGS.current_level > 4 and SETTINGS.levels_list == SETTINGS.glevels_list: self.health += int(SETTINGS.current_level / 3) self.dmg += int(SETTINGS.current_level / 5) #Make NPC stronger if player is doing well if SETTINGS.player_health >= 90 or SETTINGS.player_armor >= 90: self.dmg += 2 #Give NPC more health if player has lots of ammo - phew, long line. if SETTINGS.inventory['primary'] and SETTINGS.held_ammo[SETTINGS.inventory['primary'].ammo_type] >= SETTINGS.max_ammo[SETTINGS.inventory['primary'].ammo_type]: self.health = int(self.health * 1.5) #Npc actions self.dead = False self.moving = False self.attacking = False self.hurting = False self.player_in_view = False #Textures and animations self.texture_path = texture # Used for creating new NPCS self.texture = pygame.image.load(texture).convert_alpha() self.texturerect = self.texture.get_rect() self.stand_texture = [self.texture.subsurface(0,0,64,128).convert_alpha(), self.texture.subsurface(64,0,64,128).convert_alpha(), self.texture.subsurface(128,0,64,128).convert_alpha(), self.texture.subsurface(192,0,64,128).convert_alpha(), self.texture.subsurface(256,0,64,128).convert_alpha(), self.texture.subsurface(320,0,64,128).convert_alpha(), self.texture.subsurface(384,0,64,128).convert_alpha(), self.texture.subsurface(448,0,64,128).convert_alpha()] self.front_texture = [self.texture.subsurface(0,128,64,128).convert_alpha(), self.texture.subsurface(64,128,64,128).convert_alpha(), self.texture.subsurface(128,128,64,128).convert_alpha(), self.texture.subsurface(192,128,64,128).convert_alpha(), self.texture.subsurface(256,128,64,128).convert_alpha(), self.texture.subsurface(320,128,64,128).convert_alpha(), self.texture.subsurface(384,128,64,128).convert_alpha(), self.texture.subsurface(448,128,64,128).convert_alpha(), self.texture.subsurface(512,128,64,128).convert_alpha(), self.texture.subsurface(576,128,64,128).convert_alpha()] self.frontright_texture = [self.texture.subsurface(0,256,64,128).convert_alpha(), self.texture.subsurface(64,256,64,128).convert_alpha(), self.texture.subsurface(128,256,64,128).convert_alpha(), self.texture.subsurface(192,256,64,128).convert_alpha(), self.texture.subsurface(256,256,64,128).convert_alpha(), self.texture.subsurface(320,256,64,128).convert_alpha(), self.texture.subsurface(384,256,64,128).convert_alpha(), self.texture.subsurface(448,256,64,128).convert_alpha(), self.texture.subsurface(512,256,64,128).convert_alpha(), self.texture.subsurface(576,256,64,128).convert_alpha()] self.right_texture = [self.texture.subsurface(0,384,64,128).convert_alpha(), self.texture.subsurface(64,384,64,128).convert_alpha(), self.texture.subsurface(128,384,64,128).convert_alpha(), self.texture.subsurface(192,384,64,128).convert_alpha(), self.texture.subsurface(256,384,64,128).convert_alpha(), self.texture.subsurface(320,384,64,128).convert_alpha(), self.texture.subsurface(384,384,64,128).convert_alpha(), self.texture.subsurface(448,384,64,128).convert_alpha(), self.texture.subsurface(512,384,64,128).convert_alpha(), self.texture.subsurface(576,384,64,128).convert_alpha()] self.backright_texture = [self.texture.subsurface(0,512,64,128).convert_alpha(), self.texture.subsurface(64,512,64,128).convert_alpha(), self.texture.subsurface(128,512,64,128).convert_alpha(), self.texture.subsurface(192,512,64,128).convert_alpha(), self.texture.subsurface(256,512,64,128).convert_alpha(), self.texture.subsurface(320,512,64,128).convert_alpha(), self.texture.subsurface(384,512,64,128).convert_alpha(), self.texture.subsurface(448,512,64,128).convert_alpha(), self.texture.subsurface(512,512,64,128).convert_alpha(), self.texture.subsurface(576,512,64,128).convert_alpha()] self.back_texture = [self.texture.subsurface(0,640,64,128).convert_alpha(), self.texture.subsurface(64,640,64,128).convert_alpha(), self.texture.subsurface(128,640,64,128).convert_alpha(), self.texture.subsurface(192,640,64,128).convert_alpha(), self.texture.subsurface(256,640,64,128).convert_alpha(), self.texture.subsurface(320,640,64,128).convert_alpha(), self.texture.subsurface(384,640,64,128).convert_alpha(), self.texture.subsurface(448,640,64,128).convert_alpha(), self.texture.subsurface(512,640,64,128).convert_alpha(), self.texture.subsurface(576,640,64,128).convert_alpha()] self.backleft_texture = [] self.left_texture = [] self.frontleft_texture = [] for frame in self.backright_texture: self.backleft_texture.append(pygame.transform.flip(frame, True, False)) for frame in self.right_texture: self.left_texture.append(pygame.transform.flip(frame, True, False)) for frame in self.frontright_texture: self.frontleft_texture.append(pygame.transform.flip(frame, True, False)) self.die_texture = [self.texture.subsurface(0,768,64,128).convert_alpha(), self.texture.subsurface(64,768,64,128).convert_alpha(), self.texture.subsurface(128,768,64,128).convert_alpha(), self.texture.subsurface(192,768,64,128).convert_alpha(), self.texture.subsurface(256,768,64,128).convert_alpha(), self.texture.subsurface(320,768,64,128).convert_alpha(), self.texture.subsurface(384,768,64,128).convert_alpha(), self.texture.subsurface(448,768,64,128).convert_alpha(), self.texture.subsurface(512,768,64,128).convert_alpha(), self.texture.subsurface(576,768,64,128).convert_alpha(), self.texture.subsurface(640,768,64,128).convert_alpha()] self.hit_texture = [self.texture.subsurface(0,896,64,128).convert_alpha(), self.texture.subsurface(64,896,64,128).convert_alpha(), self.texture.subsurface(128,896,64,128).convert_alpha(), self.texture.subsurface(192,896,64,128).convert_alpha(), self.texture.subsurface(256,896,64,128).convert_alpha(), self.texture.subsurface(320,896,64,128).convert_alpha()] self.hurt_texture = [self.die_texture[0]] self.current_frame = 1 self.update_timer = 0 #Creating the sprite rect is awful, I know. Keeps it from entering walls. self.sprite = SPRITES.Sprite(self.front_texture[1], self.ID, [self.rect.centerx - int(SETTINGS.tile_size / 12), self.rect.centery - int(SETTINGS.tile_size / 10)], 'npc', self) #The position in SETTINGS.all_sprites of this NPC self.num = len(SETTINGS.all_sprites)-1 def think(self): self.map_pos = [int(self.rect.centerx / SETTINGS.tile_size), int(self.rect.centery / SETTINGS.tile_size)] if self.state == 'attacking' or self.state == 'fleeing': self.speed = self.OG_speed * 2 if not self.dead: self.timer += SETTINGS.dt self.update_timer += SETTINGS.dt if self.update_timer >= 2: self.update_timer = 0 if not self.dead and self.health > 0 and not SETTINGS.player_states['dead']: self.render() if self.dist and self.dist <= SETTINGS.render * SETTINGS.tile_size * 1.2: #PASSIVE if self.mind == 'passive': if self.state == 'idle': self.idle() elif self.state == 'patrolling': self.move() #HOSTILE elif self.mind == 'hostile': if self.state == 'idle': self.idle() if not SETTINGS.ignore_player: if self.player_in_view: if self.detect_player(): self.path = [] SOUND.play_sound(self.sounds['spot'], self.dist) self.state = 'attacking' elif self.state == 'patrolling': if self.player_in_view and not SETTINGS.ignore_player and self.detect_player(): self.path = [] SOUND.play_sound(self.sounds['spot'], self.dist) self.state = 'attacking' elif self.dist <= SETTINGS.tile_size / 2 and not SETTINGS.ignore_player: state = 'attacking' else: self.move() elif self.state == 'attacking': self.attack() #SHY elif self.mind == 'shy': if self.state == 'idle': self.idle() if not SETTINGS.ignore_player: if self.player_in_view: if self.detect_player(): self.path = [] SOUND.play_sound(self.sounds['spot'], self.dist) self.state = 'fleeing' elif self.dist <= SETTINGS.tile_size / 2: state = 'attacking' elif self.state == 'patrolling': if self.player_in_view: if not SETTINGS.ignore_player: if self.detect_player(): self.path = [] SOUND.play_sound(self.sounds['spot'], self.dist) self.state = 'fleeing' elif self.dist <= SETTINGS.tile_size / 2: if not SETTINGS.ignore_player: state = 'attacking' else: self.move() elif self.state == 'fleeing': self.move() #Run animations if self.hurting: self.animate('hurting') elif self.moving: self.animate('walking') if SETTINGS.player_states['dead']: self.face += 10 if self.face >= 360: self.face -= 360 self.render() elif self.health <= 0 and not self.dead: self.animate('dying') self.render() def render(self): '''== Draw the NPC ==''' if self.dead: self.solid = False xpos = SETTINGS.player_rect.centerx - self.rect.centerx ypos = SETTINGS.player_rect.centery - self.rect.centery self.dist = math.sqrt(xpos*xpos + ypos*ypos) if self.dist <= SETTINGS.render * SETTINGS.tile_size: theta = math.atan2(-ypos, xpos) % (2*math.pi) theta = math.degrees(theta) self.postheta = theta theta -= self.face if theta < 0: theta += 360 elif theta > 360: theta -= 360 self.theta = theta self.sprite.update_pos([self.rect.x, self.rect.y]) #What side is the NPC facing? (or not self.side is to make sure it finds the right angle from initialization) if theta <= 22.5 or theta >= 337.5: self.player_in_view = True if (self.side != 'front' and not self.dead and not self.hurting and not self.attacking and self.in_canvas) or not self.side: if self.moving: self.sprite.texture = self.front_texture[self.current_frame] else: self.sprite.texture = self.stand_texture[0] self.side = 'front' elif theta <= 67.5 and theta >= 22.5: self.player_in_view = True if (self.side != 'frontleft' and not self.dead and not self.hurting and not self.attacking and self.in_canvas) or not self.side: if self.moving: self.sprite.texture = self.frontleft_texture[self.current_frame] else: self.sprite.texture = self.stand_texture[7] self.side = 'frontleft' elif theta <= 112.5 and theta >= 67.5: self.player_in_view = False if (self.side != 'left' and not self.dead and not self.hurting and not self.attacking and self.in_canvas) or not self.side: if self.moving: self.sprite.texture = self.left_texture[self.current_frame] else: self.sprite.texture = self.stand_texture[6] self.side = 'left' elif theta <= 157.5 and theta >= 112.5: self.player_in_view = False if (self.side != 'backleft' and not self.dead and not self.hurting and not self.attacking and self.in_canvas) or not self.side: if self.moving: self.sprite.texture = self.backleft_texture[self.current_frame] else: self.sprite.texture = self.stand_texture[5] self.side = 'backleft' elif theta <= 202.5 and theta >= 157.5: self.player_in_view = False if (self.side != 'back' and not self.dead and not self.hurting and not self.attacking and self.in_canvas) or not self.side: if self.moving: self.sprite.texture = self.back_texture[self.current_frame] else: self.sprite.texture = self.stand_texture[4] self.side = 'back' elif theta <= 247.5 and theta >= 202.5: self.player_in_view = False if (self.side != 'backright' and not self.dead and not self.hurting and not self.attacking and self.in_canvas) or not self.side: if self.moving: self.sprite.texture = self.backright_texture[self.current_frame] else: self.sprite.texture = self.stand_texture[3] self.side = 'backright' elif theta <= 292.5 and theta >= 247.5: self.player_in_view = False if (self.side != 'right' and not self.dead and not self.hurting and not self.attacking and self.in_canvas) or not self.side: if self.moving: self.sprite.texture = self.right_texture[self.current_frame] else: self.sprite.texture = self.stand_texture[2] self.side = 'right' elif theta <= 337.5 and theta >= 292.5: self.player_in_view = True if (self.side != 'frontright' and not self.dead and not self.hurting and not self.attacking and self.in_canvas) or not self.side: if self.moving: self.sprite.texture = self.frontright_texture[self.current_frame] else: self.sprite.texture = self.stand_texture[1] self.side = 'frontright' if SETTINGS.all_sprites[self.num] != self.sprite: SETTINGS.all_sprites[self.num] = self.sprite #Find out what x and y coordinates would change if self.face == 90: self.front_tile = (0, -1) elif self.face == 180: self.front_tile = (-1, 0) elif self.face == 270: self.front_tile = (0, 1) elif self.face == 0 or self.face == 360: self.front_tile = (1, 0) def round_up(self, a): return int(a + 0.5) def detect_player(self): '''== Is player visible from NPC position? ==\ndetect_player(self) -> boolean''' own_tile = self.map_pos #front_tile = [own_tile[0] + self.front_tile[0], own_tile[1] + self.front_tile[1]] player_tile = SETTINGS.player_map_pos #DDA Algorithm x1,y1 = own_tile[0], own_tile[1] x2,y2 = player_tile[0], player_tile[1] #If the coords are negative, start from player instead of NPC if x1 > x2 or (x1 == x2 and y1 > y2): temp1,temp2 = x1,y1 x1,y1 = x2,y2 x2,y2 = temp1,temp2 x,y = x1, y1 dx = abs(x2-x1) dy = abs(y2-y1) length = dx if dx > dy else dy #Make sure, you won't divide by 0 if length == 0: length = 0.001 xinc = (x2-x1)/float(length) yinc = (y2-y1)/float(length) mapx = self.round_up(x) mapy = self.round_up(y) #Extend DDA algorithm for i in range(int(length)): if i > SETTINGS.render: break x += xinc y += yinc mapx = self.round_up(x) mapy = self.round_up(y) #If line of sight hits a wall next_wall = [tile for tile in self.dda_list if tile.map_pos == [mapx, mapy]] if not next_wall: break else: next_wall = next_wall[0] if SETTINGS.tile_visible[next_wall.ID]: if next_wall.type != 'hdoor' and next_wall.type != 'vdoor': break elif next_wall.type == 'hdoor' or next_wall.type == 'vdoor': if next_wall.solid: break #if player is spotted if mapx == x2 and mapy == y2: return True if self.dist <= SETTINGS.tile_size/3: return True def collide_update(self, x, y): #make sure the NPC doesn't walk inside stuff self.real_x += x * SETTINGS.dt self.real_y += y * SETTINGS.dt self.rect.x = self.real_x self.rect.y = self.real_y if self.collide_list[-1] != SETTINGS.player: self.collide_list.append(SETTINGS.player) else: self.collide_list[-1] = SETTINGS.player tile_hit_list = [s for s in self.collide_list if self.rect.colliderect(s)] for tile in tile_hit_list: if tile.solid: if x > 0: self.rect.right = tile.rect.left self.real_x = self.rect.x if x < 0: self.rect.left = tile.rect.right self.real_x = self.rect.x if y > 0: self.rect.bottom = tile.rect.top self.real_y = self.rect.y if y < 0: self.rect.top = tile.rect.bottom self.real_y = self.rect.y for door in SETTINGS.all_doors: if door.get_dist(self.rect.center, 'npc') <= 50: door.sesam_luk_dig_op() break def move(self): #Make the NPC move according to current state. moving_up = False moving_down = False moving_right = False moving_left = False if self.path and self.rect.center != self.path[-1].rect.center and self.health > 0 and not self.hurting: self.moving = True #Redo path if tile is occupied by another NPC. if self.update_timer <= 0.5: for npc in SETTINGS.npc_list: if npc.map_pos == self.path[-1].map_pos: available_pos = [x for x in SETTINGS.walkable_area if abs(x.map_pos[0]-self.map_pos[0]) <= 3 and abs(x.map_pos[1]-self.map_pos[1]) <= 3] self.path = PATHFINDING.pathfind(self.map_pos, random.choice(available_pos).map_pos) self.path_progress = 0 break if self.rect.colliderect(self.path[self.path_progress].rect) and self.path[self.path_progress] != self.path[-1]: self.path_progress += 1 else: #Move down if self.rect.centery < self.path[self.path_progress].rect.centery: if abs(self.path[self.path_progress].rect.centery - self.rect.centery) >= self.speed * SETTINGS.dt: self.collide_update(0, self.speed) moving_down = True else: self.rect.centery = self.path[self.path_progress].rect.centery #Moving up elif self.rect.centery > self.path[self.path_progress].rect.centery: if abs(self.path[self.path_progress].rect.centery - self.rect.centery) >= self.speed * SETTINGS.dt: self.collide_update(0, -self.speed) moving_up = True else: self.rect.centery = self.path[self.path_progress].rect.centery #Move right if self.rect.centerx < self.path[self.path_progress].rect.centerx: if abs(self.path[self.path_progress].rect.centerx - self.rect.centerx) >= self.speed * SETTINGS.dt: self.collide_update(self.speed, 0) moving_right = True else: self.rect.centerx = self.path[self.path_progress].rect.centerx #Move left elif self.rect.centerx > self.path[self.path_progress].rect.centerx: if abs(self.rect.centerx - self.path[self.path_progress].rect.centerx) >= self.speed * SETTINGS.dt: self.collide_update(-self.speed, 0) moving_left = True else: self.rect.centerx = self.path[self.path_progress].rect.centerx if moving_up: if not moving_right and not moving_left: self.face = 90 elif moving_right: self.face = 45 elif moving_left: self.face = 135 elif moving_down: if not moving_right and not moving_left: self.face = 270 elif moving_right: self.face = 315 elif moving_left: self.face = 225 elif moving_left: self.face = 180 elif moving_right: self.face = 0 else: self.moving = False self.attack_move = False if self.timer >= self.frame_interval: self.path = [] self.path_progress = 0 if self.state == 'patrolling': if self.path == []: if random.randint(0,3) == 3: self.state = 'idle' self.sprite.texture = self.stand_texture[4] else: #Make the NPC not walk too far. available_pos = [x for x in SETTINGS.walkable_area if abs(x.map_pos[0]-self.map_pos[0]) <= 3 and abs(x.map_pos[1]-self.map_pos[1]) <= 3] self.path = PATHFINDING.pathfind(self.map_pos, random.choice(available_pos).map_pos) elif self.state == 'fleeing': if self.dist <= SETTINGS.tile_size * 4: flee_pos = random.choice(SETTINGS.walkable_area) player_tile = [x for x in SETTINGS.walkable_area if x.map_pos == SETTINGS.player_map_pos] if player_tile: player_tile = player_tile[0] else: player_tile = PATHFINDING.find_near_position(SETTINGS.player_map_pos) if self.player_in_view: if self.detect_player() and player_tile: if ((SETTINGS.walkable_area.index(flee_pos) < SETTINGS.walkable_area.index(player_tile) + int(SETTINGS.current_level_size[0] / 5)) or (SETTINGS.walkable_area.index(flee_pos) > SETTINGS.walkable_area.index(player_tile) - int(SETTINGS.current_level_size[0] / 5))) and self.path == []: self.path_progress = 0 self.path = PATHFINDING.pathfind(self.map_pos, flee_pos.map_pos) def idle(self): #Make the NPC rotate randomly as it stands still. self.idle_timer += SETTINGS.dt if self.idle_timer >= 3: if random.randint(0,2) == 2: self.face += 45 elif random.randint(0,2) == 2: self.face -= 45 if self.face >= 360: self.face -= 360 self.idle_timer = 0 #Do only change to patrolling if it was that in the first place. if self.OG_state != 'idle': if random.randint(0, 2) == 2: self.state = 'patrolling' #Make NPC react to gunshot if close. Or just if the player is too close. if (self.dist <= SETTINGS.tile_size * 4 and SETTINGS.mouse_btn_active and SETTINGS.current_gun) or self.dist <= self.rect.width: self.face = self.face + self.theta if self.face >= 360: self.face -= 360 self.face = min([0,90,180,270,359], key=lambda x:abs(x-self.face)) def attack(self): if self.attack_move: self.move() else: if self.atcktype == 'melee': #Move close to player and keep attacking if self.dist <= SETTINGS.tile_size*0.7: self.path = [] self.moving = False if not self.attacking: if self.timer >= self.frame_interval * self.atckrate: self.attacking = True self.timer = 0 else: #Make the NPC not flinch when attacking if self.hurting: if random.randint(0,2) != 2 or self.attacking: self.animate('attacking') self.hurting = False if random.randint(0,2) == 2: SOUND.play_sound(random.choice(self.sounds['damage']), self.dist) else: self.animate('attacking') else: if self.dist > SETTINGS.tile_size*0.7 and self.path == []: self.path_progress = 0 self.path = PATHFINDING.pathfind(self.map_pos, SETTINGS.player_map_pos) elif self.path != []: try: if self.path[-1].map_pos != SETTINGS.player_map_pos: if self.dist <= (SETTINGS.render/2) * SETTINGS.tile_size and random.randint(0, 5) == 5: self.path_progress = 0 self.path = PATHFINDING.pathfind(self.map_pos, SETTINGS.player_map_pos) elif random.randint(0,10) >= 8: self.path_progress = 0 self.path = PATHFINDING.pathfind(self.map_pos, SETTINGS.player_map_pos) else: self.move() except: pass elif self.atcktype == 'hitscan': #Move somewhat close to player and change position after attacking if self.dist <= SETTINGS.tile_size * self.range and (self.dist >= SETTINGS.tile_size * 1.5 or (SETTINGS.current_gun and SETTINGS.current_gun.guntype == 'melee')) and not self.attack_move: self.path = [] self.moving = False if not self.attacking: if random.randint(0, self.atckchance) == 5 and self.detect_player(): self.attacking = True self.atckchance += int(self.atckrate) self.movechance = 10 else: if random.randint(0, self.movechance) == 10: move_pos = random.choice([x for x in SETTINGS.walkable_area if (x.map_pos[0] <= self.map_pos[0]+1 and x.map_pos[0] >= self.map_pos[0]-1) and (x.map_pos[1] <= self.map_pos[1]+1 and x.map_pos[1] >= self.map_pos[1]-1)]) self.path_progress = 0 self.path = PATHFINDING.pathfind(self.map_pos, move_pos.map_pos) self.attacking = False self.attack_move = True #This variable is to make sure the NPC doesn't just walk around without attacking. self.movechance += 3 self.atckchance = 5 #There is a chance the NPC will not flinch when shot while attacking elif self.attacking: if self.hurting: if random.randint(0,5) >= 3: self.animate('attacking') self.hurting = False if random.randint(0,2) == 2: SOUND.play_sound(random.choice(self.sounds['damage']), self.dist) else: self.animate('attacking') #Move away from player if too close elif self.dist < SETTINGS.tile_size * 1.5 and self.health <= 6: if self.rect.centerx > SETTINGS.player_rect.centerx: self.collide_update(self.speed, 0) self.animate('walking') elif self.rect.centerx < SETTINGS.player_rect.centerx: self.collide_update(-self.speed, 0) self.animate('walking') if self.rect.centery > SETTINGS.player_rect.centery: self.collide_update(0, self.speed) self.animate('walking') elif self.rect.centery < SETTINGS.player_rect.centery: self.collide_update(0, -self.speed) self.animate('walking') else: if not self.attack_move: if self.dist >= SETTINGS.tile_size * 2.5 and self.path == []: self.path_progress = 0 self.path = PATHFINDING.pathfind(self.map_pos, SETTINGS.player_map_pos) elif self.path != []: try: if self.path[-1].map_pos != SETTINGS.player_map_pos: if self.dist <= (SETTINGS.render/2) * SETTINGS.tile_size and random.randint(0, 5) == 5: self.path_progress = 0 self.path = PATHFINDING.pathfind(self.map_pos, SETTINGS.player_map_pos) elif random.randint(0,10) == 10: self.path_progress = 0 self.path = PATHFINDING.pathfind(self.map_pos, SETTINGS.player_map_pos) else: self.move() except: pass def animate(self, animation): '''== Animate NPC ==\nanimation -> dying, walking, attacking, hurting''' if self.running_animation != animation: self.current_frame = 0 self.running_animation = animation #walk animation if animation == 'walking': if self.side == 'front': self.sprite.texture = self.front_texture[self.current_frame] elif self.side == 'frontleft': self.sprite.texture = self.frontleft_texture[self.current_frame] elif self.side == 'left': self.sprite.texture = self.left_texture[self.current_frame] elif self.side == 'backleft': self.sprite.texture = self.backleft_texture[self.current_frame] elif self.side == 'back': self.sprite.texture = self.back_texture[self.current_frame] elif self.side == 'backright': self.sprite.texture = self.backright_texture[self.current_frame] elif self.side == 'right': self.sprite.texture = self.right_texture[self.current_frame] elif self.side == 'frontright': self.sprite.texture = self.frontright_texture[self.current_frame] if self.timer >= self.frame_interval: self.current_frame += 1 self.timer = 0 if self.current_frame == len(self.front_texture)-1: self.current_frame = 0 #die animation elif animation == 'dying': self.sprite.texture = self.die_texture[self.current_frame] if self.current_frame == 0 and not self.mein_leben: self.mein_leben = True SOUND.play_sound(random.choice(self.sounds['die']), self.dist) if self.timer >= self.frame_interval and self.current_frame < len(self.die_texture)-1: self.current_frame += 1 self.timer = 0 elif self.current_frame == len(self.die_texture)-1 and self.knockback == 0: self.dead = True self.drop_item() SETTINGS.statistics['last enemies'] += 1 elif self.knockback > 0: self.collide_update(-math.cos(math.radians(self.postheta))*self.knockback, 0) self.collide_update(0, math.sin(math.radians(self.postheta))*self.knockback) self.knockback = int(self.knockback*0.8) #hurt animation elif animation == 'hurting': self.sprite.texture = self.hurt_texture[0] self.moving = False if self.timer >= self.frame_interval*2: self.side = None self.hurting = False self.timer = 0 SOUND.play_sound(random.choice(self.sounds['damage']), self.dist) if self.state == 'idle' or self.state == 'patrolling' or self.state == 'fleeing': self.face = self.face + self.theta if self.face >= 360: self.face -= 360 self.face = min([0,90,180,270,359], key=lambda x:abs(x-self.face)) #attack animation elif animation == 'attacking': self.sprite.texture = self.hit_texture[self.current_frame] self.moving = False if self.timer >= self.frame_interval: self.current_frame += 1 self.timer = 0 if self.current_frame == len(self.hit_texture): SOUND.play_sound(self.sounds['attack'], self.dist) self.sprite.texture = self.stand_texture[0] self.current_frame = 0 self.attacking = False if random.randint(0,8) != 8: #A chance to miss if SETTINGS.player_armor > 0: SETTINGS.player_health -= int(self.dmg * 0.65) if SETTINGS.player_armor >= self.dmg * 2: SETTINGS.player_armor -= self.dmg * 2 else: SETTINGS.player_armor = 0 else: SETTINGS.player_health -= self.dmg def drop_item(self): texture = 'none.png' possible_drops = ['bullet', 'bullet', 'bullet', 'shell', 'shell', 'health', 'armor', 'ferromag', 'ferromag',] drop = random.choice(possible_drops) effect = random.randint(4, 12) if drop == 'bullet': texture = 'bullet.png' elif drop == 'shell': texture = 'shell.png' elif drop == 'health': texture = 'firstaid.png' elif drop == 'armor': texture = 'kevlar.png' elif drop == 'ferromag': texture = 'ferromag.png' else: print("Error: No texture with name ", drop) SETTINGS.all_items.append(ITEMS.Item(self.map_pos, os.path.join('graphics', 'items', texture), drop, effect)) #stats = { # 'pos': [tile pos], # 'face': degrees, # 'spf': seconds per frame (float), # 'dmg': damage on player, # 'health' : health points, # 'speed': pixels per second, # 'mind': string -> hostile, passive, shy, # 'state': string -> idle, patrolling, # 'atcktype': string -> melee, hitscan, # 'atckrate': chance of attacking - lower = faster # 'id' : unique ID for npcs, # 'filepath' : ('folder', 'folder', 'file.ext'), # 'npc_name' : 'name' -> Used for connecting to a sound pack. # }, ================================================ FILE: PATHFINDING.py ================================================ import SETTINGS import random #There is some whack error handling. This is because this might be used manually by a human and therefore it needs some human-friendly feedback. #This is the A* pathfinding algorithm for NPC movement and more #G = Distance from start #H = Distance to end #F = G + H #open/closedlist syntax = [G, H, F, parent] #Parent is from where the node is checked. def pathfind(start, end): #print(start, end) '''== A* Pathfinding ==\npathfind(start, end) -> Shortest path from start to end\nFormat is list with tile objects''' openlist = {} closedlist = {} path = [] error = False #Reports if a node is outside the map if start[0] > max(SETTINGS.all_tiles, key=lambda x: x.map_pos).map_pos[0] or start[1] > max(SETTINGS.all_tiles, key=lambda x: x.map_pos).map_pos[1]: print("=== WARNING: ===") print("Start point in pathfinding is outiside map!") error = True elif end[0] > max(SETTINGS.all_tiles, key=lambda x: x.map_pos).map_pos[0] or end[1] > max(SETTINGS.all_tiles, key=lambda x: x.map_pos).map_pos[1]: print("=== WARNING: ===") print("End point in pathfinding is outside map!") error = True if not error: start_point = [x for x in SETTINGS.all_tiles if x.map_pos == start][0] end_point = [x for x in SETTINGS.all_tiles if x.map_pos == end][0] #Report errors if SETTINGS.tile_solid[start_point.ID] and (start_point.type != 'hdoor' and start_point.type != 'vdoor'): print("=== WARNING: ===") print("Error! Start point in pathfinding is a solid block!") print(start_point.map_pos, start_point.ID) print() error = True if SETTINGS.tile_solid[end_point.ID] and (end_point.type != 'hdoor' and end_point.type != 'vdoor'): print("=== WARNING: ===") print("Error! End point in pathfinding is a solid block!") print(end_point.map_pos, end_point.ID) print() error = True if error: end_point = [x for x in SETTINGS.all_tiles if x.map_pos == find_near_position(end)] if end_point: end_point = end_point[0] error = False if not error: #f_value has to be determined after creation of node. openlist[start_point] = [0, find_distance(start_point, end_point), 0, None] openlist[start_point][2] = f_value(start_point, openlist) current_point = start_point while current_point != end_point: try: current_point = min(openlist, key=lambda k: (openlist[k][2], openlist[k][1])) except: error = True break closedlist[current_point] = openlist[current_point] del openlist[current_point] #Find adjacent nodes adjacent = [] adj_up = [x for x in SETTINGS.all_tiles if x.map_pos[0] == current_point.map_pos[0] and x.map_pos[1] == current_point.map_pos[1]-1] adj_right = [x for x in SETTINGS.all_tiles if x.map_pos[0] == current_point.map_pos[0]+1 and x.map_pos[1] == current_point.map_pos[1]] adj_down = [x for x in SETTINGS.all_tiles if x.map_pos[0] == current_point.map_pos[0] and x.map_pos[1] == current_point.map_pos[1]+1] adj_left = [x for x in SETTINGS.all_tiles if x.map_pos[0] == current_point.map_pos[0]-1 and x.map_pos[1] == current_point.map_pos[1]] if adj_up: adjacent.append(adj_up[0]) if adj_right: adjacent.append(adj_right[0]) if adj_down: adjacent.append(adj_down[0]) if adj_left: adjacent.append(adj_left[0]) #Add adjecent nodes to openlist if they are not in closedlist and are not solid for adj in adjacent: if (adj.type == 'hdoor' or adj.type == 'vdoor' or not SETTINGS.tile_solid[adj.ID]) and adj not in closedlist: if (adj in openlist and openlist[adj][0] > closedlist[current_point][0]+1) or adj not in openlist: openlist[adj] = [closedlist[current_point][0]+1, find_distance(adj, end_point), 0, current_point] openlist[adj][2] = f_value(adj, openlist) try: while closedlist[current_point][3] != None: path.append(current_point) current_point = closedlist[current_point][3] except: pass path.append(start_point) path = list(reversed(path)) if error: return closedlist else: return path def find_near_position(position): adjacent_tiles = [x for x in SETTINGS.walkable_area if (x.map_pos[0] == position[0] + 1 or x.map_pos[0] == position[0] -1 or x.map_pos[0] == position[0]) and (x.map_pos[1] == position[1] + 1 or x.map_pos[1] == position[1] - 1 or x.map_pos[1] == position[1])] #convert coordinates to a tile chosen_tiles = [x for x in SETTINGS.all_tiles if x.map_pos in adjacent_tiles] if chosen_tiles: return random.choice(chosen_tiles) else: return None def find_distance(point, end): x = point.map_pos[0] + point.map_pos[1] y = end.map_pos[0] + end.map_pos[1] h = abs(x - y) return h def f_value(point, openlist): f = openlist[point][2] = openlist[point][0] + openlist[point][1] return f def random_point(start): #cpos = Current pos closedlist = [] cpos = start closedlist.append(cpos) for x in range(random.randint(50,200)): #adjacent = up, right, down, left adjacent = [[cpos[0], cpos[1]-1], [cpos[0]+1, cpos[1]], [cpos[0], cpos[1]+1], [cpos[0]-1, cpos[1]]] ranadj = random.choice(adjacent) ranadj_tile = [x for x in SETTINGS.all_tiles if ranadj == x.map_pos] if not SETTINGS.tile_solid[ranadj_tile[0].ID] and ranadj not in closedlist: cpos = ranadj closedlist.append(cpos) return cpos ================================================ FILE: PLAYER.py ================================================ #This is the player script. This is where the movement and collision detection of the player is. import SETTINGS import EFFECTS import INVENTORY import SOUND import pygame import math import os class Player: def __init__(self, pos): self.max_speed = SETTINGS.player_speed self.speed = 0 self.angle = SETTINGS.player_angle self.health = SETTINGS.player_health self.real_x = pos[0] self.real_y = pos[1] self.color = SETTINGS.BLUE self.sprite = pygame.Surface([SETTINGS.tile_size / 12, SETTINGS.tile_size / 12]) self.sprite.fill(self.color) self.rect = self.sprite.get_rect() self.rect.x = self.real_x self.rect.y = self.real_y SETTINGS.player_rect = self.rect self.last_pos_tile = None self.mouse = pygame.mouse self.sensitivity = SETTINGS.sensitivity self.gun = 0 self.gunsprites_aim = [] self.gunsprites_shoot = [] SETTINGS.player = self self.collide_list = SETTINGS.all_solid_tiles + SETTINGS.npc_list self.update_collide_list = False self.solid = True self.dead = False self.last_call = 0 self.type = 'player' self.hurt_sound = pygame.mixer.Sound(os.path.join('sounds', 'other', 'damage.ogg')) self.change_level = pygame.mixer.Sound(os.path.join('sounds', 'other', 'next_level.ogg')) self.current_level = SETTINGS.current_level #input variables self.mouse2 = 0 self.inventory = 0 self.esc_pressed = False self.dont_open_menu = False def direction(self, offset, distance): if distance == 0: direction = [math.cos(math.radians(self.angle + offset)), -math.sin(math.radians(self.angle + offset))] else: direction = [(math.cos(math.radians(self.angle + offset))) * distance, (-math.sin(math.radians(self.angle + offset))) * distance] return direction def control(self, canvas): #Make sure the collide list is complete if len(self.collide_list) != len(SETTINGS.all_solid_tiles + SETTINGS.npc_list): self.collide_list = SETTINGS.all_solid_tiles + SETTINGS.npc_list elif self.current_level != SETTINGS.current_level: self.collide_list = SETTINGS.all_solid_tiles + SETTINGS.npc_list self.current_level = SETTINGS.current_level elif self.update_collide_list: self.collide_list = SETTINGS.all_solid_tiles + SETTINGS.npc_list self.update_collide_list = False #Update health if self.health != SETTINGS.player_health and SETTINGS.player_states['heal']: self.health = SETTINGS.player_health key = pygame.key.get_pressed() #Movement controls (WASD) if not SETTINGS.player_states['dead']: #Inventory open if not SETTINGS.player_states['invopen']: if SETTINGS.aiming: self.sensitivity = SETTINGS.sensitivity / 3 self.max_speed = SETTINGS.player_speed / 3 if self.speed > self.max_speed: self.speed = self.max_speed else: self.sensitivity = SETTINGS.sensitivity self.max_speed = SETTINGS.player_speed if key[pygame.K_a] or key[pygame.K_d] or key[pygame.K_w] or key[pygame.K_s]: if self.speed < self.max_speed: self.speed += 50 if self.speed > self.max_speed: self.speed = self.max_speed else: if self.speed > 0: if self.last_call == 0: self.move(self.direction(90, self.speed * 0.8)) elif self.last_call == 1: self.move(self.direction(-90, self.speed * 0.8)) elif self.last_call == 2: self.move(self.direction(0, self.speed)) elif self.last_call == 3: self.move(self.direction(0, -self.speed * 0.5)) self.speed -= 80 if self.speed < 1: self.speed = 0 if key[pygame.K_a]: self.move(self.direction(90, self.speed * 0.8)) self.last_call = 0 if key[pygame.K_d]: self.move(self.direction(-90, self.speed * 0.8)) self.last_call = 1 if key[pygame.K_w]: self.move(self.direction(0, self.speed)) self.last_call = 2 if key[pygame.K_s]: self.move(self.direction(0, -self.speed * 0.5)) self.last_call = 3 SETTINGS.player_states['cspeed'] = self.speed #Shoot gun (Mouse input) if pygame.mouse.get_pressed()[2] and self.mouse2 < 1: SETTINGS.mouse2_btn_active = True self.mouse2 += 1 elif self.mouse2 >= 1: SETTINGS.mouse2_btn_active = False if not pygame.mouse.get_pressed()[2]: self.mouse2 = 0 if pygame.mouse.get_pressed()[0] and not SETTINGS.player_states['dead']: SETTINGS.mouse_btn_active = True else: SETTINGS.mouse_btn_active = False if key[pygame.K_r]: SETTINGS.reload_key_active = True else: SETTINGS.reload_key_active = False #Change gun if key[pygame.K_1] and SETTINGS.inventory['primary']: SETTINGS.next_gun = SETTINGS.inventory['primary'] elif key[pygame.K_2] and SETTINGS.inventory['secondary']: SETTINGS.next_gun = SETTINGS.inventory['secondary'] elif key[pygame.K_3] and SETTINGS.inventory['melee']: SETTINGS.next_gun = SETTINGS.inventory['melee'] #Keep angle in place if self.angle >= 360: self.angle = 0 elif self.angle < 0: self.angle = 359 #Interact if key[pygame.K_e]: if SETTINGS.middle_slice: if SETTINGS.middle_slice_len <= SETTINGS.tile_size*1.5 and (SETTINGS.middle_slice.type == 'vdoor' or SETTINGS.middle_slice.type == 'hdoor'): SETTINGS.middle_slice.sesam_luk_dig_op() elif SETTINGS.middle_slice_len <= SETTINGS.tile_size and SETTINGS.middle_slice.type == 'end' and not SETTINGS.player_states['fade']: SETTINGS.player_states['fade'] = True SETTINGS.changing_level = True SOUND.play_sound(self.change_level, 0) madd = self.mouse.get_rel()[0] * self.sensitivity if madd > 38: madd = 38 elif madd < -38: madd = -38 self.angle -= madd SETTINGS.player_angle = self.angle #Open inventory if key[pygame.K_i] and self.inventory < 1: if SETTINGS.player_states['invopen']: SETTINGS.player_states['invopen'] = False SETTINGS.inv_strings_updated = False else: SETTINGS.player_states['invopen'] = True self.inventory += 1 elif not key[pygame.K_i]: self.inventory = 0 #Use escape to close inventory if key[pygame.K_ESCAPE] and SETTINGS.player_states['invopen']: SETTINGS.player_states['invopen'] = False SETTINGS.inv_strings_updated = False self.dont_open_menu = True elif not key[pygame.K_ESCAPE] and not SETTINGS.player_states['invopen']: self.dont_open_menu = False #Show menu if key[pygame.K_ESCAPE] and not self.dont_open_menu: self.esc_pressed = True elif self.esc_pressed and not self.dont_open_menu: SETTINGS.menu_showing = True self.esc_pressed = False #Is the player dead or taking damage? if self.health > SETTINGS.player_health: SETTINGS.statistics['last dtaken'] += (self.health - SETTINGS.player_health) self.health = SETTINGS.player_health SETTINGS.player_states['hurt'] = True SOUND.play_sound(self.hurt_sound, 0) if SETTINGS.player_health <= 0 and not SETTINGS.godmode: self.dead = True SETTINGS.player_states['dead'] = True if SETTINGS.player_health < 0: SETTINGS.player_health = 0 if SETTINGS.menu_showing or SETTINGS.player_states['invopen']: pygame.event.set_grab(False) self.mouse.set_visible(True) # elif key[pygame.K_q]: # pygame.event.set_grab(True) # self.mouse.set_visible(False) else: pygame.event.set_grab(True) self.mouse.set_visible(False) ## #Change to map view (DEV) ## if key[pygame.K_m]: ## SETTINGS.switch_mode = True ## ## ## #Change FOV (DEV) ## if key[pygame.K_UP]: ## SETTINGS.fov += 2 ## elif key[pygame.K_DOWN]: ## SETTINGS.fov -= 2 ## ## #Screen shake (DEV) ## if key[pygame.K_p]: ## SETTINGS.screen_shake = 20 ## SETTINGS.player_hurt = True #====================================================== def move(self, pos): if SETTINGS.cfps > 5: if pos[0] != 0: self.update(pos[0], 0) if pos[1] != 0: self.update(0, pos[1]) def update(self, x, y): self.real_x += x * SETTINGS.dt self.real_y += y * SETTINGS.dt self.rect.x = self.real_x self.rect.y = self.real_y SETTINGS.player_rect = self.rect tile_hit_list = pygame.sprite.spritecollide(self, self.collide_list, False) #Actually there are not only tiles in the list. NPCs as well. for tile in tile_hit_list: if tile.solid: if x > 0: self.rect.right = tile.rect.left self.real_x = self.rect.x if x < 0: self.rect.left = tile.rect.right self.real_x = self.rect.x if y > 0: self.rect.bottom = tile.rect.top self.real_y = self.rect.y if y < 0: self.rect.top = tile.rect.bottom self.real_y = self.rect.y SETTINGS.player_map_pos = [int(self.rect.centerx / SETTINGS.tile_size), int(self.rect.centery / SETTINGS.tile_size)] #check if player is out of bounds and teleport them back. generator_check_list = [x for x in SETTINGS.walkable_area if x.map_pos == SETTINGS.player_map_pos] if generator_check_list: pos = generator_check_list[0].map_pos else: pos = [] check_list = SETTINGS.walkable_area + SETTINGS.all_solid_tiles out_generator = [x for x in check_list if x.map_pos == SETTINGS.player_map_pos] if out_generator: pos2 = out_generator[0].map_pos else: pos2 = [] if SETTINGS.player_map_pos == pos: SETTINGS.last_player_map_pos = SETTINGS.player_map_pos self.last_pos_tile = generator_check_list[0] elif SETTINGS.player_map_pos != pos2 and SETTINGS.last_player_map_pos: if self.last_pos_tile: SETTINGS.player_map_pos = SETTINGS.last_player_map_pos self.rect.center = self.last_pos_tile.rect.center SETTINGS.player_rect = self.rect self.real_x = self.rect.x self.real_y = self.rect.y def draw(self, canvas): pointer = self.direction(0, 10) p1 = pointer[0] + self.rect.center[0] p2 = pointer[1] + self.rect.center[1] canvas.blit(self.sprite, (self.rect.x/4, self.rect.y/4)) pygame.draw.line(canvas, self.color, (self.rect.center[0]/4, self.rect.center[1]/4), (p1/4, p2/4)) ================================================ FILE: RAYCAST.py ================================================ #This is the script where all the code for raycasting goes. The screen rendering in 2.5D will also go here. import SETTINGS import PLAYER import pygame import math pygame.init() class Slice: def __init__(self, location, surface, width, vh): self.slice = surface.subsurface(pygame.Rect(location, (1, width))).convert() self.rect = self.slice.get_rect(center = (0, SETTINGS.canvas_target_height/2)) self.distance = None self.type = 'slice' self.vh = vh self.xpos = 0 if SETTINGS.shade: self.shade_slice = pygame.Surface(self.slice.get_size()).convert_alpha() sv = SETTINGS.shade_visibility / 10 self.shade_intensity = [sv*1, sv*2, sv*3, sv*4, sv*5, sv*6, sv*7, sv*8, sv*9, sv*10] def update_rect(self, new_slice): self.tempslice = new_slice self.rect = new_slice.get_rect(center = (self.xpos, int(SETTINGS.canvas_target_height/2))) if self.vh == 'v': self.darkslice = pygame.Surface(self.tempslice.get_size()).convert_alpha() self.darkslice.fill((0,0,0,SETTINGS.texture_darken)) if SETTINGS.shade: #Shade intensity table intensity = 0 if self.distance < self.shade_intensity[0]: intensity = 0 elif self.distance < self.shade_intensity[1]: intensity = 0.1 elif self.distance < self.shade_intensity[2]: intensity = 0.2 elif self.distance < self.shade_intensity[3]: intensity = 0.3 elif self.distance < self.shade_intensity[4]: intensity = 0.4 elif self.distance < self.shade_intensity[5]: intensity = 0.5 elif self.distance < self.shade_intensity[6]: intensity = 0.6 elif self.distance < self.shade_intensity[7]: intensity = 0.7 elif self.distance < self.shade_intensity[8]: intensity = 0.8 elif self.distance < self.shade_intensity[9]: intensity = 0.9 else: intensity = 1 self.shade_slice = pygame.Surface(self.tempslice.get_size()).convert_alpha() self.shade_slice.fill((SETTINGS.shade_rgba[0]*intensity, SETTINGS.shade_rgba[1]*intensity, SETTINGS.shade_rgba[2]*intensity, SETTINGS.shade_rgba[3]*intensity)) class Raycast: '''== Raycasting class ==\ncanvas -> Game canvas''' def __init__(self, canvas, canvas2): self.res = SETTINGS.resolution self.fov = SETTINGS.fov self.render = SETTINGS.render self.tile_size = SETTINGS.tile_size self.door_size = self.tile_size / 2 self.wall_width = int(SETTINGS.canvas_target_width / self.res) self.canvas = canvas self.canvas2 = canvas2 self.current_vtile = None self.current_htile = None def calculate(self): self.res = SETTINGS.resolution self.fov = SETTINGS.fov angle = SETTINGS.player_angle step = self.fov / self.res fov = int(self.fov/2) ray = -fov ray_number = 0 for tile in SETTINGS.all_solid_tiles: tile.distance = tile.get_dist(SETTINGS.player_rect.center) while ray < fov: degree = angle - ray if degree <= 0: degree += 360 elif degree > 360: degree -= 360 self.beta = abs(degree - angle) self.cast(SETTINGS.player_rect, degree, ray_number) ray_number += 1 ray += step def find_offset(self, position, ray_number, angle, tile, hv): #position is H_x or V_y if hv == 'v': if tile.type == 'vdoor': offset = abs(int(position - tile.rect.y)) - tile.open else: offset = abs(int(position - tile.rect.y)) else: if tile.type == 'hdoor': offset = abs(int(position - tile.rect.x)) - tile.open else: offset = abs(int(position - tile.rect.x)) #Fuck it. Catch all the crashes. if offset >= SETTINGS.tile_size: offset = SETTINGS.tile_size - 1 return(offset) def check_hit(self, V_hit, H_hit, H_distance, V_distance, full_check): #Break loop if any ray has hit a wall if H_hit and V_hit: return True elif full_check: if H_hit: if H_distance < V_distance: return True elif V_hit: if V_distance < H_distance: return True def cast(self, player_rect, angle, ray_number): H_hit = False V_hit = False H_offset = V_offset = 0 end_pos = (0, 0) angle -= 0.001 #Horizontal if angle < 180: H_y = int(player_rect.center[1] / self.tile_size) * self.tile_size else: H_y = int(player_rect.center[1] / self.tile_size) * self.tile_size + self.tile_size H_x = player_rect.center[0] + (player_rect.center[1] - H_y) / math.tan(math.radians(angle)) #Vertical if angle > 270 or angle < 90: V_x = int(player_rect.center[0] / self.tile_size) * self.tile_size + self.tile_size else: V_x = int(player_rect.center[0] / self.tile_size) * self.tile_size V_y = player_rect.center[1] + (player_rect.center[0] - V_x) * math.tan(math.radians(angle)) #Extend for x in range(0, SETTINGS.render): H_distance = abs((player_rect.center[0] - H_x) / math.cos(math.radians(angle))) V_distance = abs((player_rect.center[0] - V_x) / math.cos(math.radians(angle))) if self.check_hit(V_hit, H_hit, H_distance, V_distance, True): break for tile in SETTINGS.rendered_tiles: if self.check_hit(V_hit, H_hit, H_distance, V_distance, False): break if not H_hit: if (H_y == tile.rect.bottom and H_x >= tile.rect.bottomleft[0] and H_x <= tile.rect.bottomright[0]) and player_rect.centery > tile.rect.bottom: H_hit = True H_texture = SETTINGS.tile_texture[tile.ID] self.current_htile = tile if tile.type == 'hdoor': H_y -= self.door_size H_x += self.door_size / math.tan(math.radians(angle)) H_offset = offset = self.find_offset(H_x, ray_number, angle, tile, 'h') if H_offset < 0: H_hit = False H_y += self.door_size H_x -= self.door_size / math.tan(math.radians(angle)) else: H_offset = offset = self.find_offset(H_x, ray_number, angle, tile, 'h') elif (H_y == tile.rect.top and H_x >= tile.rect.topleft[0] and H_x <= tile.rect.topright[0]) and player_rect.centery < tile.rect.top: H_hit = True H_texture = SETTINGS.tile_texture[tile.ID] self.current_htile = tile if tile.type == 'hdoor': H_y += self.door_size H_x -= self.door_size / math.tan(math.radians(angle)) H_offset = offset = self.find_offset(H_x, ray_number, angle, tile, 'h') if H_offset < 0: H_hit = False H_y -= self.door_size H_x += self.door_size / math.tan(math.radians(angle)) else: H_offset = self.find_offset(H_x, ray_number, angle, tile, 'h') if self.check_hit(V_hit, H_hit, H_distance, V_distance, False): break if not V_hit: if (V_x == tile.rect.left and V_y >= tile.rect.topleft[1] and V_y <= tile.rect.bottomleft[1]) and player_rect.centerx < tile.rect.left: V_hit = True V_texture = SETTINGS.tile_texture[tile.ID] self.current_vtile = tile if tile.type == 'vdoor': V_x += self.door_size V_y -= self.door_size * math.tan(math.radians(angle)) V_offset = self.find_offset(V_y, ray_number, angle, tile, 'v') if V_offset < 0: V_hit = False V_x -= self.door_size V_y += self.door_size * math.tan(math.radians(angle)) else: V_offset = self.find_offset(V_y, ray_number, angle, tile, 'v') elif (V_x == tile.rect.right and V_y >= tile.rect.topright[1] and V_y <= tile.rect.bottomright[1]) and player_rect.centerx > tile.rect.right: V_hit = True V_texture = SETTINGS.tile_texture[tile.ID] self.current_vtile = tile if tile.type == 'vdoor': V_x -= self.door_size V_y += self.door_size * math.tan(math.radians(angle)) V_offset = self.find_offset(V_y, ray_number, angle, tile, 'v') if V_offset < 0: V_hit = False V_x += self.door_size V_y -= self.door_size * math.tan(math.radians(angle)) else: V_offset = self.find_offset(V_y, ray_number, angle, tile, 'v') #Extend actual ray if not H_hit: if angle < 180: H_y -= self.tile_size else: H_y += self.tile_size if angle >= 180: H_x -= self.tile_size / math.tan(math.radians(angle)) else: H_x += self.tile_size / math.tan(math.radians(angle)) if not V_hit: if angle > 270 or angle < 90: # -> V_x += self.tile_size else: V_x -= self.tile_size if angle >= 270 or angle < 90: # <- V_y -= self.tile_size * math.tan(math.radians(angle)) else: V_y += self.tile_size * math.tan(math.radians(angle)) if V_hit and H_hit: H_hit, V_hit = False, False if H_distance < V_distance: end_pos = (H_x, H_y) texture = H_texture tile_len = H_distance offset = H_offset current_tile = self.current_htile H_hit = True else: end_pos = (V_x, V_y) texture = V_texture tile_len = V_distance offset = V_offset current_tile = self.current_vtile V_hit = True elif H_hit and not V_hit: end_pos = (H_x, H_y) texture = H_texture tile_len = H_distance offset = H_offset current_tile = self.current_htile elif V_hit and not H_hit: end_pos = (V_x, V_y) texture = V_texture tile_len = V_distance offset = V_offset current_tile = self.current_vtile else: end_pos = (SETTINGS.player_rect[0],SETTINGS.player_rect[1]) texture = None tile_len = None offset = 0 current_tile = None if V_hit: vh = 'v' else: vh = 'h' #Mode self.control(end_pos, ray_number, tile_len, player_rect, texture, offset, current_tile, vh) def control(self, end_pos, ray_number, tile_len, player_rect, texture, offset, current_tile, vh): if SETTINGS.mode == 1: if tile_len: wall_dist = tile_len * math.cos(math.radians(self.beta)) else: wall_dist = None self.render_screen(ray_number, wall_dist, texture, int(offset), current_tile, vh, end_pos) else: self.draw_line(player_rect, end_pos) def render_screen(self, ray_number, wall_dist, texture, offset, current_tile, vh, end_pos): if wall_dist: wall_height = int((self.tile_size / wall_dist) * (360 / math.tan(math.radians(SETTINGS.fov * 0.8)))) SETTINGS.zbuffer.append(Slice((texture.slices[offset], 0), texture.texture, texture.rect.width, vh)) SETTINGS.zbuffer[ray_number].distance = wall_dist rendered_slice = pygame.transform.scale(SETTINGS.zbuffer[ray_number].slice, (self.wall_width, wall_height)) SETTINGS.zbuffer[ray_number].update_rect(rendered_slice) SETTINGS.zbuffer[ray_number].xpos = ((ray_number) * self.wall_width) else: SETTINGS.zbuffer.append(None) #Middle ray info if ray_number == int(self.res/2): SETTINGS.middle_slice_len = wall_dist SETTINGS.middle_slice = current_tile SETTINGS.middle_ray_pos = end_pos def draw_line(self, player_rect, end_pos): SETTINGS.raylines.append((player_rect.center, end_pos)) ================================================ FILE: README.md ================================================ ## DUGA Raycaster DUGA is a raycaster made in Python3 with Pygame. It is a game built on top of a simple engine of the same name. An example of what the Pygame framework is capable of. The trailer for DUGA [![DUGA Trailer](http://i3.ytimg.com/vi/By_cbsacvTM/maxresdefault.jpg)](https://www.youtube.com/watch?v=qaSFO028JEo) ## My goal Inititally, my goal was to make a first person shooter with roguelike elements and local multiplayer. However, I discarded the multiplayer and kept the other parts. Now I just want the game to be good and I hope people will enjoy playing it! ## Game doesn't start? Are you seeing a white rectangle on a black background? Try downgrading Pygame (2.1.3 should work) ## License If you want to contribute to the project, please contact me somewhere. This project is under Mozilla Public License 2.0 Various sound effects found online from: RA The Sun God - soundbible.com - Attribution 3.0 Mike Koenig - soundbible.com - Attribution 3.0 GoodSoundForYou - soundbible.com - Attribution 3.0 InspectorJ - freesound.org - Attribution 3.0 Kibblesbob - soundbible.com - Attribution 3.0 original_sound - freesound.org - Attribution 3.0 ================================================ FILE: SEGMENTS.py ================================================ #This script contains the level segment objects for level generation. import SETTINGS import pickle import os class Segment: def __init__(self, stats): self.stats = stats self.ID = stats['id'] self.array = stats['array'] self.width = len(self.array[0]) self.height = len(self.array) self.doors = stats['doors'] self.items = stats['items'] self.npcs = stats['npcs'] self.type = stats['type'] self.level_pos = None if 'player_pos' in stats: self.player_pos = stats['player_pos'] else: self.player_pos = None def load_customs(): segments = [] with open(os.path.join('data', 'standardSegments.dat'), 'rb') as file: segments = pickle.load(file) for seg in segments: SETTINGS.segments_list.append(Segment(seg)) #If any custom segments, load those too if os.stat(os.path.join('data', 'customSegments.dat')).st_size != 0: custom_segs = [] with open(os.path.join('data', 'customSegments.dat'), 'rb') as file1: custom_segs = pickle.load(file1) for seg in custom_segs: SETTINGS.segments_list.append(Segment(seg)) #SETTINGS.segments_list.append(Segment({ #'id' : Unique ID, #'npcs' : [([x,y], face, id)], #'items' : [([x,y], id)]), #'array' : [array], #'doors' : [Entrances to the segment in degrees], #})) load_customs() ================================================ FILE: SETTINGS.py ================================================ #Settings for DUGA '''Game settings''' current_level = 0 fps = 31 caption = "DUGA v1.4" mode = 1 volume = 1 music_volume = 1 fullscreen = True menu_showing = True #Below this point are the non-configurable game variables. current_level_size = None changing_level = False quit_game = False game_won = False dt = 0 cfps = 0 statistics = {} play_seconds = 0 '''Level settings''' glevels_size = 4 glevels_amount = 100 #These are non-configurable. levels_list = [] segments_list = [] clevels_list = [] glevels_list = [] tlevels_list = [] seed = None playing_customs = False playing_new = False playing_tutorial = False '''Canvas settings''' canvas_target_width = 700 #700 canvas_target_height = 550 #600 #Below this point are the non-configurable canvas variables. canvas_actual_width = 0 canvas_map_width = None canvas_map_height = None window_height = int(canvas_target_height + (canvas_target_height *0.15)) switch_mode = False axes = (0, 0) screen_shake = 0 '''Raycasting settings''' resolution = 140 fov = 60 render = 16 shade = False shade_rgba = (0,0,0,255) shade_visibility = 1000 #Below this point are the non-configurable raycasting variables. zbuffer = [] middle_slice_len = None middle_slice = None middle_ray_pos = None raylines = [] '''Tile settings''' tile_size = 64 #Below this point are the non-configurable tile variables. all_tiles = [] trigger_tiles = [] all_solid_tiles = [] rendered_tiles = [] walkable_area = [] all_doors = [] end_angle = 0 '''Player settings''' #Speed in px/s player_speed = 256 sensitivity = 0.25 player_angle = 270 og_player_health = 25 og_player_armor = 5 godmode = False #Below this point are the non-configurable player variables. player_health = og_player_health player_armor = og_player_armor player_pos = [0,0] player_map_pos = [] player_rect = None mouse_btn_active = False mouse2_btn_active = False reload_key_active = False aiming = False player_states = { 'dead' : False, 'hurt' : False, 'heal' : False, 'armor' : False, 'invopen' : False, 'fade' : False, 'black' : False, 'title' : False, 'cspeed' : 0, } player = None last_player_map_pos = None '''Texture settings''' #Wall textures and sprites go here. texture_darken = 100 texture_list = [] '''Weapon settings''' #Settings for guns and ammo go here. unlimited_ammo = False #Below this point are non-configurable variables. current_gun = None next_gun = None prev_gun = None gun_list = [] ground_weapon = None '''NPC settings''' #NPC information goes here ignore_player = False #Below this point are non-configurable variables. npc_list = [] npc_types = [] npc_soundpacks = [] '''Inventory settings''' #This is for scripts to access inventory data - Not configurable held_ammo = {} max_ammo = {} inventory = { 'primary': None, 'secondary': None, 'melee': None} item_types = [] inv_strings_updated = False '''Tile configurations''' #Assign each kind of tile with a texture or sprite tile_texture = {} tile_solid = { 0 : False, 1 : True, 2 : True, 3 : True, 4 : True, 5 : True, 6 : True, 7 : True, 8 : True, 9 : True, 10 : False, 11 : True, 12 : True, 13 : True, 14 : True, 15 : True, 16 : True, 17 : True, 18 : True, 19 : True, 20 : True, 21 : True, 22 : True, 23 : True, 24 : True, 25 : True, } tile_visible = { #Sprite tiles are not visible 0 : False, 1 : True, 2 : True, 3 : True, 4 : True, 5 : True, 6 : True, 7 : True, 8 : False, 9 : False, 10 : False, 11 : True, 12 : True, 13 : True, 14 : True, 15 : True, 16 : False, 17 : False, 18 : False, 19 : True, 20 : True, 21 : True, 22 : True, 23 : True, 24 : True, 25 : False, } texture_type = { #air, wall, trigger, sprite 0 : 'air', 1 : 'wall', 2 : 'wall', 3 : 'wall', 4 : 'wall', 5 : 'end', 6 : 'vdoor', 7 : 'hdoor', 8 : 'sprite', 9 : 'sprite', 10 : 'sprite', 11 : 'wall', 12 : 'wall', 13 : 'wall', 14 : 'wall', 15 : 'end', 16 : 'sprite', 17 : 'sprite', 18 : 'sprite', 19 : 'wall', 20 : 'wall', 21 : 'wall', 22 : 'end', 23 : 'vdoor', 24 : 'hdoor', 25 : 'sprite', } '''Sprite settings''' all_sprites = [] '''Item settings''' all_items = [] '''Colours''' BLACK = (0, 0, 0) BLUE = (0, 0, 255) BROWN = (140, 50, 20) DARKGRAY = (50, 50, 50) DARKRED = (80, 0, 0) DARKGREEN = (0, 100, 0) GRAY = (100, 100, 100) GREEN = (0, 255, 0) LIGHTBLUE = (100, 100, 225) LIGHTGRAY = (150, 150, 150) LIGHTGREEN = (100, 255, 100) RED = (255, 0, 0) WHITE = (255, 255, 255) YELLOW = (255, 255, 0) #Create a new tile / sprite tile: #1. Create texture and add it to TEXTURES.py in the marked area for tiles. #2. Give the tile by ID the settings you want in dictionaries above. #3. Make sure that all tiles has a texture or sprite. Invisible tiles can use null.png #4. Note that Sprite tiles are not visible. The tile itself is not rendered. #Note: A tile can be solid, but invisible, but not vice versa #Create a new sprite (NPC): #1. Create the texture and add it to TEXTURES.py in the marked area for NPC's #2. Assign the sprite an ID and make sure the sprite is added to SETTINGS.all_sprites. #3. Add the sprite (ID) to the texture_type dictionary above. Call it 'sprite'. #4. Fill out the arguments to make a sprite (pos, path) #Controls #Overvej at lave justerbare controls temp = [] ================================================ FILE: SOUND.py ================================================ from pygame import mixer import SETTINGS def play_sound(sound, distance): if distance <= SETTINGS.tile_size * SETTINGS.render: if distance >= SETTINGS.tile_size * (SETTINGS.render*0.8): mixer.Sound.set_volume(sound, 0.2 * SETTINGS.volume) elif distance >= SETTINGS.tile_size * (SETTINGS.render*0.4): mixer.Sound.set_volume(sound, 0.5 * SETTINGS.volume) else: mixer.Sound.set_volume(sound, SETTINGS.volume) mixer.Sound.play(sound) ================================================ FILE: SPRITES.py ================================================ import pygame import math import SETTINGS # I noticed, that the sprites are not projected correctly. However, I do not have the guts to fix it. Feel free to take a look. class Sprite: '''== Create a sprite ==\ntexture -> loaded texture | ID -> unique\npos -> px coords | texture_type -> sprite, npc''' def __init__(self, texture, ID, pos, texture_type, parent = None): self.texture = texture self.texture = pygame.transform.scale(self.texture, (SETTINGS.tile_size*2, SETTINGS.tile_size*4)).convert_alpha() self.texture_type = texture_type self.type = texture_type self.ID = ID self.rect = self.texture.get_rect() self.rect_size = (self.rect.width, self.rect.height) self.rect.centerx = pos[0] self.rect.centery = pos[1] self.new_rect = None self.distance = None self.theta = None #If this sprite belongs to an NPC, make the NPC a parent of this sprite #This will help calculating the position of the NPC if self.texture_type == 'npc': self.parent = parent else: self.parent = None SETTINGS.all_sprites.append(self) def get_pos(self, canvas): angle = SETTINGS.player_angle fov = SETTINGS.fov xpos = self.rect.centerx - SETTINGS.player_rect[0] ypos = SETTINGS.player_rect[1] - self.rect.centery dist = math.sqrt(xpos*xpos + ypos*ypos) if dist == 0: dist += 0.0001 self.distance = dist thetaTemp = math.atan2(ypos, xpos) thetaTemp = math.degrees(thetaTemp) if thetaTemp < 0: thetaTemp += 360 self.theta = thetaTemp yTmp = angle + (fov/2) - thetaTemp if thetaTemp > 270 and angle < 90: yTmp = angle + (fov/2) - thetaTemp + 360 if angle > 270 and thetaTemp < 90: yTmp = angle + (fov/2) - thetaTemp - 360 xTmp = yTmp * SETTINGS.canvas_actual_width / fov sprite_height = int((self.rect.height / dist) * (100 / math.tan(math.radians(fov * 0.8)))) if sprite_height > 2500: sprite_height = 2500 sprite_width = int(self.rect.width / self.rect.height * sprite_height) if xTmp > (0 - sprite_width) and xTmp < (SETTINGS.canvas_actual_width + sprite_width): SETTINGS.zbuffer.append(self) if self.parent: self.parent.in_canvas = True else: if self.parent: self.parent.in_canvas = False self.new_size = pygame.transform.scale(self.texture, (sprite_width, sprite_height)) self.new_rect = self.new_size.get_rect() self.new_rect.center = (xTmp, SETTINGS.canvas_target_height/2) if self.parent: self.parent.hit_rect = self.new_rect def draw(self, canvas): canvas.blit(self.new_size, self.new_rect) def update_pos(self, pos): self.rect.centerx = pos[0] self.rect.centery = pos[1] ================================================ FILE: TEXT.py ================================================ import pygame pygame.font.init() class Text: def __init__(self, posx, posy, string, color, font, size): self.posx = posx self.posy = posy self.string = string self.color = color self.size = size self.font = pygame.font.Font(font, self.size) self.layout = self.font.render(self.string, True, self.color) def draw(self, canvas): #Draw the text - Call each frame. canvas.blit(self.layout,(self.posx, self.posy)) def update_string(self, string): #Update the string that will be shown if needed. self.layout = self.font.render(string, True, self.color) self.string = string def update_pos(self, x, y): #Updates the position of the text. self.posx = x self.posy = y ================================================ FILE: TEXTURES.py ================================================ #Textures for tiles: Walls and sprites. import os #RYD OP I DEN HER TIL SIDST all_textures = [ os.path.join('graphics', 'tiles', 'null.png'), #Air #0 #-- Wood theme -- # Walls os.path.join('graphics', 'tiles', 'walls', 'wood_wall.png'), #1 os.path.join('graphics', 'tiles', 'walls', 'wood_painting.png'), #2 os.path.join('graphics', 'tiles', 'walls', 'wood_fireplace.png'), #3 os.path.join('graphics', 'tiles', 'walls', 'wood_books.png'), #4 os.path.join('graphics', 'tiles', 'walls', 'wood_end.png'), #5 # Doors os.path.join('graphics', 'tiles', 'walls', 'wood_door.png'), #6 os.path.join('graphics', 'tiles', 'walls', 'wood_door.png'), #7 # Sprites os.path.join('graphics', 'tiles', 'sprites', 'pillar.png'), #8 os.path.join('graphics', 'tiles', 'sprites', 'table.png'), #9 os.path.join('graphics', 'tiles', 'sprites', 'lysekrone.png'), #10 #-- Stone theme -- # Walls os.path.join('graphics', 'tiles', 'walls', 'stone_wall.png'), #11 os.path.join('graphics', 'tiles', 'walls', 'stone_vent.png'), #12 os.path.join('graphics', 'tiles', 'walls', 'stone_wall_crack.png'), #13 os.path.join('graphics', 'tiles', 'walls', 'stone_vase.png'), #14 os.path.join('graphics', 'tiles', 'walls', 'stone_end.png'), #15 # Sprites os.path.join('graphics', 'tiles', 'sprites', 'lysestage.png'), #16 os.path.join('graphics', 'tiles', 'sprites', 'barrel.png'), #17 os.path.join('graphics', 'tiles', 'sprites', 'stone_pillar.png'), #18 #-- Baroque theme -- # Walls os.path.join('graphics', 'tiles', 'walls', 'baroque.png'), #19 os.path.join('graphics', 'tiles', 'walls', 'baroque_lamps.png'), #20 os.path.join('graphics', 'tiles', 'walls', 'baroque_worn.png'), #21 os.path.join('graphics', 'tiles', 'walls', 'baroque_end.png'), #22 # Doors os.path.join('graphics', 'tiles', 'walls', 'baroque_door.png'), #23 os.path.join('graphics', 'tiles', 'walls', 'baroque_door.png'), #24 # Sprites os.path.join('graphics', 'tiles', 'sprites', 'fern.png'), #25 ] ================================================ FILE: TUTORIAL.py ================================================ import pygame import TEXT import SETTINGS class Controller: def __init__(self): self.text = TEXT.Text(0,0, "INTET!", SETTINGS.BLACK, "DUGAFONT.ttf", 26) #Strings self.welcome = { 'string' : "WELCOME TO DUGA! PRESS 'E' TO OPEN THE DOOR", 'tiles' : [[1,11], [1,10], [2,10], [2,11], [2,12], [3,10], [3,11], [3,12], [4,10], [4,11], [4,12], [5,11]] } self.items1 = { 'string' : "PICK UP THE ARMOR AND HEALTH ON THE GROUND", 'tiles' : [[2,6],[3,6],[4,6], [2,7],[3,7],[4,7],] } self.arrow = { 'string' : "FOLLOW THE GREEN ARROW IN THE LOWER CORNER", 'tiles' : [[2,5],[3,5],[4,5]] } self.exits = { 'string' : "PRESS 'E' ON THE EXIT TO FINISH THE FIRST TUTORIAL", 'tiles': [[2,1],[3,1],[4,1], [2,2],[3,2],[4,2],] } self.second = { 'string' : "DUGA HAS PRIMARY, SECONDARY AND MELEE WEPONS", 'tiles' : [[2,17],[3,16],[3,17],[3,18], [4,16], [4,17]] } self.weapons = { 'string' : "PICK UP THE WEAPONS AND SWITCH WITH '1, 2, 3'", 'tiles' : [ [2,12], [3,12], [4,12], [1,13],[2,13], [3,13], [4,13],[5,13], [1,14],[2,14], [3,14], [4,14],[5,14]] } self.ammo = { 'string' : "EACH GUN HAS A TYPE OF AMMO. RELOAD WITH 'R'", 'tiles' : [[2,7], [3,7], [4,7], [2,8], [3,8], [4,8], [2,9], [3,9], [4,9]] } self.compare = { 'string' : "FOR BETTER AIM, RIGHT CLICK. GO STAND ON THE GUN", 'tiles' : [[2,3], [4,3], [2,4], [3,4], [4,4], [2,5], [3,5], [4,5]] } self.gauss = { 'string' : "OPEN INVENTORY WITH 'I' AND CLICK ON GROUND SLOT", 'tiles' : [[3,3]] } self.combat = { 'string' : "TIME FOR SOME COMBAT!", 'tiles' : [[2,14], [3,14], [2,15], [3,15], [4,15], [2,16], [3,16], [4,16],] } self.items = { 'string' : "ENEMIES HAVE DIFFERENT BEHAVIOURS. NOW, GEAR UP!", 'tiles' : [[2,10], [3,10], [4,10], [2,11], [3,11], [4,11], [2,12], [3,12], [4,12],] } self.enemy = { 'string' : "KILL HIM!! LEFT CLICK TO SHOOT!", 'tiles' : [ [2,4], [3,4], [4,4],[5,4], [1,5],[2,5], [3,5], [4,5],[5,5], [1,6],[2,6], [3,6], [4,6],[5,6], [1,7],[2,7], [3,7], [4,7],[5,7], [1,8],[2,8], [3,8], [4,8],[5,8],] } self.done = { 'string' : "WELL DONE! NOW, LET'S PLAY!", 'tiles' : [[2,1], [3,1], [4,4], [3,2], [4,2],] } def control(self, canvas): if SETTINGS.current_level == 0: if SETTINGS.player_map_pos in self.welcome['tiles']: self.draw(self.welcome, canvas) elif SETTINGS.player_map_pos in self.items1['tiles']: self.draw(self.items1, canvas) elif SETTINGS.player_map_pos in self.arrow['tiles']: self.draw(self.arrow, canvas) elif SETTINGS.player_map_pos in self.exits['tiles']: self.draw(self.exits, canvas) elif SETTINGS.current_level == 1: if SETTINGS.player_map_pos in self.second['tiles']: self.draw(self.second, canvas) elif SETTINGS.player_map_pos in self.weapons['tiles']: self.draw(self.weapons, canvas) elif SETTINGS.player_map_pos in self.ammo['tiles']: self.draw(self.ammo, canvas) elif SETTINGS.player_map_pos in self.compare['tiles']: self.draw(self.compare, canvas) elif SETTINGS.player_map_pos in self.gauss['tiles']: self.draw(self.gauss, canvas) elif SETTINGS.current_level == 2: if SETTINGS.player_map_pos in self.combat['tiles']: self.draw(self.combat, canvas) elif SETTINGS.player_map_pos in self.items['tiles']: self.draw(self.items, canvas) elif SETTINGS.player_map_pos in self.enemy['tiles']: self.draw(self.enemy, canvas) elif SETTINGS.player_map_pos in self.done['tiles']: self.draw(self.done, canvas) def draw(self, string, canvas): self.text.update_string(string['string']) self.text.update_pos((SETTINGS.canvas_actual_width/2)-(self.text.layout.get_width()/2), 480) self.box = pygame.Surface((self.text.layout.get_width()+6, self.text.layout.get_height()+6)).convert_alpha() self.box.fill((255,255,255,180)) canvas.blit(self.box, (self.text.posx-3, self.text.posy-3)) self.text.draw(canvas) ================================================ FILE: data/CrashReport.log ================================================ WARNING:root:DUGA has crashed. Please send this report to MaxwellSalmon, so he can fix it. ERROR:root:Error message: Traceback (most recent call last): File "C:\Python34\Portfolio\PyGame\DUGA\MAIN.py", line 447, in main_loop render_screen(gameCanvas.canvas) File "C:\Python34\Portfolio\PyGame\DUGA\MAIN.py", line 295, in render_screen EFFECTS.render(gameCanvas.canvas) File "C:\Python34\Portfolio\PyGame\DUGA\EFFECTS.py", line 48, in render player_armor(canvas) File "C:\Python34\Portfolio\PyGame\DUGA\EFFECTS.py", line 101, in player_armor armor.fill((0, 0, 225, armor_intensity)) TypeError: invalid color argument