Ejemplo n.º 1
0
def main():
    init()
    global width, height, animator
    size = width, height = 1920, 1080
    screen = pygame.display.set_mode(size, FULLSCREEN)
    gameClock = time.Clock()
    animator = Animation()
    
    #load default background
    background = pygame.sprite.Group()
    bg1 = SpriteRemix.Background(transform.scale(image.load("Assets\\backgrounds\outsidebg.png").convert(),(1920,1080)))
    bg2 = SpriteRemix.Background(transform.scale(image.load("Assets\\backgrounds\outsidebg.png").convert(),(1920,1080)))
    bg1.stateVal = 1 #slowscroll
    bg2.stateVal = 1 #slowscroll
    bg1.add(background)
    bg2.add(background)

    #load ui, TODO: encapsulate this shit
    ui = pygame.sprite.Group()
    health = SpriteRemix.UI(transform.scale(image.load("Assets\\sprites\\ui\\health.png").convert(),(596,72)))
    healthbar = SpriteRemix.UI(transform.scale(image.load("Assets\\sprites\\ui\\healthbar.png").convert_alpha(),(840,84)))
    health.add(ui)
    healthbar.add(ui)

    #create cursor and add it to a sprite group, can only hold 1 cursor at a time
    cursors = pygame.sprite.GroupSingle()
    crsr = SpriteRemix.Cursor(transform.scale(pygame.image.load("Assets\\sprites\\cursors\\crosshair1.png").convert_alpha(),(70,70)))
    crsr.add(cursors)


    #create pc and add it to a sprite group
    #TODO: need an initializer class for player that loads projectiles and weapons and shit
    pc = pygame.sprite.Group()
    playerSprite = PlayerCharacterSprite()
    animator.load(playerSprite)
    playerSprite.add(pc)
    
    #weapon, this shit is f*****g retarded
    pcAccessory = pygame.sprite.Group()
    playerWeapon = SpriteRemix.Weapon()
    animator.load(playerWeapon)
    playerWeapon.add(pcAccessory)

    
    #create baddies and add them to a sprite group
    baddies = sprite.Group()
    baddySprite = EnemyCharacterSprite("notzigrunt")
    animator.load(baddySprite)
    baddySprite.add(baddies)

    #create doodads and add them to a sprite group
    doodads = sprite.Group()
    box = SpriteRemix.Doodad(pygame.image.load("Assets\\sprites\\doodads\\box.png").convert())
    #alsoBox = Doodad(pygame.image.load("Assets\\sprites\\doodads\\box.png").convert())
    box.add(doodads)
    #alsoBox.add(doodads)

    #initialize projectile sprite group (obv nothing to put here at startup
    projectiles = sprite.Group()
    
    #floating combat text
    combatTextArr = []

    #place everything
    bg1.rect.topleft = [-1920,0]
    bg2.rect.topleft = [0,0]

    healthbar.rect.topleft = [50,50]
    health.rect.topleft = [healthbar.rect.left+239, healthbar.rect.top+5]
    
    playerSprite.rect.bottomleft = [100,height-50]
    playerWeapon.rect.midright = playerSprite.rect.midleft

    baddySprite.rect.bottom = height
    baddySprite.rect.left = 960

    box.rect.left = 540
    box.rect.bottom = height
    '''alsoBox.rect.left = 920
    alsoBox.rect.bottom = height'''

    #create a list of all sprite groups
    entities = [pc.sprites(), baddies.sprites()]
    sprites = [pc, pcAccessory, baddies, doodads, projectiles, background, ui, cursors]

    while 1:

        now = time.get_ticks()
        
        #this for loop processes all inputs in the event queue
        events = pygame.event.get()
        for event in events:

            
            #close window and quit if x is clicked or esc is pressed
            if event.type == QUIT or (event.type == KEYDOWN and event.key == K_ESCAPE):
                quit()
                sys.exit()

            #track the cursor
            if event.type == MOUSEMOTION:
                crsr.rect.centerx = event.pos[0]
                crsr.rect.centery = event.pos[1]

            #if no input is given, this remains True, animation reflects that.
            if not playerSprite.state["ducking"]:
                playerSprite.state["idle"] = True
            
            #only control pc if pc not dead
            #may be simplified by banning control input events when pc dies.
            if not playerSprite.state["dying"] and not playerSprite.state["dead"]:

                

                #movement d-right a-left space-jump
                if not playerSprite.state["ducking"]:
                    if event.type == KEYUP and event.key == K_d:
                        playerSprite.rightDash = now
                        Movement.accel(playerSprite, -playerSprite.velocity[0])
                        Movement.coast(playerSprite, playerSprite.velocity[0])
                        if playerSprite.velocity[0] == 0:
                            playerSprite.state["running"] = False
                        playerSprite.idleTime = 0
                        playerSprite.state["idle"] = False

                    elif event.type == KEYUP and event.key == K_a:
                        playerSprite.leftDash = now
                        Movement.accel(playerSprite, -playerSprite.velocity[0])
                        Movement.coast(playerSprite, playerSprite.velocity[0])
                        if playerSprite.velocity[0] == 0:
                            playerSprite.state["running"] = False
                        playerSprite.idleTime = 0
                        playerSprite.state["idle"] = False

                    elif event.type == KEYDOWN and event.key == K_d:
                        if now - playerSprite.rightDash > 250:
                            Movement.accel(playerSprite, 12)
                        else:
                            Movement.accel(playerSprite, 24)
                        playerSprite.leftDash = 0
                        playerSprite.xflip = False
                        if not playerSprite.state["jumping"] and not playerSprite.state["falling"]:
                            playerSprite.state["running"] = True
                        playerSprite.idleTime = 0
                        playerSprite.state["idle"] = False

                    elif event.type == KEYDOWN and event.key == K_a:
                        if now - playerSprite.leftDash > 250:
                            Movement.accel(playerSprite, -12)
                        else:
                            Movement.accel(playerSprite, -24)
                        playerSprite.rightDash = 0
                        playerSprite.xflip = True
                        if not playerSprite.state["jumping"] and not playerSprite.state["falling"]:
                            playerSprite.state["running"] = True
                        playerSprite.idleTime = 0
                        playerSprite.state["idle"] = False

                if playerSprite.velocity[0] == 0 and playerSprite.velocity[1] == 0:
                    if event.type == KEYUP and event.key == K_s:
                        playerSprite.idleTime = 0
                        playerSprite.state["ducking"] = False
                        playerSprite.state["idle"] = False

                    elif event.type == KEYDOWN and event.key == K_s:
                        #crouching provides traction
                        playerSprite.xcoast = playerSprite.xcoast/2.0
                        playerSprite.state["ducking"] = True
                        playerSprite.idleTime = 0
                        playerSprite.state["idle"] = False


                if event.type == KEYUP and event.key == K_SPACE:
                    playerSprite.velocity[1] = max(0,playerSprite.velocity[1])
                    playerSprite.state["jumping"] = False
                    playerSprite.idleTime = 0
                    playerSprite.state["idle"] = False

                if event.type == KEYDOWN and event.key == K_SPACE:
                    Movement.jump(playerSprite)
                    playerSprite.state["idle"] = False
                    playerSprite.state["jumping"] = True
                    playerSprite.idleTime = 0
                    playerSprite.state["idle"] = False


                #melee attack
                if event.type == MOUSEBUTTONDOWN and event.button == 1 and now - playerSprite.lastMelee > 300:
                    playerSprite.state["attacking"] = True
                    playerSprite.last["meleed"] = now
                    playerSprite.idleTime = 0
                    playerWeapon.hostile = True
                    playerSprite.state["idle"] = False

                if event.type == MOUSEBUTTONUP and event.button == 1:
                    playerWeapon.hostile = False
                    playerSprite.state["idle"] = False
                    playerSprite.state["attacking"] = False
                
                #ranged attack
                if event.type == MOUSEBUTTONDOWN and event.button == 3 and playerSprite.ammo > 0 and now - playerSprite.lastShot > 250:
                    playerSprite.ammo -= 1
                    projLoc = [playerSprite.rect.right, playerSprite.rect.bottom-130]
                    newProj = SpriteRemix.Projectile(projLoc, event.pos, speed=45)
                    animator.load(newProj)
                    newProj.add(projectiles)
                    newProj.rect.center = projLoc
                    playerSprite.last["shot"] = now
                    playerSprite.state["shooting"] = True
                    playerSprite.idleTime = 0
                    playerSprite.state["idle"] = False

            #ragdoll pc
            else:
                playerSprite.xcoast = playerSprite.velocity[0]
                playerSprite.ycoast = playerSprite.velocity[1]
                playerSprite.velocity = [0,0]

        if playerSprite.state["idle"]:
            playerSprite.idleTime += gameClock.get_time()
            if playerSprite.idleTime >= 2000:
                playerSprite.state["idle"] = True
            elif playerSprite.idleTime >= 300:
                playerSprite.state["ready"] = True

        #baddy behavior
        '''if (now%1000 < 20) and baddySprite.stateVal != 1:
            Movement.jump(baddySprite)'''

        #replenish ammo over time
        if (now %100 < 20) and playerSprite.ammo < 4:
            playerSprite.ammo += 1

        if (now % 5000 < 20):
            print(playerSprite.state)

        

        #sprites move, but these moves haven't been drawn yet
        for i in range(len(sprites)-2):
            groupList = sprites[i].sprites()
            for aSprite in groupList:
                if aSprite.xcoast > 0:
                    if aSprite.xcoast > .6:
                        aSprite.xcoast += min(-aSprite.xcoast*.1,-.6)
                    else:
                        aSprite.xcoast = 0
                elif aSprite.xcoast < 0:
                    if aSprite.xcoast < -.6:
                        aSprite.xcoast += max(-aSprite.xcoast*.1,.6)
                    else:
                        aSprite.xcoast = 0
                if aSprite.ycoast > 0:
                    if aSprite.ycoast > .6:
                        aSprite.ycoast += min(-aSprite.ycoast*.1,-.6)
                    else:
                        aSprite.ycoast = 0
                elif aSprite.ycoast < 0:
                    if aSprite.ycoast < -.6:
                        aSprite.ycoast += max(-aSprite.ycoast*.1,.6)
                    else:
                        aSprite.ycoast = 0
                aSprite.rect = aSprite.rect.move([aSprite.velocity[0]+aSprite.xcoast,aSprite.velocity[1]+aSprite.ycoast])


        # keeps characters in frame and handles collisions
        resolveFrame(sprites,entities,combatTextArr)

        # position the pc's weapon
        # TODO: encapsulate this, preferably in Animation once I figure out why it wasn't working there.
        playerWeapon.state = playerSprite.state
        if playerSprite.xflip:
            if playerWeapon.state["dead"] or playerWeapon.state["idle"]:
                playerWeapon.rect.topleft = [0,0]
            elif playerWeapon.state["attacking"] or playerWeapon.state["shooting"]:
                playerWeapon.rect.midright = [playerSprite.rect.midleft[0]+8,playerSprite.rect.midleft[1]-62]
            else:
                playerWeapon.rect.midleft = [playerSprite.rect.midright[0],playerSprite.rect.midright[1]+58]
            playerWeapon.xflip = True
        else:
            if playerWeapon.state["dead"] or playerWeapon.state["idle"]:
                playerWeapon.rect.topleft = [0,0]
            elif playerWeapon.state["attacking"] or playerWeapon.state["shooting"]:
                playerWeapon.rect.midleft = [playerSprite.rect.midright[0]-8,playerSprite.rect.midright[1]-62]
            else:
                playerWeapon.rect.midright = [playerSprite.rect.midleft[0],playerSprite.rect.midleft[1]+58]
            playerWeapon.xflip = False

        # only animate characters and projectiles so far (i = 0 is pc, i = 1 is baddies, i = 3 is projectiles, i = 4 is background)
        animator.animate([sprites[0].sprites(), sprites[1].sprites(), sprites[2].sprites(), sprites[4].sprites(), sprites[5].sprites()], now)

        # refresh screen by drawing over previous frame with background
        screen.blit(bg1.image, bg1.rect)
        screen.blit(bg2.image, bg2.rect)
            
        # draw active CombatText objects and remove faded ones
        for combatText in combatTextArr[:]:
            if combatText.progress(now):
                combatText.draw(screen)
            else:
                combatTextArr.remove(combatText)

        # draw all the rest of the in-use assets
        for i in range(len(sprites)):
            if i != 5: #don't draw UI
                # only draw visible sprites in each group
                # NOTE: this is probably bad, as I'd assume .draw() (sprite method that blits all sprites in a sprite group)is better
                # optimized, but we can't make it optionally draw
                # sprites unless we change everything to DirtySprites (a type built into pygame)
                for aSprite in sprites[i].sprites():
                    if aSprite.visible:
                        screen.blit(aSprite.image,aSprite.rect)

        # draw UI last
        if health.rect.width > 5.96*playerSprite.health:
            health.image = transform.scale(health.image, (max(0,health.rect.width-3),health.rect.height))
            health.rect = health.image.get_rect()
            health.rect.topleft = (289,56)
        screen.blit(health.image, health.rect)
        screen.blit(healthbar.image, healthbar.rect)
        
        gameClock.tick(60)


        #game over check
        if playerSprite.health <= 0:
            defaultText = font.Font(None,120)
            gameOverSurface = defaultText.render("Game Over man, Game Over!", True, (255,0,0))
            gameOverRect = gameOverSurface.get_rect()
            gameOverRect.center = (960,540)
            screen.blit(gameOverSurface, gameOverRect)
            playerSprite.state["dead"] = True

        #victory check
        enemies = sprites[2].sprites()
        allDead = True
        for enemy in enemies:
            if enemy.stateVal != 4:
                allDead = False
        if allDead:
            defaultText = font.Font(None,200)
            conglaturationSurface = defaultText.render("Conglaturation", True, (255,255,255))
            conglaturationRect = conglaturationSurface.get_rect()
            conglaturationRect.center = (960,540)
            screen.blit(conglaturationSurface, conglaturationRect)

        pygame.display.flip()
