Full Code of MaxwellSalmon/DUGA for AI

master 8e9ff611a3a8 cached
109 files
344.0 KB
89.2k tokens
205 symbols
1 requests
Download .txt
Showing preview only (360K chars total). Download the full file or copy to clipboard to get everything.
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
     
Download .txt
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
Download .txt
SYMBOL INDEX (205 symbols across 22 files)

FILE: EFFECTS.py
  function render (line 40) | def render(canvas):
  function screen_shake (line 55) | def screen_shake():
  function player_hurt (line 65) | def player_hurt(canvas):
  function player_heal (line 83) | def player_heal(canvas):
  function player_armor (line 96) | def player_armor(canvas):
  function fade_black (line 108) | def fade_black(canvas):
  function show_title (line 130) | def show_title(canvas):

FILE: ENTITIES.py
  function load_guns (line 13) | def load_guns():
  function load_npc_types (line 567) | def load_npc_types():
  function load_npc_sounds (line 898) | def load_npc_sounds():
  function spawn_npcs (line 956) | def spawn_npcs():
  function load_item_types (line 976) | def load_item_types():
  function spawn_items (line 1227) | def spawn_items():

FILE: GENERATION.py
  class Generator (line 10) | class Generator:
    method __init__ (line 12) | def __init__(self):
    method create_seed (line 96) | def create_seed(self, seed):
    method generate_levels (line 107) | def generate_levels(self, amount, size, *seed):
    method generate_level (line 118) | def generate_level(self, size, *seed):
    method rotate_segment (line 237) | def rotate_segment(self, segment):
    method suitable_segment (line 311) | def suitable_segment(self, array, prev, current, nex, special):
    method kill_dead_ends (line 386) | def kill_dead_ends(self, array):
    method translate_map (line 439) | def translate_map(self, segs, size):
    method place_random_items (line 491) | def place_random_items(self):
    method spawn_random_npcs (line 532) | def spawn_random_npcs(self):

FILE: GUNS.py
  class Gun (line 10) | class Gun:
    method __init__ (line 15) | def __init__(self, textures, stats, sounds, aim_pos):#  damage, spread...
    method update_rect (line 84) | def update_rect(self, accuracy_added):
    method aim_animation (line 88) | def aim_animation(self):
    method shoot_animation (line 122) | def shoot_animation(self):
    method damage (line 196) | def damage(self):
    method reload_animation (line 237) | def reload_animation(self):
    method draw (line 268) | def draw(self, canvas):
    method re_init (line 360) | def re_init(self):

FILE: HUD.py
  class hud (line 6) | class hud:
    method __init__ (line 8) | def __init__(self):
    method render (line 41) | def render(self, canvas):

FILE: INVENTORY.py
  class inventory (line 9) | class inventory:
    method __init__ (line 12) | def __init__(self, ammo_dict):
    method draw (line 123) | def draw(self, canvas):
    method draw_menu (line 228) | def draw_menu(self, canvas):
    method ammo_selection (line 337) | def ammo_selection(self, slot, canvas):
    method compare_weapons (line 375) | def compare_weapons(self, comp):

FILE: ITEMS.py
  class Item (line 8) | class Item:
    method __init__ (line 10) | def __init__(self, pos, sprite, item_type, effect):
    method update (line 21) | def update(self):

FILE: LEVELS.py
  class Level (line 5) | class Level:
    method __init__ (line 7) | def __init__(self, stats):

