Shape Game


version = "1.3"
#   add pixilated font
#   pg.font.Font("prstartk.ttf"
import asyncio
import random
import math
import pygame as pg

pg.init()
clock = pg.time.Clock()
WIDTH, HEIGHT = 500, 500
screen = pg.display.set_mode((WIDTH, HEIGHT))

dots = []
for i in range(100):
  dots.append((random.randint(-1000, 1000), random.randint(-1000, 1000)))

touch=False
BossesSpawned = 0
StartingRound = 0
dificulty = "none"
zoneTarget = [0, 0]
zoneXY = [200, 0]
zoneRadius = 400
zoneSpeed = 1
timeLeft = 20000
DoubleMoney = 1
cansprint = False
multiShot = 1
round = 0
enemySpeed = 1
sprint = 0
reloadtime = 1
health = 5
maxhealth = 5
totalMoney = 0
money = 0
playerSpeed = 2
playerPos = [0, 0]
screenPos = [0, 0]
colors = [(255, 255, 255), (255, 255, 0), (0, 0, 0), (239, 245, 66),
          (66, 245, 144), (215, 64, 245), (245, 155, 64), (237, 57, 57),
          (57, 237, 201), (96, 181, 87), (15, 11, 230)]
MenuScreen = 0
MenuText = [["Easy", "Normal", "Hard", "Hardcore", "Custom", "Touch"],
            ["Easy", "Normal", "Hard", "Hardcore"],
            [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]]
MenuText2 = [
    "Select a dificulty", "Select a Custom dificulty",
    "Select a round to start on"
]
MenuText3 = [
    "Start with infinite money", "Normal dificulty [recommended]",
    "Enemys are quite a lot are faster", "Faster enemys and only one life",
    "Select a custom starting round", "Toggles Touchscreen mode"
]
MouseDown = False