Ejemplo n.º 2
0
class Game:

    def __init__(self):
        #define the screen related constants and set-up the sprite manager
        self.width = WIDTH
        self.height = HEIGHT
        self.tile = TILE
        self.fps = FPS
        self.state = MENU
        self.sprite_manager = SpriteManager(self)

    def create_window(self):
        #initialize the pygame window
        pygame.init()
        pygame.mixer.init()

        #create the screen and clock
        self.screen = pygame.display.set_mode((self.width, self.height))
        self.clock = pygame.time.Clock()

        #define a font
        self.font = pygame.font.Font(None, TILE)
        
    def load_map(self):
        #load the map, add tiles and sprite
        self.map = Map(self, "map.txt")
        self.map.add_tiles()
        self.map.add_sprites()

        #set the current sprite manager's sprites
        self.sprite_manager.add_sprites(self.all_sprites)

    def load_helpers(self):
        #load the camera, animator and renderer classes
        self.camera = Camera(self.map.width, self.map.height)
        self.animator = Animation(self)
        self.Renderer = Renderer(self)

    def new_groups(self):
        #define new groups
        self.walls = []
        self.tiles = []
        self.all_sprites = pygame.sprite.Group()
        self.characters = pygame.sprite.Group()
        self.animations = pygame.sprite.Group()
        self.hitboxes = pygame.sprite.Group()
        self.items = pygame.sprite.Group()
        
    def new_game(self):
        #create a new game
        self.new_groups()
        self.load_map()
        self.load_helpers()

    def state_transition(self, next_state):
        # transition to the next state and update the sprite manager's currently active sprites based on
        # the new game state
        self.state = next_state
        if next_state == INVENTORY:
            self.sprite_manager.add_sprites(self.player.inventory)
            self.order_player_inventory()
        elif next_state == CHARACTER_SCREEN:
            self.sprite_manager.add_sprites(self.player.equipped)
        elif next_state == RUNNING:
            self.sprite_manager.add_sprites(self.all_sprites)

    def update(self):
        #update the sprites and the camera view
        self.sprite_manager.active_sprites.update()
        self.items.update()
        self.camera.update(self.player)

    def order_player_inventory(self):
        #order the player's items in the inventory.
        #need to make sure this properly wraps around the screen once the width of the screen is exceeded.
        for i, item in enumerate(self.player.inventory):
            item.x = i * TILE
            item.y = 0

    def mouse_click_collision(self, position):
        #create a single pixel rectangle located at the position and check whether it collides with
        #and of the sprites in the sprite_manager's active sprites. If there is a collision, the sprite
        #is returned.
        #//NOTE// I couldn't seem to get built-in sprite.collidepoint method to work so I opted to create
        #a single pixel rectangle and this seems to do the job.
        pos_rect = pygame.Rect(position[0], position[1],1,1)
        for sprite in self.sprite_manager.active_sprites:
            sprite_rect = pygame.Rect(sprite.x, sprite.y, TILE, TILE)
            if pos_rect.colliderect(sprite_rect):
                return sprite

    def events(self):
        #get the event based on user input. The game's state is updated by calling the state_transition method.
        #the if/elif block is broken down by the state and the relevant method is called after user input.
        #I'm not happy with this as it's getting longer and longer so refactoring is on the to-do list.
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()
            if self.state == MENU: # start menu
                if event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_1:
                        self.state_transition(RUNNING)
                    elif event.key == pygame.K_2:
                        pygame.quit()
                        sys.exit()
            elif self.state == PAUSED: # paused
                if event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_ESCAPE:
                        self.state_transition(RUNNING)
                    elif event.key == pygame.K_1:
                        pass
                    elif event.key == pygame.K_2:
                        pass
            elif self.state == RUNNING: # playing
                if event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_ESCAPE:
                        self.state_transition(PAUSED)
                    elif event.key == pygame.K_r:
                        self.state_transition(INVENTORY)
                    elif event.key == pygame.K_c:
                        self.state_transition(CHARACTER_SCREEN)
                    elif event.key == pygame.K_SPACE:
                        self.player.attack(self.dt)
                    elif event.key == pygame.K_e:
                        self.pick_up_item()
            elif self.state == INVENTORY: # inventory
                if event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_ESCAPE or event.key == pygame.K_r:
                        self.state_transition(RUNNING)
                    if event.key == pygame.K_c:
                        self.state_transition(CHARACTER_SCREEN)
                if event.type == pygame.MOUSEBUTTONDOWN:
                    item = self.mouse_click_collision(pygame.mouse.get_pos())
                    if item:
                        if event.button == LEFT:
                            self.equip_item(item)
                        elif event.button == RIGHT:
                            self.drop_item(item)
            elif self.state == CHARACTER_SCREEN: # character screen
                if event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_ESCAPE or event.key == pygame.K_c:
                        self.state_transition(RUNNING)
                    elif event.key == pygame.K_i:
                        self.state_transition(INVENTORY)
                if event.type == pygame.MOUSEBUTTONDOWN:
                    item = self.mouse_click_collision(pygame.mouse.get_pos())
                    if item:
                        if event.button == LEFT:
                            self.unequip_item(item)
                        elif event.button == RIGHT:
                            self.drop_item(item)

    def equip_item(self, item):
        #determine if the item is a weapon or a piece of armour. Update the item's (x,y) position based on
        #where the character screen will show the body and weapon slots. The player's hand/body is checked to
        #determine whether an item is being held/worn. If not, the item is added to the relevant slot and to
        #the equipped group. It is then removed from the player's inventory and the sprite manager's active
        #sprite group. If an item is currently being held/worn, the new item is removed from the inventory and
        #sprite manager's active sprites. The held/worn item is then removed from the equipped group and added
        #to the inventory and sprite manager's active sprites. The new item is then added the equipped group
        # and finally the player's hand/body item is set as the item. 
        if isinstance(item, Weapon):
            item.x = 6*TILE
            item.y = 7*TILE
            if not self.player.hand:
                self.player.hand = item
                self.player.equipped.add(item)
                self.player.inventory.remove(item)
                self.sprite_manager.active_sprites.remove(item)
            else:
                self.player.inventory.remove(item)
                self.sprite_manager.active_sprites.remove(item)
                self.player.equipped.remove(self.player.hand)
                self.player.inventory.add(self.player.hand)
                self.sprite_manager.active_sprites.add(self.player.hand)
                self.player.equipped.add(item)
                self.player.hand = item
        elif isinstance(item, Armour):
            item.x = 7*TILE
            item.y = 7*TILE
            if not self.player.body:
                self.player.body = item
                self.player.equipped.add(item)
                self.player.inventory.remove(item)
                self.sprite_manager.active_sprites.remove(item)
            else:
                self.player.inventory.remove(item)
                self.sprite_manager.active_sprites.remove(item)
                self.player.equipped.remove(self.player.body)
                self.player.inventory.add(self.player.body)
                self.sprite_manager.active_sprites.add(self.player.body)
                self.player.equipped.add(item)
                self.player.body = item
        self.order_player_inventory()

    def unequip_item(self, item):
        #removes the item from the equipped group and the sprite manager's active sprites
        self.player.equipped.remove(item)
        self.sprite_manager.active_sprites.remove(item)

        #check if the item is either a Weapon or a piece of Armour. The item is added to the inventory and
        #the relevant slot (hand/body) is set to None.
        if isinstance(item, Weapon):
            self.player.inventory.add(item)
            self.player.hand = None
        elif isinstance(item, Armour):
            self.player.inventory.add(item)
            self.player.body = None
            
    def drop_item(self, item):
        #sets the item's x,y coordinates as the same as the player's.
        item.x = self.player.x
        item.y = self.player.y

        #goes through the items in the player's inventory and the player's equipped items and removes from the
        #relevant group and also removes from the sprite manager's active sprites.
        if item in self.player.inventory:
            self.player.inventory.remove(item)
        elif item in self.player.equipped:
            self.player.equipped.remove(item)
        self.sprite_manager.active_sprites.remove(item)

        #adds the item to the game's all_sprites and the game's items groups.
        self.all_sprites.add(item)
        self.items.add(item)

    def pick_up_item(self):
        #cycles through the items and determines whether there is a collision between the player and the item's
        #rectangle. If there is, the item is added to the player's inventory and removed from the game's items,
        #all_sprites and the sprite manager's active sprites.
        for item in self.items:
            if pygame.Rect(self.player.x, self.player.y, TILE, TILE).colliderect(pygame.Rect(item.x, item.y, TILE, TILE)):
                if not self.inventory_limit_reached(item):
                    self.player.inventory.add(item)
                    self.items.remove(item)
                    self.all_sprites.remove(item)
                    self.sprite_manager.active_sprites.remove(item)

    def inventory_limit_reached(self, item):
        #creates the local weight variable and determines the weight of the items in the player's inventory
        #and equipped groups. It then addes the new item's weight and checks if the sum is less than or equal
        #to the player's encumberance limit.
        weight = 0
        for item_ in self.player.inventory:
            weight += item_.weight
        for item_ in self.player.equipped:
            weight += item_.weight
        weight += item.weight
        if weight <= self.player.encumberance:
            return False

    def game_loop(self):
        #create a game window, initialize a new game and set the playing parameter to true.
        self.create_window()
        self.new_game()
        self.playing = True

        #enter the game loop
        while self.player.is_alive() and self.playing:
            self.show_fps = self.font.render(str(int(self.clock.get_fps())), True, WHITE)
            self.clock.tick(self.fps)
            self.dt = self.clock.tick(self.fps) / 1000
            self.events()
            if self.state == MENU:
                self.Renderer.draw_start_menu()
            elif self.state == RUNNING:
                self.update()
                self.animator.animate(self.dt)
                self.Renderer.draw_game()
            elif self.state == PAUSED:
                self.Renderer.draw_pause()
            elif self.state == INVENTORY:
                self.Renderer.draw_inventory()
            elif self.state == CHARACTER_SCREEN:
                self.Renderer.draw_character_screen()
        
        if not self.player.is_alive():
            self.Renderer.draw_game_over()
