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