def menu():
  global MenuScreen, dificulty, StartingRound, MainMenu, MouseDown, round, touch
  #pg.mouse.set_cursor(pg.SYSTEM_CURSOR_HAND)
  screen.fill((0, 0, 20))
  drawDots()
  drawZone()
  mouse_x, mouse_y = pg.mouse.get_pos()

  transparent_black_surface = pg.Surface((WIDTH, HEIGHT))
  transparent_black_surface.set_alpha(50)
  transparent_black_surface.fill((0, 0, 0))
  screen.blit(transparent_black_surface, (0, 0))

  font = pg.font.Font("prstartk.ttf",
                      45 - (abs(mouse_x + mouse_y - WIDTH // 2)) // 60)
  text = font.render(f"SHAPE GAME", True, (255, 255, 255))
  screen.blit(text, (WIDTH / 2 - text.get_width() / 2, text.get_height()))

  font = pg.font.Font("prstartk.ttf", 15)
  text = font.render(MenuText2[MenuScreen], True, (255, 255, 255))
  screen.blit(text,
              (WIDTH / 2 - text.get_width() / 2, text.get_height() + 140))

  card_height = 100
  card_spacing = 10
  border_size = 5
  if MenuScreen == 2:
    card_width = WIDTH // 7 - card_spacing
  else:
    card_width = WIDTH // 3 - card_spacing

  for i in range(len(MenuText[MenuScreen])):
    if MenuScreen == 2:
      if i < 7:
        top = HEIGHT // 2 - card_height // 2
        left = i * card_width + card_spacing * (i + 1) - card_spacing // 2
      else:
        top = HEIGHT // 2 - card_height // 2 + card_height + card_spacing + 40
        left = (i - 7) * card_width + card_spacing * (i -
                                                      6) - card_spacing // 2
    else:
      if i < 3:
        top = HEIGHT // 2 - card_height // 2
        left = i * card_width + card_spacing * (i + 1) - card_spacing // 2
      else:
        top = HEIGHT // 2 - card_height // 2 + card_height + card_spacing + 40
        left = (i - 3) * card_width + card_spacing * (i -
                                                      2) - card_spacing // 2

    rect = pg.Rect(left, top, card_width, card_height)
    try:
      font = pg.font.Font("prstartk.ttf", 15)
      if MenuScreen == 2:
        text = font.render(f"{MenuText[MenuScreen][i]+1}", True,
                           (255, 255, 255))
      else:
        text = font.render(f"{MenuText[MenuScreen][i]}", True, (255, 255, 255))
      screen.blit(text, (left + card_width / 2 - text.get_width() / 2,
                         top + card_height / 2 - text.get_height() / 2))
    except:
      pass

    if rect.collidepoint(mouse_x, mouse_y):
      if MenuScreen != 2:
        font = pg.font.Font("prstartk.ttf", 15)
        text = font.render(MenuText3[i], True, (255, 255, 255))
        screen.blit(
            text, (WIDTH / 2 - text.get_width() / 2, text.get_height() + 452))

      mouse_pressed = pg.mouse.get_pressed()
      rect = pg.Rect(left - 2, top - 2, card_width + 4, card_height + 4)
      if mouse_pressed[0]:  #different menus----------
        if not MouseDown:
          if MenuScreen == 0:
            if i == 5:
              touch=(touch is False)
            else:
              dificulty = MenuText[MenuScreen][i]
              if dificulty == "Custom":
                MenuScreen = 2
              else:
                MainMenu = False
          elif MenuScreen == 1:
            dificulty = MenuText[MenuScreen][i]
            MainMenu = False
          elif MenuScreen == 2:
            StartingRound = MenuText[MenuScreen][i]
            round = StartingRound
            MenuScreen = 1
        MouseDown = True
        pg.draw.rect(screen, (255, 255, 0), rect, border_size + 5)
      else:
        MouseDown = False
        pg.draw.rect(screen, (255, 255, 255), rect, border_size + 5)
      inner_rect = rect.inflate(-border_size * 2 - 10, -border_size * 2 - 10)

    else:
      pg.draw.rect(screen, (255, 255, 255), rect, border_size)
      inner_rect = rect.inflate(-border_size * 2, -border_size * 2)
    if MenuScreen==0 and i==5:
      if touch:
        pg.draw.rect(screen, (255, 255, 0), rect, border_size + 5)
      else:
        pg.draw.rect(screen, (255, 255, 255), rect, border_size)
      

def CrossHair():
  mouse_x, mouse_y = pg.mouse.get_pos()
  #pg.draw.line(screen, (255,255,255), (mouse_x,mouse_y), (playerPos[0] - screenPos[0]
  #                                    + WIDTH / 2, playerPos[1] - screenPos[1] + HEIGHT / 2), 1)
  #pg.draw.circle(screen, (255,255,255), (mouse_x,mouse_y), 5)

  pg.draw.polygon(screen, (255, 255, 255),
                  ((mouse_x + 20, mouse_y + 15), (mouse_x + 20, mouse_y + 20),
                   (mouse_x + 15, mouse_y + 20), (mouse_x + 20, mouse_y + 20)),
                  2)
  pg.draw.polygon(screen, (255, 255, 255),
                  ((mouse_x - 20, mouse_y - 15), (mouse_x - 20, mouse_y - 20),
                   (mouse_x - 15, mouse_y - 20), (mouse_x - 20, mouse_y - 20)),
                  2)
  pg.draw.polygon(screen, (255, 255, 255),
                  ((mouse_x - 20, mouse_y + 15), (mouse_x - 20, mouse_y + 20),
                   (mouse_x - 15, mouse_y + 20), (mouse_x - 20, mouse_y + 20)),
                  2)
  pg.draw.polygon(screen, (255, 255, 255),
                  ((mouse_x + 20, mouse_y - 15), (mouse_x + 20, mouse_y - 20),
                   (mouse_x + 15, mouse_y - 20), (mouse_x + 20, mouse_y - 20)),
                  2)

  #pg.draw.polygon(screen, (255,255,255), ((mouse_x,mouse_y),(mouse_x+20,mouse_y),(mouse_x,mouse_y),(mouse_x,mouse_y+20),(mouse_x,mouse_y),(mouse_x-20,mouse_y),(mouse_x,mouse_y),(mouse_x,mouse_y-20)), 2)


helptextQ = []
nextHelpText = pg.time.get_ticks()


def drawHUD():
  global helptextQ, nextHelpText
  pg.draw.circle(screen, (255, 255, 0), (11, 15), 5)
  font = pg.font.Font("prstartk.ttf", 15)
  text = font.render(f"{money}", True, (255, 255, 0))
  screen.blit(text, (21, 6))

  if nextHelpText <= pg.time.get_ticks() and len(helptextQ) > 0:
    helptextQ.pop(0)
    nextHelpText = pg.time.get_ticks() + 4000
  if len(helptextQ) > 0:
    font = pg.font.Font("prstartk.ttf", 15)
    text = font.render(helptextQ[0], True, (255, 255, 0))
    screen.blit(text, (WIDTH - text.get_width() - 6, 6 + (cansprint) * 40))

  #boss bar
  for bossbar in boss.instances:
    pg.draw.rect(screen, (255, 255, 255), (WIDTH / 2 - 105, 10, 210, 30), 5)
    if round!=13:
      pg.draw.rect(
          screen, (255, 0, 0),
          (WIDTH / 2 - 100, 15, bossbar.health * bossbar.maxhealth / 50, 20))
    else:
      pg.draw.rect(
          screen, (255, 0, 0),
          (WIDTH / 2 - 100, 15, bossbar.health * bossbar.maxhealth / 200, 20))
    for i in range(3):
      pg.draw.line(screen, (255, 255, 255), (WIDTH / 2 - 50 + i * 50, 10),
                   (WIDTH / 2 - 50 + i * 50, 35), 5)

  #font = pg.font.Font("prstartk.ttf", 70) #print timer on screen
  #text = font.render(f"{int(timeLeft//1000)}", True, (255, 0, 0))
  #screen.blit(text, (WIDTH/2-text.get_width()/2, 0))

  if health <= 0:
    font = pg.font.Font("prstartk.ttf", 50)
    text = font.render("YOU DIED!", True, (255, 0, 0))
    screen.blit(
        text,
        (WIDTH / 2 - text.get_width() / 2, HEIGHT / 2 - text.get_height() / 2))
    font = pg.font.Font("prstartk.ttf", 25)
    text = font.render(f"score {totalMoney}", True, (255, 255, 0))
    screen.blit(text, (WIDTH / 2 - text.get_width() / 2,
                       HEIGHT / 2 - text.get_height() / 2 + 55))
  if cansprint:
    pg.draw.line(screen, (100, 100, 150), (WIDTH, 22), (WIDTH - 103, 22), 14)
    pg.draw.circle(screen, (100, 100, 150), (WIDTH - 103, 23), 7)

    pg.draw.line(screen, (255, 255, 255), (WIDTH, 19), (WIDTH - sprint, 19),
                 14)
    pg.draw.circle(screen, (255, 255, 255), (WIDTH - sprint, 20), 7)


#["name",number of plays,"text",cost]
cards = [["HEAL FULL", 3, "", 10], ["LANDMINE", 2, "", 1],
         ["FIRE RATE", 5, "+1", 5], ["BULLETS", 2, "+1", 25],
         ["SPRINT", 1, "", 15], ["MONEY", 1, "x2", 40]]


def playcard(card):
  global money, health, maxhealth, multiShot, cansprint, reloadtime, DoubleMoney
  if cards[card][1] > 0:
    if money >= cards[card][3]:
      money -= cards[card][3]
      if card == 0:
        health = maxhealth
      elif card == 1:
        Landmine.instances.append(Landmine(playerPos))
      elif card == 2:
        reloadtime -= 0.1
      elif card == 3:
        multiShot += 1
      elif card == 4:
        cansprint = True
        helptextQ.append("")
        helptextQ.append("sprint[right mouse]")
      elif card == 5:
        DoubleMoney = 2
      cards[card][1] -= 1


nextPlayCard = 0
ButtonColour = (255, 255, 255)


def drawShop():
  global cards, nextPlayCard, MouseDown, ButtonColour
  transparent_black_surface = pg.Surface((WIDTH, HEIGHT))
  transparent_black_surface.set_alpha(50)
  transparent_black_surface.fill((0, 0, 0))
  screen.blit(transparent_black_surface, (0, 0))

  pg.draw.circle(screen, (255, 255, 0), (11, 15), 5)
  font = pg.font.Font("prstartk.ttf", 15)
  text = font.render(f"{money}/{totalMoney}", True, (255, 255, 0))
  text2 = font.render(f"round {round+1}/14", True, (0, 255, 0))
  screen.blit(text, (21, 6))
  screen.blit(text2, (5, 26))

  #font = pg.font.Font("prstartk.ttf", 70) #print timer on screen
  #text = font.render(f"{int(timeLeft//1000)}", True, (255, 0, 0))
  #screen.blit(text, (WIDTH/2-text.get_width()/2, 0))

  card_height = 100
  card_spacing = 10
  card_width = WIDTH // 3 - card_spacing
  border_size = 5
  mouse_x, mouse_y = pg.mouse.get_pos()
  for i in range(6):
    if i < 3:
      top = HEIGHT // 2 - card_height // 2
      left = i * card_width + card_spacing * (i + 1) - card_spacing // 2
    else:
      top = HEIGHT // 2 - card_height // 2 + card_height + card_spacing + 40
      left = (i - 3) * card_width + card_spacing * (i - 2) - card_spacing // 2
    rect = pg.Rect(left, top, card_width, card_height)

    if rect.collidepoint(mouse_x, mouse_y):
      mouse_pressed = pg.mouse.get_pressed()
      rect = pg.Rect(left - 2, top - 2, card_width + 4, card_height + 4)
      if mouse_pressed[0]:
        if not MouseDown:
          if money >= cards[i][3] and cards[i][1] != 0:
            playcard(i)
            ButtonColour = (255, 255, 0)
            #pg.draw.rect(screen, (255, 255, 0), rect, border_size + 5)
          else:
            ButtonColour = (255, 0, 0)
            #pg.draw.rect(screen, (255, 0, 0), rect, border_size + 5)
          MouseDown = True
      else:
        MouseDown = False
        ButtonColour = (255, 255, 255)
      pg.draw.rect(screen, ButtonColour, rect, border_size + 5)
      inner_rect = rect.inflate(-border_size * 2 - 10, -border_size * 2 - 10)

    else:
      pg.draw.rect(screen, (255, 255, 255), rect, border_size)
      inner_rect = rect.inflate(-border_size * 2, -border_size * 2)

    inner_surface = pg.Surface(inner_rect.size, pg.SRCALPHA)
    inner_surface.fill((0, 0, 0, 180))  # 70% transparent black
    screen.blit(inner_surface, inner_rect)
    card_text = cards[i][0]
    card_text2 = cards[i][1]
    card_text3 = cards[i][2]
    card_text4 = cards[i][3]
    font = pg.font.Font("prstartk.ttf", 25)
    font2 = pg.font.Font("prstartk.ttf", 15)
    text = font2.render(f"{card_text}", True, (255, 255, 255))
    text2 = font.render(f"{card_text2}", True, (255, 255, 255))
    text3 = font2.render(f"{card_text3}", True, (255, 255, 255))
    text4 = font2.render(f"{card_text4}", True, (255, 255, 0))
    screen.blit(text, (left + card_width // 2 - text.get_width() // 2,
                       top + card_height - card_spacing - text.get_height()))
    screen.blit(text2, (left + card_width - card_spacing - text2.get_width(),
                        top + card_spacing))
    pg.draw.circle(screen, (255, 255, 0),
                   (left + card_width // 2 - text4.get_width() // 2 - 4,
                    top + card_height - card_spacing - card_spacing + 30), 5)
    screen.blit(text4, (left + card_width // 2 - text4.get_width() // 2 + 5,
                        top + card_height - card_spacing - card_spacing + 22))
    screen.blit(
        text3,
        (left + card_width // 2 - text3.get_width() // 2,
         top + card_height - card_spacing - text2.get_height() - card_spacing))
  font = pg.font.Font("prstartk.ttf", 10)
  text = font.render("by SEB", False, (255, 255, 255))
  screen.blit(text, (WIDTH - text.get_width(), HEIGHT - text.get_height()))


def drawDots():
  for i in range(len(dots)):
    if dots[i][0] - screenPos[0] < WIDTH + 10 and dots[i][1] - screenPos[
        1] < HEIGHT + 10:
      pos = (dots[i][0] - screenPos[0] + WIDTH / 2,
             dots[i][1] - screenPos[1] + HEIGHT / 2)
      pg.draw.circle(screen, (0, 0, 30), pos, 70)


def drawPlayer():
  pos = (playerPos[0] - screenPos[0] + WIDTH / 2,
         playerPos[1] - screenPos[1] + HEIGHT / 2)  #draw player
  if health >= 6:
    color_value = (255 - int(255 * (health - 5) / maxhealth), 255, 255)
    pg.draw.circle(screen, color_value, pos, 20, 4)
  elif health > 0:
    color_value = (255, int(255 * health / maxhealth),
                   int(255 * health / maxhealth))
    pg.draw.circle(screen, color_value, pos, 20, 4)


nextSprintDown = 0
nextSprintUp = 0
nextReload = 0


def zonemove():
  global zoneTarget, zoneOrigin, zoneXY
  if not BossActive:
    if ((zoneXY[0] - zoneTarget[0])**2 +
        (zoneXY[1] - zoneTarget[1])**2)**0.5 > 10:
      if clock.get_fps() != 0:
        dx = zoneTarget[0] - zoneXY[0]
        dy = zoneTarget[1] - zoneXY[1]
        angle = math.degrees(math.atan2(dy, dx))
        zoneXY[0] += math.cos(
            math.radians(angle)) * 20 / clock.get_fps() * zoneSpeed
        zoneXY[1] += math.sin(
            math.radians(angle)) * 20 / clock.get_fps() * zoneSpeed
    else:
      zoneTarget = [random.randint(-800, 800), random.randint(-800, 800)]


def drawZone():
  #pg.draw.circle(screen, (255, 255, 255), (zoneXY[0] - screenPos[0] + WIDTH / 2,
  #                                     zoneXY[1] - screenPos[1] + HEIGHT / 2), 5)
  pg.draw.circle(screen, (0, 0, 100), (zoneXY[0] - screenPos[0] + WIDTH / 2,
                                       zoneXY[1] - screenPos[1] + HEIGHT / 2),
                 zoneRadius + 710, 710)

fingers={}
def playermove(x, y, shift):
  global playerPos, screenPos, nextReload, nextSprintUp, nextSprintDown, sprint, health, fingers
  if ((playerPos[0] - zoneXY[0])**2 +
      (playerPos[1] - zoneXY[1])**2)**0.5 > zoneRadius + 22:  #zone collision
    health = 0

  pressed = pg.mouse.get_pressed()
  sprintMultiplyer = 1
  if clock.get_fps() != 0:
    if cansprint:  #sprint
      if sprint < 100 and nextSprintUp < pg.time.get_ticks():
        sprint += 1
        nextSprintUp = pg.time.get_ticks() + 100
      if pressed[2] and sprint > 0 and nextSprintDown < pg.time.get_ticks():
        sprint -= 1
        sprintMultiplyer = 3
        nextSprintDown = pg.time.get_ticks() + 20
        nextSprintUp = pg.time.get_ticks() + 3000

    if touch:
      for event in pg.event.get():
        if event.type == pg.FINGERDOWN:
          x = event.x * WIDTH
          y = event.y * HEIGHT
          fingers[event.finger_id] = x,y
      for finger, pos in fingers.items(): 
        dx = pos[0] + screenPos[0] - WIDTH / 2 - playerPos[0]
        dy = pos[1] + screenPos[1] - HEIGHT / 2 - playerPos[1]
        angle = math.degrees(math.atan2(dy, dx))
        playerPos[0] += math.cos(math.radians(angle)) * 60 / clock.get_fps() * playerSpeed * sprintMultiplyer
        playerPos[1] += math.sin(math.radians(angle)) * 60 / clock.get_fps() * playerSpeed * sprintMultiplyer
      
      if nextReload < pg.time.get_ticks() / 1000:
        if multiShot == 1:
          Bullet.instances.append(Bullet(0))
        if multiShot == 2:
          Bullet.instances.append(Bullet(-5))
          Bullet.instances.append(Bullet(5))
        if multiShot == 3:
          Bullet.instances.append(Bullet(0))
          Bullet.instances.append(Bullet(-10))
          Bullet.instances.append(Bullet(10))
        nextReload = pg.time.get_ticks() / 1000 + reloadtime

    else:
      if x != 0 and y != 0:
        playerPos[0] -= y / 1.5 * 60 / clock.get_fps(
        ) * playerSpeed * sprintMultiplyer
        playerPos[1] -= x / 1.5 * 60 / clock.get_fps(
        ) * playerSpeed * sprintMultiplyer
      else:
        playerPos[0] -= y * 60 / clock.get_fps() * playerSpeed * sprintMultiplyer
        playerPos[1] -= x * 60 / clock.get_fps() * playerSpeed * sprintMultiplyer
      
      if pressed[0] and nextReload < pg.time.get_ticks() / 1000:
          if multiShot == 1:
            Bullet.instances.append(Bullet(0))
          if multiShot == 2:
            Bullet.instances.append(Bullet(-5))
            Bullet.instances.append(Bullet(5))
          if multiShot == 3:
            Bullet.instances.append(Bullet(0))
            Bullet.instances.append(Bullet(-10))
            Bullet.instances.append(Bullet(10))
          nextReload = pg.time.get_ticks() / 1000 + reloadtime

    screenPos[0] += (playerPos[0] - screenPos[0]) / 10 * 60 / clock.get_fps()
    screenPos[1] += (playerPos[1] - screenPos[1]) / 10 * 60 / clock.get_fps()

typeTOspawn = [[6, 5, 4], [3, 3, 4, 4, 4, 5], [4, 4, 4, 4, 5, 6],
               [3, 6, 6, 3, 4, 4], [3, 3, 3, 5, 6, 7],
               [3, 4, 5, 6, 7, 8, 9, 5], [6, 6, 3], [3, 9], [3, 4, 8],
               [4, 5, 10, 10], [5, 9, 7], [7, 8], [6, 7, 8, 9],
               [3, 3, 4, 4, 5, 6, 7, 8, 9, 10]]
dataTOspawn = [[3, 5], [3, 16], [3, 14], [2, 12], [4, 10], [5, 15], [5, 14],
               [1, 16], [2, 14], [3, 30], [2, 14], [2, 20], [1, 26], [1, 30]]
#[time between spawns,max no of spawns]
nextSpawn = 0

lastround = 0
MoneyForRound = [10, 11, 12, 14, 16, 18, 20, 23, 25, 30, 35, 40, 45, 50, 55]


def spawn():
  global nextSpawn, enemySpeed, round, money, health, BossesSpawned, lastround

  if sum(MoneyForRound[i] for i in range (round)) <= totalMoney:
    round += 1

  if round > 13:
    round = 13

  if lastround < round:
    lastround = round
    cards[0] = ["HEAL FULL", 5, "", 10]
    cards[1] = ["LANDMINE", 2, "", 1]

  if dificulty == "Easy":
    money = 999
  elif dificulty == "Hardcore" and health > 0:
    health = 1

  if dificulty == "Hard" or "Hardcore":
    enemySpeed = round // 5 + 4
  else:  # normal dificulty
    enemySpeed = round // 5 + 2
    if round == 13:
      enemySpeed = 5

  if round == 4 and BossesSpawned == 0:
    BossesSpawned = 1
    boss.instances.append(boss(10))

  if round == 9 or round == 13 and BossesSpawned == 0:
    BossesSpawned = 1
    boss.instances.append(boss(20))

  if round == 5 or round == 10:
    BossesSpawned == 0

  if not BossActive and nextSpawn < pg.time.get_ticks() / 1000 and sum(
      1 for enemy in Enemy.instances
      if enemy.type != 1 and enemy.task != "bullet") < dataTOspawn[round][1]:
    Enemy.instances.append(Enemy(round, 0, 0, 0))
    nextSpawn = pg.time.get_ticks() / 1000 + dataTOspawn[round][0]


def SpawnParticles(x, y, type, angle):
  if type > 10 and type != 100:
    for i in range(6):
      Particle.instances.append(Particle(x, y, type, angle))
  elif type >= 3 or type == 100:
    for i in range(40):
      Particle.instances.append(Particle(x, y, type, angle))
  else:
    for i in range(6):
      Particle.instances.append(Particle(x, y, type, angle))


class Particle:
  instances = []

  def __init__(self, x, y, type, angle):
    if type > 10:
      self.angle = random.randint(0, 70) * 5
      self.x = x
      self.y = y
    elif type > 0:
      self.angle = random.randint(0, 70) * 5
      self.x = x + math.cos(math.radians(self.angle)) * 20
      self.y = y + math.sin(math.radians(self.angle)) * 20
    else:
      self.angle = angle + random.randint(90, 270)
      self.x = x
      self.y = y

    if type == 100:
      self.type = 0
    elif type > 10:
      self.type = type - 10
    else:
      self.type = type
    self.size = random.randint(2, 3)
    self.killTime = pg.time.get_ticks() + random.randint(200, 500)

  def move(self):
    self.angle += random.randint(-10, 10)
    self.size -= random.choice([0, 0, 0, 1])
    self.x += math.cos(math.radians(self.angle)) * random.randint(
        20, 40) / clock.get_fps()
    self.y += math.sin(math.radians(self.angle)) * random.randint(
        20, 40) / clock.get_fps()

  def draw(self):
    pos = (self.x - screenPos[0] + WIDTH / 2,
           self.y - screenPos[1] + HEIGHT / 2)
    pg.draw.circle(screen, colors[self.type], pos, self.size)

  def tick(self, paused):
    if not paused:
      if clock.get_fps() != 0:
        self.move()
      if self.killTime < pg.time.get_ticks():
        Particle.instances.remove(self)
    self.draw()


class Bullet:  #____________________________________________BULLET________________________
  instances = []

  def __init__(self, angle):
    self.hit = False
    self.x = playerPos[0]
    self.y = playerPos[1]
    if touch:
      closest=[9999,0,0]
      for enemy in Enemy.instances:
        if enemy.task=="bullet" or enemy.task=="lazer" or enemy.type==1:
          pass
        else:
          distance=((enemy.x - playerPos[0])**2 +(enemy.y - playerPos[1])**2)**0.5
          if distance self.killtime:
        SpawnParticles(self.x, self.y, 0, self.angle)
        Bullet.instances.remove(self)
    self.draw()


class Enemy:  #______________________________________________ENEMY______________________
  instances = []

  def __init__(self, round, x, y, dir):
    self.angle = random.randint(0, 360)
    if round <= 13:
      self.type = random.choice(typeTOspawn[round])

    if x != 0:
      self.type = (round - 1) // 2
      if round > 10:
        self.x = x + math.cos(math.radians(dir)) * 100
        self.y = y + math.sin(math.radians(dir)) * 100
      else:
        self.x = x + math.cos(math.radians(dir)) * 17
        self.y = y + math.sin(math.radians(dir)) * 17
      self.angle = dir
      self.task = "bullet"
      self.health = multiShot
      self.nextTask = pg.time.get_ticks() + random.randint(
          1000, 2000) + (BossActive == True) * 10000

    elif self.type == 2:
      self.x = playerPos[0] + random.randint(-40, 40)
      self.y = playerPos[1] + random.randint(-40, 40)
      self.hitbox = []
      for i in range(-800, 800, 10):
        x = math.cos(math.radians(self.angle)) * i
        y = math.sin(math.radians(self.angle)) * i
        self.hitbox.append([x, y])

      self.nextTask = pg.time.get_ticks() + 3000
      self.task = "lazer"
      self.health = 999

    else:
      self.x = zoneXY[0] + math.cos(math.radians(
          self.angle)) * (zoneRadius + 70)
      self.y = zoneXY[1] + math.sin(math.radians(
          self.angle)) * (zoneRadius + 70)
      self.health = self.type
      self.task = "attack"
      self.nextTask = pg.time.get_ticks() + 2000

      if self.type == 9 or self.type == 10:
        self.task = "shoot"
        self.nextTask = pg.time.get_ticks() + random.randint(2500, 3500)

  def move(self):
    if clock.get_fps() != 0:

      if self.task == "bullet":
        self.x += math.cos(math.radians(self.angle)) * 60 / clock.get_fps()
        self.y += math.sin(math.radians(self.angle)) * 60 / clock.get_fps()

      elif self.task == "lazer":
        if self.nextTask < pg.time.get_ticks():
          Enemy.instances.remove(self)
      else:
        dx = playerPos[0] - self.x
        dy = playerPos[1] - self.y
        target_angle = math.degrees(math.atan2(dy, dx))
        angle_diff = (target_angle - self.angle) % 360
        if angle_diff > 180:
          angle_diff -= 360
        self.angle += angle_diff * 0.05  #adjust the rotation speed here

      if self.task == "attack":
        strafeAngle = 0
        moveSpeed = 1
        if self.nextTask < pg.time.get_ticks():
          if random.randint(0, 3) == 0:
            self.task = random.choice(["circleL", "circleR", "charge"])
            self.nextTask = pg.time.get_ticks() + random.randint(1000, 3000)
          else:
            self.nextTask = pg.time.get_ticks() + 2000

      elif self.task == "shoot":
        strafeAngle = 0
        moveSpeed = 1
        if self.nextTask < pg.time.get_ticks():
          for i in range(self.type):
            Enemy.instances.append(
                Enemy(self.type, self.x, self.y,
                      self.angle + i * (360 / self.type)))
          self.nextTask = pg.time.get_ticks() + random.randint(2500, 3500)

      elif self.task == "charge":  #x2 speed of attack
        strafeAngle = 0
        moveSpeed = 2
        if self.nextTask < pg.time.get_ticks():
          self.task = "attack"
          self.nextTask = pg.time.get_ticks() + 2000

      elif self.task == "runL":  #back and to the left
        strafeAngle = 225
        moveSpeed = 1

      elif self.task == "runR":  #back and to the right
        strafeAngle = 135
        moveSpeed = 1

      elif self.task == "circleL":  #circle to the left
        strafeAngle = 270
        moveSpeed = 1

      elif self.task == "circleR":  #circle to the right
        strafeAngle = 90
        moveSpeed = 1

      else:
        strafeAngle = 0
        moveSpeed = 1

      if "run" in self.task:
        if self.nextTask < pg.time.get_ticks():
          self.task = "attack"
          self.nextTask = pg.time.get_ticks() + 2000

      if "circle" in self.task:
        if self.nextTask < pg.time.get_ticks():
          if random.randint(0, 4) == 0:
            self.task = "charge"
            self.nextTask = pg.time.get_ticks() + random.randint(1000, 3000)
          else:
            self.nextTask = pg.time.get_ticks() + random.randint(1000, 3000)
        for bullet in Bullet.instances:
          if ((self.x - bullet.x)**2 + (self.y - bullet.y)**2)**0.5 < 90:
            if random.randint(0, 3) == 0:
              self.task = random.choice(["runL", "runR"])
              self.nextTask = pg.time.get_ticks() + random.randint(1000, 3000)
            else:
              self.task = "attack"
              self.nextTask = pg.time.get_ticks() + 2000
      if self.task != "bullet":
        self.x += math.cos(math.radians(self.angle + strafeAngle)) * (
            11 -
            self.type) / 24 * 60 / clock.get_fps() * enemySpeed * moveSpeed
        self.y += math.sin(math.radians(self.angle + strafeAngle)) * (
            11 -
            self.type) / 24 * 60 / clock.get_fps() * enemySpeed * moveSpeed

  def collide(self):
    global money, totalMoney, health, timeLeft
    if self.type == 1 and ((self.x - playerPos[0])**2 +
                           (self.y - playerPos[1])**2)**0.5 < 25:
      money += 1 * DoubleMoney  #collide with money
      timeLeft += 5000
      totalMoney += 1
      Enemy.instances.remove(self)

    elif self.task == "bullet" and self in Enemy.instances and (
        (self.x - playerPos[0])**2 + (self.y - playerPos[1])**2)**0.5 < 23:
      SpawnParticles(self.x, self.y, self.type + 10, 0)
      Enemy.instances.remove(self)  #enemy bullet collide with player
      health -= 1

    elif self.task == "bullet" and self in Enemy.instances:
      for safety in Safety.instances:
        if ((self.x - safety.x)**2 +
            (self.y - safety.y)**2)**0.5 < safety.size + 5:
          SpawnParticles(self.x, self.y, self.type + 10, 0)
          Enemy.instances.remove(self)  #enemy bullet collide with safety

    elif self.task == "lazer" and self.health <= 999 and self.nextTask - 1000 > pg.time.get_ticks(
    ) and self.nextTask - 2000 < pg.time.get_ticks():
      for i in range(len(self.hitbox)):
        if ((self.hitbox[i][0] - playerPos[0])**2 +
            (self.hitbox[i][1] - playerPos[1])**2)**0.5 < 25:
          #SpawnParticles(self.x, self.y, 1, 0)  #collide with lazer
          health -= 1
          self.health = 9999

    elif self.type != 1 and ((self.x - playerPos[0])**2 +
                             (self.y - playerPos[1])**2)**0.5 < 30:
      SpawnParticles(self.x, self.y, self.type, 0)
      Enemy.instances.remove(self)  #enemy collide with player
      health -= 1

    for landmine in Landmine.instances:
      if self.task != "bullet" and self.type != 1 and (
          (self.x - landmine.x)**2 + (self.y - landmine.y)**2)**0.5 < 40:
        SpawnParticles(self.x, self.y, self.type, 0)
        self.type = 1
        #Enemy.instances.remove(self)  #enemy collide with Landmine

    for bullet in Bullet.instances:
      if self.type != 1 and self.task != "bullet" and (
          (self.x - bullet.x)**2 + (self.y - bullet.y)**2)**0.5 < 25:
        if self.health > 1:
          self.health -= 1
          bullet.hit = True
        else:
          SpawnParticles(self.x, self.y, self.type, 0)
          bullet.hit = True  #enemy collide with bullet
          self.type = 1
          screenPos[0] += random.choice([-5, 5])  #screenshake
          screenPos[1] += random.choice([-5, 5])  #screenshake
      elif self.task == "bullet" and self in Enemy.instances and (
          (self.x - bullet.x)**2 + (self.y - bullet.y)**2)**0.5 < 6:
        SpawnParticles(self.x, self.y, self.type + 10, 0)
        bullet.hit = True
        Enemy.instances.remove(self)  #bullet collide with enemy bullet

    if self.task == "bullet" and self in Enemy.instances:  #gets rid of bullet if out of time
      if self.nextTask < pg.time.get_ticks():
        Enemy.instances.remove(self)

  def draw(self):
    points = []
    if self.type == 1 or self.task == "bullet":
      pg.draw.circle(screen, colors[self.type],
                     (self.x - screenPos[0] + WIDTH / 2,
                      self.y - screenPos[1] + WIDTH / 2), 3)
    elif self.task == "lazer":

      x1 = math.cos(math.radians(self.angle)) * 800
      y1 = math.sin(math.radians(self.angle)) * 800
      x2 = math.cos(math.radians(self.angle + 180)) * 800
      y2 = math.sin(math.radians(self.angle + 180)) * 800
      co1 = (x1 - screenPos[0] + WIDTH / 2, y1 - screenPos[1] + WIDTH / 2)
      co2 = (x2 - screenPos[0] + WIDTH / 2, y2 - screenPos[1] + WIDTH / 2)

      if self.nextTask - 1000 > pg.time.get_ticks(
      ) and self.nextTask - 2000 < pg.time.get_ticks():
        pg.draw.line(screen, (255, 0, 0), co1, co2, 5)
      else:
        pg.draw.line(screen, (255, 255, 255), co1, co2, 5)

    else:
      for i in range(self.type):
        x = math.cos(math.radians(self.angle + 360 / self.type * i)) * 20
        y = math.sin(math.radians(self.angle + 360 / self.type * i)) * 20
        points.append((x + self.x - screenPos[0] + WIDTH / 2,
                       y + self.y - screenPos[1] + WIDTH / 2))
      pg.draw.polygon(screen, colors[self.type], points, 4)
      #tracers
      #pg.draw.polygon(screen,(255,255,255),[(WIDTH/2,HEIGHT/2),(self.x-screenPos[0]+WIDTH/2,self.y-screenPos[1]+HEIGHT/2)],1)
      #
  def tick(self, paused):
    if not paused:
      if self.type != 1:
        self.move()
      self.collide()
    self.draw()


BossActive = False


class boss:
  instances = []

  def __init__(self, sides):
    global BossActive
    self.sides = sides
    self.x = zoneXY[0]
    self.y = zoneXY[1]
    self.angle = 0
    self.health = sides * 10
    self.maxhealth = self.health
    self.phase = 0
    self.LorR = 1
    self.nextTask = pg.time.get_ticks() + 1000
    self.shot = 0
    BossActive = True
    self.size = 0
    self.fractionHealth = 0

  def draw(self):
    points = []
    for i in range(self.sides):
      x = math.cos(math.radians(self.angle + 360 / self.sides * i)) * self.size
      y = math.sin(math.radians(self.angle + 360 / self.sides * i)) * self.size
      points.append((x + self.x - screenPos[0] + WIDTH / 2,
                     y + self.y - screenPos[1] + WIDTH / 2))
    pg.draw.polygon(screen, colors[self.sides // 2], points, 4)

  def collide(self):
    global health
    if ((self.x - playerPos[0])**2 +
        (self.y - playerPos[1])**2)**0.5 < self.size + 20 and self.phase != 0:
      health -= 1  # collide with player
    for bullet in Bullet.instances:
      if ((self.x - bullet.x)**2 +
          (self.y - bullet.y)**2)**0.5 < self.size + 5:
        #if self.health > 1:
        self.fractionHealth += 1
        if self.fractionHealth >= multiShot:
          self.health -= 1
          self.fractionHealth = 0
        bullet.hit = True
        #else:
        #SpawnParticles(self.x, self.y, self.sides, 0)  #enemy dies
        #bullet.hit = True
        #BossActive=False
        #collide with bullet
        #screenPos[0] += random.choice([-5, 5])  #screenshake
        #screenPos[1] += random.choice([-5, 5])  #screenshake
        #Boss.instances.remove( self)

  def move(self):
    if self.phase == 0:
      RotateSpeed = 0
      if self.nextTask < pg.time.get_ticks():
        self.nextTask = pg.time.get_ticks() + 1
        self.size += 1
        if self.size >= 100:
          self.phase = 1

    elif self.phase == 1:
      RotateSpeed = 0.5
      if self.angle > 180 and self.LorR == 1:
        self.LorR = -1
      if self.angle < 0 and self.LorR == -1:
        self.LorR = 1
      FireRate = 500  #one every 0.5 seconds
      EveryOtherPoint = 2
      self.size = 100
      if self.health <= self.maxhealth / 4 * 3:
        self.phase = 2

    elif self.phase == 2:
      RotateSpeed = 0.2
      FireRate = 500  #one every 0.5 seconds
      EveryOtherPoint = 1
      if self.health <= self.maxhealth / 2:
        self.phase = 3
        self.LorR *= -1

    elif self.phase == 3:
      RotateSpeed = 1
      EveryOtherPoint = 1
      if self.nextTask < pg.time.get_ticks():
        if self.shot < 3:
          FireRate = 100  #one every 0.1 seconds
          self.shot += 1
        else:
          FireRate = 2000  #one every 1 seconds
          self.shot = 0
      if self.health <= self.maxhealth / 4:
        self.phase = 4

    elif self.phase == 4:
      RotateSpeed = 1
      EveryOtherPoint = 1

      if self.nextTask < pg.time.get_ticks():
        if self.shot < 50:
          self.shot += 1
          FireRate = 200
        elif self.shot < 51:
          self.shot += 1
          FireRate = 9000
        else:
          self.LorR *= -1
          self.shot = 0
          FireRate = 200
      if self.health <= 0:
        self.phase = 5

    else:
      RotateSpeed = (100 - self.size) / 10
      if self.nextTask < pg.time.get_ticks():
        self.nextTask = pg.time.get_ticks() + 1
        self.size -= 1
        screenPos[0] += random.choice([-5, 5])  #screenshake
        screenPos[1] += random.choice([-5, 5])  #screenshake
      if self.size <= -20:
        SpawnParticles(self.x, self.y, self.sides // 2, 0)  #enemy dies
        BossActive = False
        boss.instances.remove(self)

    if self.nextTask < pg.time.get_ticks():  #shoot
      for i in range(0, self.sides, EveryOtherPoint):
        Enemy.instances.append(
            Enemy(self.sides + 1, self.x, self.y,
                  self.angle + i * (360 / self.sides)))
      self.nextTask = pg.time.get_ticks() + FireRate

    self.angle += self.LorR * 60 / clock.get_fps() * RotateSpeed

  def tick(self, paused):
    if not paused:
      self.move()
      self.collide()
    self.draw()


class Landmine():
  instances = []

  def __init__(self, pos):
    self.x = pos[0]
    self.y = pos[1]
    self.angle = random.randint(0, 360)
    self.killTime = pg.time.get_ticks() + 20000  # disappear in 20 seconds

  def draw(self):
    pg.draw.circle(
        screen, (255, 255, 255),
        (self.x - screenPos[0] + WIDTH / 2, self.y - screenPos[1] + WIDTH / 2),
        20, 4)
    for j in range(0, 91, 90):
      points = []
      for i in range(0, 181, 180):
        x = math.cos(math.radians(self.angle + i + j)) * 18
        y = math.sin(math.radians(self.angle + i + j)) * 18
        points.append((x + self.x - screenPos[0] + WIDTH / 2,
                       y + self.y - screenPos[1] + WIDTH / 2))
      pg.draw.polygon(screen, (255, 255, 255), points, 5)

  def move(self):
    if self.killTime < pg.time.get_ticks():
      SpawnParticles(self.x, self.y, 100, 0)
      Landmine.instances.remove(self)

  def tick(self, paused):
    if not paused:
      self.move()
    self.draw()


class Safety():
  instances = []

  def __init__(self):
    if len(Safety.instances) == 1:
      self.angle = 1
    self.angle = random.randint(0, 360)
    self.distance = random.randint(150, zoneRadius - 50)
    self.x = zoneXY[0] + math.cos(math.radians(self.angle)) * self.distance
    self.y = zoneXY[1] + math.sin(math.radians(self.angle)) * self.distance
    self.task = 0
    self.size = 0
    self.killTime = pg.time.get_ticks() + 1

  def draw(self):
    pg.draw.circle(
        screen, (10, 10, 100),
        (self.x - screenPos[0] + WIDTH / 2, self.y - screenPos[1] + WIDTH / 2),
        self.size, 4)

  def move(self):
    if self.killTime < pg.time.get_ticks():
      if self.task == 0:
        if self.size <= 50:
          self.size += 1
          self.killTime = pg.time.get_ticks() + 1
        else:
          self.killTime = pg.time.get_ticks() + random.randint(10, 15) * 1000
          self.task = 1
      elif self.task == 1:
        self.task = 2
      else:
        if self.size > 0:
          self.size -= 1
          self.killTime = pg.time.get_ticks() + 1
        else:
          Safety.instances.append(Safety())
          Safety.instances.remove(self)

  def tick(self, paused):
    if not paused:
      self.move()
    self.draw()


for i in range(2):
  Safety.instances.append(Safety())  #create 2 safety bubbles

MainMenu = True
a = True

startPause = 0
endPause = 0
start = 0
ShopOpen = False


async def main():
  global startPause, endPause, a, nextSpawn, nextReload, nextSprintDown, nextSprintUp, nextHelpText, start, helptextQ, ShopOpen
  #_________________________________________________________while true loop_______________
  while True:
    if MainMenu == True:
      menu()

    else:
      if start == 0:
        helptextQ = [
            "", "move[W][A][S][D]", "", "shoot[left click]", "", "shop[space]",
            ""
        ]
        start = 1
      #particles testing
      key = pg.key.get_pressed()
      mouse_x, mouse_y = pg.mouse.get_pos()
      if key[pg.K_SPACE] and a and health > 0:
        ShopOpen = (ShopOpen is False)
        a = False
      if key[pg.K_SPACE] is False:
        a = True
      #particles testing

      key = pg.key.get_pressed()
      if ShopOpen and health > 0:  #__ ____________shop
        #pg.mouse.set_cursor(pg.SYSTEM_CURSOR_HAND)
        if startPause == 0:
          startPause = pg.time.get_ticks()

        screen.fill((0, 0, 20))
        drawDots()
        numENEMY = 0

        for safety in Safety.instances:
          safety.tick(True)

        for enemy in Enemy.instances:
          enemy.tick(True)
          if enemy.type != 1:
            numENEMY += 1

        for Boss in boss.instances:
          Boss.tick(True)

        for bullet in Bullet.instances:
          bullet.tick(True)

        for particle in Particle.instances:
          particle.tick(True)

        for landmine in Landmine.instances:
          landmine.tick(True)

        drawPlayer()
        drawZone()
        drawShop()

      else:  #________________________________not shop
        #pg.mouse.set_cursor((8,8),(0,0),(0,0,0,0,0,0,0,0),(0,0,0,0,0,0,0,0))

        #if clock.get_fps() != 0:
        #  timeLeft -= 600 / clock.get_fps() #timer time left
        if startPause != 0:
          endPause = pg.time.get_ticks()

        if endPause != 0 and startPause != 0:
          nextSpawn += (endPause - startPause) / 1000
          nextReload += (endPause - startPause) / 1000
          nextSprintUp += endPause - startPause
          nextSprintDown += endPause - startPause
          nextHelpText += endPause - startPause
        spawn()

        if health > 0:
          playermove(key[pg.K_w] - key[pg.K_s], key[pg.K_a] - key[pg.K_d],
                     key[pg.K_LSHIFT])
        else:
          playerPos = zoneXY
          screenPos[0] += (playerPos[0] - screenPos[0]) / 100 * 60 / clock.get_fps()
          screenPos[1] += (playerPos[1] - screenPos[1]) / 100 * 60 / clock.get_fps()
        screen.fill((0, 0, 20))
        drawDots()

        for safety in Safety.instances:
          safety.killTime += endPause - startPause
          safety.tick(False)

        for Boss in boss.instances:
          Boss.nextTask += endPause - startPause
          Boss.tick(False)

        numENEMY = 0
        for enemy in Enemy.instances:
          enemy.tick(False)
          enemy.nextTask += endPause - startPause
          if enemy.type != 1:
            numENEMY += 1

        for bullet in Bullet.instances:
          bullet.killtime += endPause - startPause
          bullet.tick(False)

        for particle in Particle.instances:
          particle.killTime += endPause - startPause
          particle.tick(False)

        for landmine in Landmine.instances:
          landmine.killTime += endPause - startPause
          landmine.tick(False)

        zonemove()
        startPause = 0
        endPause = 0
        drawPlayer()
        drawZone()
        drawHUD()
        #CrossHair()


    [exit() for i in pg.event.get() if i.type == pg.QUIT]
    pg.display.set_caption(f"SEBak - Shape Game v{version}")
    pg.display.flip()
    clock.tick(60)

    await asyncio.sleep(0)


asyncio.run(main())