Ejemplo n.º 3
0
def main():
    init()
    global width, height, animator
    size = width, height = 1920, 1080
    screen = pygame.display.set_mode(size, FULLSCREEN)
    gameClock = time.Clock()
    animator = Animation()

    #load default background
    background = pygame.sprite.Group()
    bg1 = SpriteRemix.Background(
        transform.scale(
            image.load("Assets\\backgrounds\outsidebg.png").convert(),
            (1920, 1080)))
    bg2 = SpriteRemix.Background(
        transform.scale(
            image.load("Assets\\backgrounds\outsidebg.png").convert(),
            (1920, 1080)))
    bg1.stateVal = 1  #slowscroll
    bg2.stateVal = 1  #slowscroll
    bg1.add(background)
    bg2.add(background)

    #load ui, TODO: encapsulate this shit
    ui = pygame.sprite.Group()
    health = SpriteRemix.UI(
        transform.scale(
            image.load("Assets\\sprites\\ui\\health.png").convert(),
            (596, 72)))
    healthbar = SpriteRemix.UI(
        transform.scale(
            image.load("Assets\\sprites\\ui\\healthbar.png").convert_alpha(),
            (840, 84)))
    health.add(ui)
    healthbar.add(ui)

    #create cursor and add it to a sprite group, can only hold 1 cursor at a time
    cursors = pygame.sprite.GroupSingle()
    crsr = SpriteRemix.Cursor(
        transform.scale(
            pygame.image.load(
                "Assets\\sprites\\cursors\\crosshair1.png").convert_alpha(),
            (70, 70)))
    crsr.add(cursors)

    #create pc and add it to a sprite group
    #TODO: need an initializer class for player that loads projectiles and weapons and shit
    pc = pygame.sprite.Group()
    playerSprite = PlayerCharacterSprite()
    animator.load(playerSprite)
    playerSprite.add(pc)

    #weapon, this shit is f*****g retarded
    pcAccessory = pygame.sprite.Group()
    playerWeapon = SpriteRemix.Weapon()
    animator.load(playerWeapon)
    playerWeapon.add(pcAccessory)

    #create baddies and add them to a sprite group
    baddies = sprite.Group()
    baddySprite = EnemyCharacterSprite("notzigrunt")
    animator.load(baddySprite)
    baddySprite.add(baddies)

    #create doodads and add them to a sprite group
    doodads = sprite.Group()
    box = SpriteRemix.Doodad(
        pygame.image.load("Assets\\sprites\\doodads\\box.png").convert())
    #alsoBox = Doodad(pygame.image.load("Assets\\sprites\\doodads\\box.png").convert())
    box.add(doodads)
    #alsoBox.add(doodads)

    #initialize projectile sprite group (obv nothing to put here at startup
    projectiles = sprite.Group()

    #floating combat text
    combatTextArr = []

    #place everything
    bg1.rect.topleft = [-1920, 0]
    bg2.rect.topleft = [0, 0]

    healthbar.rect.topleft = [50, 50]
    health.rect.topleft = [healthbar.rect.left + 239, healthbar.rect.top + 5]

    playerSprite.rect.bottomleft = [100, height - 50]
    playerWeapon.rect.midright = playerSprite.rect.midleft

    baddySprite.rect.bottom = height
    baddySprite.rect.left = 960

    box.rect.left = 540
    box.rect.bottom = height
    '''alsoBox.rect.left = 920
    alsoBox.rect.bottom = height'''

    #create a list of all sprite groups
    entities = [pc.sprites(), baddies.sprites()]
    sprites = [
        pc, pcAccessory, baddies, doodads, projectiles, background, ui, cursors
    ]

    while 1:

        now = time.get_ticks()

        #this for loop processes all inputs in the event queue
        events = pygame.event.get()
        for event in events:

            #close window and quit if x is clicked or esc is pressed
            if event.type == QUIT or (event.type == KEYDOWN
                                      and event.key == K_ESCAPE):
                quit()
                sys.exit()

            #track the cursor
            if event.type == MOUSEMOTION:
                crsr.rect.centerx = event.pos[0]
                crsr.rect.centery = event.pos[1]

            #if no input is given, this remains True, animation reflects that.
            if not playerSprite.state["ducking"]:
                playerSprite.state["idle"] = True

            #only control pc if pc not dead
            #may be simplified by banning control input events when pc dies.
            if not playerSprite.state["dying"] and not playerSprite.state[
                    "dead"]:

                #movement d-right a-left space-jump
                if not playerSprite.state["ducking"]:
                    if event.type == KEYUP and event.key == K_d:
                        playerSprite.rightDash = now
                        Movement.accel(playerSprite, -playerSprite.velocity[0])
                        Movement.coast(playerSprite, playerSprite.velocity[0])
                        if playerSprite.velocity[0] == 0:
                            playerSprite.state["running"] = False
                        playerSprite.idleTime = 0
                        playerSprite.state["idle"] = False

                    elif event.type == KEYUP and event.key == K_a:
                        playerSprite.leftDash = now
                        Movement.accel(playerSprite, -playerSprite.velocity[0])
                        Movement.coast(playerSprite, playerSprite.velocity[0])
                        if playerSprite.velocity[0] == 0:
                            playerSprite.state["running"] = False
                        playerSprite.idleTime = 0
                        playerSprite.state["idle"] = False

                    elif event.type == KEYDOWN and event.key == K_d:
                        if now - playerSprite.rightDash > 250:
                            Movement.accel(playerSprite, 12)
                        else:
                            Movement.accel(playerSprite, 24)
                        playerSprite.leftDash = 0
                        playerSprite.xflip = False
                        if not playerSprite.state[
                                "jumping"] and not playerSprite.state[
                                    "falling"]:
                            playerSprite.state["running"] = True
                        playerSprite.idleTime = 0
                        playerSprite.state["idle"] = False

                    elif event.type == KEYDOWN and event.key == K_a:
                        if now - playerSprite.leftDash > 250:
                            Movement.accel(playerSprite, -12)
                        else:
                            Movement.accel(playerSprite, -24)
                        playerSprite.rightDash = 0
                        playerSprite.xflip = True
                        if not playerSprite.state[
                                "jumping"] and not playerSprite.state[
                                    "falling"]:
                            playerSprite.state["running"] = True
                        playerSprite.idleTime = 0
                        playerSprite.state["idle"] = False

                if playerSprite.velocity[0] == 0 and playerSprite.velocity[
                        1] == 0:
                    if event.type == KEYUP and event.key == K_s:
                        playerSprite.idleTime = 0
                        playerSprite.state["ducking"] = False
                        playerSprite.state["idle"] = False

                    elif event.type == KEYDOWN and event.key == K_s:
                        #crouching provides traction
                        playerSprite.xcoast = playerSprite.xcoast / 2.0
                        playerSprite.state["ducking"] = True
                        playerSprite.idleTime = 0
                        playerSprite.state["idle"] = False

                if event.type == KEYUP and event.key == K_SPACE:
                    playerSprite.velocity[1] = max(0, playerSprite.velocity[1])
                    playerSprite.state["jumping"] = False
                    playerSprite.idleTime = 0
                    playerSprite.state["idle"] = False

                if event.type == KEYDOWN and event.key == K_SPACE:
                    Movement.jump(playerSprite)
                    playerSprite.state["idle"] = False
                    playerSprite.state["jumping"] = True
                    playerSprite.idleTime = 0
                    playerSprite.state["idle"] = False

                #melee attack
                if event.type == MOUSEBUTTONDOWN and event.button == 1 and now - playerSprite.lastMelee > 300:
                    playerSprite.state["attacking"] = True
                    playerSprite.last["meleed"] = now
                    playerSprite.idleTime = 0
                    playerWeapon.hostile = True
                    playerSprite.state["idle"] = False

                if event.type == MOUSEBUTTONUP and event.button == 1:
                    playerWeapon.hostile = False
                    playerSprite.state["idle"] = False
                    playerSprite.state["attacking"] = False

                #ranged attack
                if event.type == MOUSEBUTTONDOWN and event.button == 3 and playerSprite.ammo > 0 and now - playerSprite.lastShot > 250:
                    playerSprite.ammo -= 1
                    projLoc = [
                        playerSprite.rect.right, playerSprite.rect.bottom - 130
                    ]
                    newProj = SpriteRemix.Projectile(projLoc,
                                                     event.pos,
                                                     speed=45)
                    animator.load(newProj)
                    newProj.add(projectiles)
                    newProj.rect.center = projLoc
                    playerSprite.last["shot"] = now
                    playerSprite.state["shooting"] = True
                    playerSprite.idleTime = 0
                    playerSprite.state["idle"] = False

            #ragdoll pc
            else:
                playerSprite.xcoast = playerSprite.velocity[0]
                playerSprite.ycoast = playerSprite.velocity[1]
                playerSprite.velocity = [0, 0]

        if playerSprite.state["idle"]:
            playerSprite.idleTime += gameClock.get_time()
            if playerSprite.idleTime >= 2000:
                playerSprite.state["idle"] = True
            elif playerSprite.idleTime >= 300:
                playerSprite.state["ready"] = True

        #baddy behavior
        '''if (now%1000 < 20) and baddySprite.stateVal != 1:
            Movement.jump(baddySprite)'''

        #replenish ammo over time
        if (now % 100 < 20) and playerSprite.ammo < 4:
            playerSprite.ammo += 1

        if (now % 5000 < 20):
            print(playerSprite.state)

        #sprites move, but these moves haven't been drawn yet
        for i in range(len(sprites) - 2):
            groupList = sprites[i].sprites()
            for aSprite in groupList:
                if aSprite.xcoast > 0:
                    if aSprite.xcoast > .6:
                        aSprite.xcoast += min(-aSprite.xcoast * .1, -.6)
                    else:
                        aSprite.xcoast = 0
                elif aSprite.xcoast < 0:
                    if aSprite.xcoast < -.6:
                        aSprite.xcoast += max(-aSprite.xcoast * .1, .6)
                    else:
                        aSprite.xcoast = 0
                if aSprite.ycoast > 0:
                    if aSprite.ycoast > .6:
                        aSprite.ycoast += min(-aSprite.ycoast * .1, -.6)
                    else:
                        aSprite.ycoast = 0
                elif aSprite.ycoast < 0:
                    if aSprite.ycoast < -.6:
                        aSprite.ycoast += max(-aSprite.ycoast * .1, .6)
                    else:
                        aSprite.ycoast = 0
                aSprite.rect = aSprite.rect.move([
                    aSprite.velocity[0] + aSprite.xcoast,
                    aSprite.velocity[1] + aSprite.ycoast
                ])

        # keeps characters in frame and handles collisions
        resolveFrame(sprites, entities, combatTextArr)

        # position the pc's weapon
        # TODO: encapsulate this, preferably in Animation once I figure out why it wasn't working there.
        playerWeapon.state = playerSprite.state
        if playerSprite.xflip:
            if playerWeapon.state["dead"] or playerWeapon.state["idle"]:
                playerWeapon.rect.topleft = [0, 0]
            elif playerWeapon.state["attacking"] or playerWeapon.state[
                    "shooting"]:
                playerWeapon.rect.midright = [
                    playerSprite.rect.midleft[0] + 8,
                    playerSprite.rect.midleft[1] - 62
                ]
            else:
                playerWeapon.rect.midleft = [
                    playerSprite.rect.midright[0],
                    playerSprite.rect.midright[1] + 58
                ]
            playerWeapon.xflip = True
        else:
            if playerWeapon.state["dead"] or playerWeapon.state["idle"]:
                playerWeapon.rect.topleft = [0, 0]
            elif playerWeapon.state["attacking"] or playerWeapon.state[
                    "shooting"]:
                playerWeapon.rect.midleft = [
                    playerSprite.rect.midright[0] - 8,
                    playerSprite.rect.midright[1] - 62
                ]
            else:
                playerWeapon.rect.midright = [
                    playerSprite.rect.midleft[0],
                    playerSprite.rect.midleft[1] + 58
                ]
            playerWeapon.xflip = False

        # only animate characters and projectiles so far (i = 0 is pc, i = 1 is baddies, i = 3 is projectiles, i = 4 is background)
        animator.animate([
            sprites[0].sprites(), sprites[1].sprites(), sprites[2].sprites(),
            sprites[4].sprites(), sprites[5].sprites()
        ], now)

        # refresh screen by drawing over previous frame with background
        screen.blit(bg1.image, bg1.rect)
        screen.blit(bg2.image, bg2.rect)

        # draw active CombatText objects and remove faded ones
        for combatText in combatTextArr[:]:
            if combatText.progress(now):
                combatText.draw(screen)
            else:
                combatTextArr.remove(combatText)

        # draw all the rest of the in-use assets
        for i in range(len(sprites)):
            if i != 5:  #don't draw UI
                # only draw visible sprites in each group
                # NOTE: this is probably bad, as I'd assume .draw() (sprite method that blits all sprites in a sprite group)is better
                # optimized, but we can't make it optionally draw
                # sprites unless we change everything to DirtySprites (a type built into pygame)
                for aSprite in sprites[i].sprites():
                    if aSprite.visible:
                        screen.blit(aSprite.image, aSprite.rect)

        # draw UI last
        if health.rect.width > 5.96 * playerSprite.health:
            health.image = transform.scale(
                health.image,
                (max(0, health.rect.width - 3), health.rect.height))
            health.rect = health.image.get_rect()
            health.rect.topleft = (289, 56)
        screen.blit(health.image, health.rect)
        screen.blit(healthbar.image, healthbar.rect)

        gameClock.tick(60)

        #game over check
        if playerSprite.health <= 0:
            defaultText = font.Font(None, 120)
            gameOverSurface = defaultText.render("Game Over man, Game Over!",
                                                 True, (255, 0, 0))
            gameOverRect = gameOverSurface.get_rect()
            gameOverRect.center = (960, 540)
            screen.blit(gameOverSurface, gameOverRect)
            playerSprite.state["dead"] = True

        #victory check
        enemies = sprites[2].sprites()
        allDead = True
        for enemy in enemies:
            if enemy.stateVal != 4:
                allDead = False
        if allDead:
            defaultText = font.Font(None, 200)
            conglaturationSurface = defaultText.render("Conglaturation", True,
                                                       (255, 255, 255))
            conglaturationRect = conglaturationSurface.get_rect()
            conglaturationRect.center = (960, 540)
            screen.blit(conglaturationSurface, conglaturationRect)

        pygame.display.flip()