FILE: LevelEditor.py
  function determine_mode (line 27) | def determine_mode():
  function determine_type (line 47) | def determine_type():
  function determine_size (line 61) | def determine_size():
  function what_now (line 77) | def what_now():
  class Canvas (line 149) | class Canvas:
    method __init__ (line 151) | def __init__(self, width, height):
    method load_items (line 260) | def load_items(self):
    method load_npcs (line 288) | def load_npcs(self):
    method draw (line 335) | def draw(self):
    method export (line 410) | def export(self, gc, sc, printit):
    method change_id (line 452) | def change_id(self):
    method change_item (line 477) | def change_item(self):
    method change_npc (line 499) | def change_npc(self):
    method change_face (line 521) | def change_face(self):
    method click_doors (line 543) | def click_doors(self):
    method change_segtype (line 585) | def change_segtype(self):
  class Map (line 605) | class Map:
    method __init__ (line 607) | def __init__(self, width, height):
    method draw (line 625) | def draw(self, canvas):
    method add_tile (line 639) | def add_tile(self):
    method remove_tile (line 650) | def remove_tile(self):
    method add_item (line 662) | def add_item(self, item, item_id):
    method add_npc (line 670) | def add_npc(self, npc, npc_id):
    method add_start (line 680) | def add_start(self):
  class Tile (line 688) | class Tile:
    method __init__ (line 690) | def __init__(self, ID, pos):
  class SaveLoad (line 707) | class SaveLoad:
    method __init__ (line 709) | def __init__(self):
    method print_slots (line 724) | def print_slots(self, mtype):
    method save_map (line 737) | def save_map(self, gc, sc):
    method load_map (line 798) | def load_map(self):
    method del_map (line 903) | def del_map(self, index, mtype):
  function main_loop (line 933) | def main_loop():
  function new (line 963) | def new():
  function start (line 966) | def start():

FILE: MAIN.py
  class Load (line 39) | class Load:
    method load_resources (line 41) | def load_resources(self):
    method get_canvas_size (line 77) | def get_canvas_size(self):
    method load_entities (line 88) | def load_entities(self):
    method load_custom_levels (line 93) | def load_custom_levels(self):
    method load_new_level (line 107) | def load_new_level(self):
  class Texture (line 145) | class Texture:
    method __init__ (line 147) | def __init__(self, file_path, ID):
    method create_slices (line 155) | def create_slices(self): # Fills list - Nothing else
  class Canvas (line 163) | class Canvas:
    method __init__ (line 165) | def __init__(self, width, height):
    method change_mode (line 191) | def change_mode(self):
    method draw (line 200) | def draw(self):
  function sort_distance (line 217) | def sort_distance(x):
  function sort_atan (line 223) | def sort_atan(x):
  function render_screen (line 248) | def render_screen(canvas):
  function update_game (line 308) | def update_game():
  function calculate_statistics (line 345) | def calculate_statistics():
  function main_loop (line 371) | def main_loop():

FILE: MAP.py
  class Map (line 11) | class Map:
    method __init__ (line 13) | def __init__(self, array):
    method draw (line 33) | def draw(self, canvas):
    method move_inaccessible_entities (line 38) | def move_inaccessible_entities(self):
  class Tile (line 77) | class Tile:
    method __init__ (line 79) | def __init__(self, ID, pos, map_pos):
    method draw (line 118) | def draw(self, canvas):
    method get_dist (line 121) | def get_dist(self, pos, *called):
    method sesam_luk_dig_op (line 131) | def sesam_luk_dig_op(self):

FILE: MENU.py
  class Controller (line 12) | class Controller:
    method __init__ (line 14) | def __init__(self, canvas):
    method load_settings (line 32) | def load_settings(self):
    method save_settings (line 43) | def save_settings(self):
    method check_mouse (line 51) | def check_mouse(self):
    method control (line 55) | def control(self):
  class Menu (line 157) | class Menu:
    method __init__ (line 159) | def __init__(self, title):
  class MainMenu (line 166) | class MainMenu(Menu):
    method __init__ (line 168) | def __init__(self):
    method draw (line 216) | def draw(self, canvas):
    method logo_animation (line 225) | def logo_animation(self, canvas):
  class NewMenu (line 240) | class NewMenu(Menu):
    method __init__ (line 242) | def __init__(self, settings):
    method draw (line 258) | def draw(self, canvas):
    method reset_inventory (line 270) | def reset_inventory(self):
    method draw_no_levels (line 304) | def draw_no_levels(self, canvas):
  class OptionsMenu (line 313) | class OptionsMenu(Menu):
    method __init__ (line 315) | def __init__(self, settings):
    method update_strings (line 343) | def update_strings(self):
    method control_options (line 366) | def control_options(self):
    method draw (line 404) | def draw(self, canvas):
  class ScoreMenu (line 418) | class ScoreMenu(Menu):
    method __init__ (line 420) | def __init__(self):
    method draw (line 503) | def draw(self, canvas):
  class CreditsMenu (line 531) | class CreditsMenu(Menu):
    method __init__ (line 533) | def __init__(self):
    method draw (line 567) | def draw(self, canvas, show):
  class SupportSplash (line 583) | class SupportSplash:
    method __init__ (line 585) | def __init__(self):
    method draw (line 610) | def draw(self, canvas):
  class GMainMenu (line 627) | class GMainMenu(Menu):
    method __init__ (line 629) | def __init__(self):
    method draw (line 637) | def draw(self, canvas):
  class Button (line 644) | class Button:
    method __init__ (line 646) | def __init__(self, xywh, text):
    method draw (line 660) | def draw(self, canvas):
    method get_clicked (line 670) | def get_clicked(self):

FILE: MUSIC.py
  class Music (line 6) | class Music:
    method __init__ (line 8) | def __init__(self):
    method control_music (line 23) | def control_music(self):

FILE: NPC.py
  class Npc (line 13) | class Npc:
    method __init__ (line 15) | def __init__(self, stats, sounds, texture):
    method think (line 131) | def think(self):
    method render (line 225) | def render(self):
    method round_up (line 335) | def round_up(self, a):
    method detect_player (line 338) | def detect_player(self):
    method collide_update (line 396) | def collide_update(self, x, y):
    method move (line 430) | def move(self):
    method idle (line 539) | def idle(self):
    method attack (line 563) | def attack(self):
    method animate (line 674) | def animate(self, animation):
    method drop_item (line 760) | def drop_item(self):

FILE: PATHFINDING.py
  function pathfind (line 12) | def pathfind(start, end):
  function find_near_position (line 112) | def find_near_position(position):
  function find_distance (line 124) | def find_distance(point, end):
  function f_value (line 131) | def f_value(point, openlist):
  function random_point (line 135) | def random_point(start):

FILE: PLAYER.py
  class Player (line 11) | class Player:
    method __init__ (line 13) | def __init__(self, pos):
    method direction (line 56) | def direction(self, offset, distance):
    method control (line 63) | def control(self, canvas):
    method move (line 255) | def move(self, pos):
    method update (line 262) | def update(self, x, y):
    method draw (line 318) | def draw(self, canvas):

FILE: RAYCAST.py
  class Slice (line 10) | class Slice:
    method __init__ (line 12) | def __init__(self, location, surface, width, vh):
    method update_rect (line 25) | def update_rect(self, new_slice):
  class Raycast (line 64) | class Raycast:
    method __init__ (line 66) | def __init__(self, canvas, canvas2):
    method calculate (line 80) | def calculate(self):
    method find_offset (line 108) | def find_offset(self, position, ray_number, angle, tile, hv):
    method check_hit (line 127) | def check_hit(self, V_hit, H_hit, H_distance, V_distance, full_check):
    method cast (line 142) | def cast(self, player_rect, angle, ray_number):
    method control (line 313) | def control(self, end_pos, ray_number, tile_len, player_rect, texture,...
    method render_screen (line 326) | def render_screen(self, ray_number, wall_dist, texture, offset, curren...
    method draw_line (line 345) | def draw_line(self, player_rect, end_pos):

FILE: SEGMENTS.py
  class Segment (line 6) | class Segment:
    method __init__ (line 8) | def __init__(self, stats):
  function load_customs (line 24) | def load_customs():

FILE: SOUND.py
  function play_sound (line 4) | def play_sound(sound, distance):

FILE: SPRITES.py
  class Sprite (line 7) | class Sprite:
    method __init__ (line 9) | def __init__(self, texture, ID, pos, texture_type, parent = None):
    method get_pos (line 36) | def get_pos(self, canvas):
    method draw (line 84) | def draw(self, canvas):
    method update_pos (line 87) | def update_pos(self, pos):

FILE: TEXT.py
  class Text (line 5) | class Text:
    method __init__ (line 7) | def __init__(self, posx, posy, string, color, font, size):
    method draw (line 16) | def draw(self, canvas):
    method update_string (line 20) | def update_string(self, string):
    method update_pos (line 25) | def update_pos(self, x, y):

FILE: TUTORIAL.py
  class Controller (line 5) | class Controller:
    method __init__ (line 7) | def __init__(self):
    method control (line 98) | def control(self, canvas):
    method draw (line 133) | def draw(self, string, canvas):
Condensed preview — 109 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (362K chars).
[
  {
    "path": ".gitignore",
    "chars": 48,
    "preview": "__pycache__/\nAndet/\nbuild/\ndist/\npyinstaller.exe"
  },
  {
    "path": "EFFECTS.py",
    "chars": 5751,
    "preview": "import pygame\nimport SETTINGS\nimport ITEMS\nimport TEXT\nimport random\n\ntitle = TEXT.Text(0,0, \"None :-)\", SETTINGS.BLACK,"
  },
  {
    "path": "ENTITIES.py",
    "chars": 54903,
    "preview": "import SETTINGS\nimport GUNS\nimport NPC\nimport ITEMS\n\nfrom os import *\nimport pygame\nimport copy\nimport random\n\n#When cre"
  },
  {
    "path": "GENERATION.py",
    "chars": 23201,
    "preview": "import random\nimport math\nimport copy\nimport os\nimport SEGMENTS\nimport SETTINGS\nimport TEXTURES\nimport LEVELS\n\nclass Gen"
  },
  {
    "path": "GUNS.py",
    "chars": 18891,
    "preview": "#GUN CLASS - See self.stats structure in the bottom of script.\n\nimport SETTINGS\nimport SOUND\nimport pygame\nimport random"
  },
  {
    "path": "HUD.py",
    "chars": 3243,
    "preview": "import SETTINGS\nimport TEXT\nimport pygame\nimport os\n\nclass hud:\n\n    def __init__(self):\n        self.health = SETTINGS."
  },
  {
    "path": "INVENTORY.py",
    "chars": 21263,
    "preview": "#This class / script is not a part of the engine itself. This means it will not\n#Be as easy to modify as the other parts"
  },
  {
    "path": "ITEMS.py",
    "chars": 3967,
    "preview": "import SETTINGS\nimport SPRITES\nimport SOUND\nimport pygame\nimport os\n\n\nclass Item:\n\n    def __init__(self, pos, sprite, i"
  },
  {
    "path": "LEVELS.py",
    "chars": 8180,
    "preview": "#Level classes for DUGA\n\nimport SETTINGS\n\nclass Level:\n    \n    def __init__(self, stats):\n        self.stats = stats\n  "
  },
  {
    "path": "LICENSE.txt",
    "chars": 16725,
    "preview": "Mozilla Public License Version 2.0\n==================================\n\n1. Definitions\n--------------\n\n1.1. \"Contributor\""
  },
  {
    "path": "LevelEditor.py",
    "chars": 36790,
    "preview": "#This is the map editor. It does not have anything to do with the game.\n#It needs access to LEVELS.py, TEXTURES.py, ENTI"
  },
  {
    "path": "MAIN.py",
    "chars": 20412,
    "preview": "#This is the MAIN script of DUGA. This is where the main loop is located and this is where all resources are loaded.\n#Al"
  },
  {
    "path": "MAIN.spec",
    "chars": 718,
    "preview": "# -*- mode: python -*-\n\nblock_cipher = None\n\n\na = Analysis(['MAIN.py'],\n             pathex=['C:\\\\Python34\\\\Portfolio\\\\P"
  },
  {
    "path": "MAP.py",
    "chars": 7443,
    "preview": "#This is the script that controls the game maps. It makes the array and the tiles.\n\nimport SETTINGS\nimport SPRITES\nimpor"
  },
  {
    "path": "MENU.py",
    "chars": 30701,
    "preview": "import pygame\nimport pickle\nimport os\nimport copy\nimport random\nimport SETTINGS\nimport TEXT\nimport SOUND\n\nSETTINGS.menu_"
  },
  {
    "path": "MUSIC.py",
    "chars": 2408,
    "preview": "import pygame\nimport os\nimport SETTINGS\nimport SOUND\n\nclass Music:\n\n    def __init__(self):\n        self.settings_volume"
  },
  {
    "path": "Manuals.txt",
    "chars": 3194,
    "preview": "  _____    _    _    _____                  __  __                                   _        \n |  __ \\  | |  | |  / ___"
  },
  {
    "path": "NPC.py",
    "chars": 40236,
    "preview": "import SETTINGS\nimport SPRITES\nimport PATHFINDING\nimport ITEMS\nimport SOUND\nimport os\nimport random\nimport math\nimport p"
  },
  {
    "path": "PATHFINDING.py",
    "chars": 6336,
    "preview": "import SETTINGS\nimport random\n\n#There is some whack error handling. This is because this might be used manually by a hum"
  },
  {
    "path": "PLAYER.py",
    "chars": 12949,
    "preview": " #This is the player script. This is where the movement and collision detection of the player is.\n\nimport SETTINGS\nimpor"
  },
  {
    "path": "RAYCAST.py",
    "chars": 14130,
    "preview": "#This is the script where all the code for raycasting goes. The screen rendering in 2.5D will also go here.\n\nimport SETT"
  },
  {
    "path": "README.md",
    "chars": 1169,
    "preview": "## DUGA Raycaster\n\nDUGA is a raycaster made in Python3 with Pygame. It is a game built on top of a simple engine of the "
  },
  {
    "path": "SEGMENTS.py",
    "chars": 1481,
    "preview": "#This script contains the level segment objects for level generation.\nimport SETTINGS\nimport pickle\nimport os\n\nclass Seg"
  },
  {
    "path": "SETTINGS.py",
    "chars": 5555,
    "preview": "#Settings for DUGA\n\n'''Game settings'''\ncurrent_level = 0\nfps = 31\ncaption = \"DUGA v1.4\"\nmode = 1\nvolume = 1\nmusic_volum"
  },
  {
    "path": "SOUND.py",
    "chars": 506,
    "preview": "from pygame import mixer\nimport SETTINGS\n\ndef play_sound(sound, distance):\n    if distance <= SETTINGS.tile_size * SETTI"
  },
  {
    "path": "SPRITES.py",
    "chars": 3159,
    "preview": "import pygame\nimport math\nimport SETTINGS\n\n # I noticed, that the sprites are not projected correctly. However, I do not"
  },
  {
    "path": "TEXT.py",
    "chars": 810,
    "preview": "import pygame\n\npygame.font.init()\n\nclass Text:\n\n    def __init__(self, posx, posy, string, color, font, size):\n        s"
  },
  {
    "path": "TEXTURES.py",
    "chars": 2086,
    "preview": "#Textures for tiles: Walls and sprites.\nimport os\n\n#RYD OP I DEN HER TIL SIDST\nall_textures = [\n    os.path.join('graphi"
  },
  {
    "path": "TUTORIAL.py",
    "chars": 5386,
    "preview": "import pygame\nimport TEXT\nimport SETTINGS\n\nclass Controller:\n\n    def __init__(self): \n        self.text = TEXT.Text(0,0"
  },
  {
    "path": "data/CrashReport.log",
    "chars": 641,
    "preview": "WARNING:root:DUGA has crashed. Please send this report to MaxwellSalmon, so he can fix it.\nERROR:root:Error message: \nTr"
  }
]

// ... and 79 more files (download for full content)

About this extraction

This page contains the full source code of the MaxwellSalmon/DUGA GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 109 files (344.0 KB), approximately 89.2k tokens, and a symbol index with 205 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!