diff --git a/BigDict.py b/BigDict.py
new file mode 100644
index 0000000..7bf683e
--- /dev/null
+++ b/BigDict.py
@@ -0,0 +1,64 @@
+BigDict = {"Enemies":{}, "Pickups":{}, "WorldObjects":{}, "Players":{}, "Items":{}}
+BigDict["Enemies"] = {"Hard":{}, "Medium":{}, "Easy":{}}
+BigDict["Players"] = {"Unarmed":{}, "Knight":{}, "Ranger":{}, "Mage":{}}
+BigDict["Pickups"] = {"Weapons":{"Ranged":{}, "Melee":{}, "Magic":{}}}
+BigDict["WorldObjects"]={"Spawnpoints":[]}
+#dictionary of enemies
+BigDict["Enemies"]["Hard"] = {"CastleSlime":["slime00cas", 100, 10, 100, "random"],"LBomb":["bomb", 100, 10, 100, "random"], "SkeletonArcher": ["Skeleton Archer", 100, 15, 100, "range"], "ghost": ["ghost", 100, 20, 150, "chase"]}
+BigDict["Enemies"]["Medium"] = {"DesertSlime":["slime00des", 100, 7, 75, "random"]}
+BigDict["Enemies"]["Easy"] = {"Slime":["slime", 100, 5, 50, "random"]}
+#Order of Attributes: Spritename, health, attack, speed, if follow(AI type)
+
+#Dictionary of playable characters.
+BigDict["Players"]["Unarmed"] = ["Unarmed", .75, .75]
+BigDict["Players"]["Knight"] = ["Knight", 1, .75]
+BigDict["Players"]["Ranger"] = ["Ranger", .75, 1]
+BigDict["Players"]["Mage"] = ["Mage", 1, 1]
+#Order of Attributes: Spritename, health, speed.
+#health and speed will be multiplied by base class.
+#Dictionary of pickups.
+#--Weapons--#
+#Order of Attributes: Spritename, attackType, Cooldown(Seconds), Power cooldown(Seconds), Damage, Power Damage, Range(pixels), Speed.
+BigDict["Pickups"]["Weapons"]["Ranged"] = [["Bow", "proj", 0.5, 2.5, 0.75, 1.25, 1024, 1.0],
+ ["Crossbow", "proj", 0.75, 2.0, 1.25, 1.0, 512, 175],
+ ["Rifle", "proj", 0.75, 4.3, 1.4, 2.0, 1536, 2.2],
+ ["Bomb", "Special", 1.0, 7.0, 2.5, 2.5, 256, 0.5]]
+
+# The outer list is the direction in which the player is facing (W, E, N, S)
+# The tuples inside that are the points for each frame
+swordPtList = [ [[(-68, -23), (-48, -23)], [(-79, -23), (-58, -23)], [(-88, -23), (-68, -23)], [(-68, -23), (-48, -23)]], #swordPtList[0]
+ [[(69,-23), (49, -23)], [(90,-23), (70, -23)], [(80,-23), (60, -23)], [(74,-23), (54, -23)]], #swordPtList[1]
+ [[(28, -80), (28, -60)], [(28, -99), (28, -79)], [(28, -94), (28, -74)], [(28, -85), (28, -65)]], #swordPtList[2]
+ [[(-28, 38), (-28, 18)], [(-28, 56), (-28, 36)], [(-28, 51), (-28, 31)], [(-28, 39), (-28, 19)]] ] #swordPtList[3]
+
+scythePtList = [ [[(-30,-59), (-60, -29)], [(-30,-59), (-60, -29)], [(-30,-59), (-60, -29)], [(-30,-59), (-60, -29)]], #scythePtList[0]
+ [[(34,-59), (64, -29)], [(34,-59), (64, -29)], [(34,-59), (64, -29)], [(34,-59), (64, -29)]], #scythePtList[1]
+ [[(23,-70), (-13, -100)], [(23,-70), (-13, -100)], [(23,-70), (-13, -100)], [(23,-70), (-13, -100)]], #scythePtList[2]
+ [[(-23, 16), (7, 36)], [(-23, 16), (7, 36)], [(-23, 16), (7, 36)], [(-23, 16), (7, 36)]] ] #scythePtList[3]
+
+BigDict["Pickups"]["Weapons"]["Melee"] = [["Sword", "stab", 0.6, 3, 1.0, 1.5, 0.0, 0.0, swordPtList],
+ ["StrongSword", "stab", 0.6, 3, 2.0, 3.0, 0.0, 0.0, swordPtList],
+ ["Scythe", "arc", 0.75, 5.1, 1.8, 2.5, 0.0, 0.0, scythePtList],
+ ["Axe", "stab", 0.75, 4.5, 1.5, 1.75, 0.0, 0.0],
+ ["Spear", "stab", 0.65, 4.4, 1.6, 2.0, 0.0, 0.0]]
+
+BigDict["Pickups"]["Weapons"]["Magic"] = [["MagicStaff", "proj", 0.75, 2.0, 1.0, 2.0, 1024, 0.6],
+ ["FireStaff", "proj", 0.75, 2.0, 1.0, 1.5, 1024, 0.6],
+ ["LightningStaff", "proj", 0.75, 2.0, 1.0, 1.5, 1024, 0.6],
+ ["EarthStaff", "proj", 0.75, 2.0, 1.0, 1.0, 1024, 0.6],
+ ["DoomStaff", "Special", 0.75, 2.5, 1.4, 2.5, 1024, 0.6]]
+
+#--Pickups--#
+BigDict["Pickups"]["Compass"] = ["Compass", True]
+#Order of Attributes: Spritename, Boolean set to true.
+
+
+
+BigDict["Items"]["HealthPot"] = ["Potion_RedPU", "heal", 30]
+#BigDict["Items"]["Sword"] = ["Claymore", "changeClass", ("Melee", BigDict["Pickups"]["Weapons"]["Melee"][0])]
+BigDict["Items"]["Staff"] = ["staffPU", "changeClass", ("Magic", BigDict["Pickups"]["Weapons"]["Magic"][0])]
+BigDict["Items"]["Scythe"] = ["ScythePU", "changeClass", ("Melee", BigDict["Pickups"]["Weapons"]["Melee"][2])]
+BigDict["Items"]["Bow"] = ["BowPU", "changeClass", ("Ranged", BigDict["Pickups"]["Weapons"]["Ranged"][0])]
+BigDict["Items"]["StrongSword"] = ["ClaymorePU", "changeClass", ("Melee", BigDict["Pickups"]["Weapons"]["Melee"][1])]
+
+
diff --git a/application.py b/application.py
new file mode 100644
index 0000000..eabdfe5
--- /dev/null
+++ b/application.py
@@ -0,0 +1,239 @@
+import math2d
+import pygame
+import pane
+import idevice
+import world
+import spriteDataBase
+import soundDataBase
+import gui_manager
+import BigDict
+import random
+class Application(object):
+ """ Controls:
+ 1. all pygame objects
+ 2. the list of panes
+ 3. world
+ 4. sprite database
+ 5. sound database
+ Calls all methods of every other object in the game"""
+
+ def __init__(self):
+ """ Initializes all data """
+ self.quit = False
+ self.pygameStartup()
+
+ self.soundeffects = soundDataBase.SoundDBase("sounds\\sound effects")
+
+ self.GManager = gui_manager.GUI_Manager(self)
+
+ # Temporarily create a single player (keyboard) & pane
+ self.IDeviceMasterList = [idevice.Keyboard()]
+ numSticks = pygame.joystick.get_count()
+ count = 0
+ while numSticks > 0:
+ self.IDeviceMasterList.append(idevice.Gamepad(count))
+ numSticks -= 1
+ count += 1
+
+ self.spriteSheets = spriteDataBase.ImageDBase("imgs\\player")
+ self.spriteSheets.addAdditionalDirectory("imgs\\enemy")
+ self.spriteSheets.addAdditionalDirectory("imgs\\pickup items")
+ #self.spriteSheets = spriteDataBase.ImageDBase("..\\Art") # Designate a path to your spritesheets folder.
+ #self.spriteSheets.addAdditionalDirectory("..\\Art\\Enemies")
+ #self.spriteSheets.addAdditionalDirectory("..\\Art\\Pick up items")
+ self.tileDBase = spriteDataBase.ImageDBase("imgs\\floor")
+ self.tileDBase.addAdditionalDirectory("imgs\\wall")
+
+ self.guiDBase = spriteDataBase.ImageDBase("imgs\\gui")
+
+ #SOUND CODE...
+ self.soundeffects = soundDataBase.SoundDBase("sounds\\sound effects")
+ self.music = soundDataBase.SoundDBase("sounds\\music")
+ self.songs = 6 #How many fight themes are in the music folder...
+ self.musicOrder = [random.randint(1,self.songs),] #self.musicOrder will have randomized numbers from one to however many songs there are.
+ #Songs are looped in this order, in-game. The following code randomly appends the next numbers...
+ newval = None
+ numberUnused = False
+ counter = 1
+ while counter < self.songs: #There are six songs so far.
+ val = random.randint(1, self.songs)
+ for i in self.musicOrder:
+ if val == i:
+ numberUnused = False
+ break
+ else:
+ numberUnused = True
+ if numberUnused:
+ self.musicOrder.append(val)
+ counter += 1
+
+ self.musicIndex = 0 #This index is incremented in self.update()
+ #self.world = world.World((6,4), self.tileDBase, self.spriteSheets)
+ self.panes = []
+ # TEMPORARY -- just to test idevice code.
+ self.testPos = math2d.Vector2(400,300)
+
+ def onAction(self, action, devNum):
+ """ Called once when an 'action' is made (e.g. 'attack',
+ 'pattack', 'interact', 'use') """
+ # TO-DO: Call Player functions
+ #print("on action num",devNum)
+ dic = self.GManager.handleAction(action,devNum)
+ #if(self.GManager.mode=="create" or self.GManager.mode=="menu" or self.GManager.mode=="options" or self.GManager.mode=="title"):
+ if dic != None:
+ #print(dic)
+ dim = dic["dim"]
+ #print(dim)
+ player = dic["player"]
+ #maps = dic["mapping"]
+ # Temporarily create a single player (keyboard) & pane
+ self.createGame(dic)
+
+## def setPaneInput(self, dic):
+## maps = dic["mapping"]
+## for i in range(len(maps)):
+## self.panes.idevice = self.IDeviceMasterList[maps[i][1]]
+
+
+ def createGame(self, dic):
+ """ Called after the countdown in the input-chooser screen. Creates the world and moves all players to
+ a random spawn point. """
+ dim = dic["dim"]
+ player = dic["player"]
+ maps = dic["mapping"]
+ self.panes = []
+ self.world = world.World(dim, self.tileDBase, self.spriteSheets)
+ if(player == 1):
+ WorldPos = random.choice(BigDict.BigDict["WorldObjects"]["Spawnpoints"])
+ self.panes.append(pane.Pane(self.IDeviceMasterList[maps[0][1]], WorldPos, self.spriteSheets, (1024,768),self.world,self, (0,0), 0)) #-------------Resolution Changed From (800,600) to (1024,768)
+ if(player == 2):
+ WorldPos = random.choice(BigDict.BigDict["WorldObjects"]["Spawnpoints"])
+ self.panes.append(pane.Pane(self.IDeviceMasterList[maps[0][1]], WorldPos, self.spriteSheets, (512,768),self.world, self, (0,0), 0))
+ WorldPos = random.choice(BigDict.BigDict["WorldObjects"]["Spawnpoints"])
+ self.panes.append(pane.Pane(self.IDeviceMasterList[maps[1][1]], WorldPos, self.spriteSheets, (512,768),self.world, self, (512,0), 1)) #NEED TO CHANGE
+ if(player == 3):
+ WorldPos = random.choice(BigDict.BigDict["WorldObjects"]["Spawnpoints"])
+ self.panes.append(pane.Pane(self.IDeviceMasterList[maps[0][1]], WorldPos, self.spriteSheets, (512,384),self.world,self,(0,0), 0))
+ WorldPos = random.choice(BigDict.BigDict["WorldObjects"]["Spawnpoints"])
+ self.panes.append(pane.Pane(self.IDeviceMasterList[maps[1][1]], WorldPos, self.spriteSheets, (512,384),self.world,self,(512,0), 1))
+ WorldPos = random.choice(BigDict.BigDict["WorldObjects"]["Spawnpoints"])
+ self.panes.append(pane.Pane(self.IDeviceMasterList[maps[2][1]], WorldPos, self.spriteSheets, (512,384),self.world,self,(0,384), 2)) # 3 player split screen
+ if(player == 4):
+ WorldPos = random.choice(BigDict.BigDict["WorldObjects"]["Spawnpoints"])
+ self.panes.append(pane.Pane(self.IDeviceMasterList[maps[0][1]], WorldPos, self.spriteSheets, (512,384),self.world,self,(0,0), 0))
+ WorldPos = random.choice(BigDict.BigDict["WorldObjects"]["Spawnpoints"])
+ self.panes.append(pane.Pane(self.IDeviceMasterList[maps[1][1]], WorldPos, self.spriteSheets, (512,384),self.world,self,(512,0), 1))
+ WorldPos = random.choice(BigDict.BigDict["WorldObjects"]["Spawnpoints"])
+ self.panes.append(pane.Pane(self.IDeviceMasterList[maps[2][1]], WorldPos, self.spriteSheets, (512,384),self.world,self,(0,384), 2))
+ WorldPos = random.choice(BigDict.BigDict["WorldObjects"]["Spawnpoints"])
+ self.panes.append(pane.Pane(self.IDeviceMasterList[maps[3][1]], WorldPos, self.spriteSheets, (512,384),self.world, self,(512,384), 3)) #4 player split screen
+
+ def refresh(self):
+ pygame.joystick.quit()
+ pygame.joystick.init()
+ self.IDeviceMasterList = [idevice.Keyboard()]
+ numSticks = pygame.joystick.get_count()
+ count = 0
+ while numSticks > 0:
+ self.IDeviceMasterList.append(idevice.Gamepad(count))
+ numSticks -= 1
+ count += 1
+ #self.panes = [pane.Pane(self.IDeviceMasterList[count], math2d.Vector2(400,300), self.spriteSheets, (1024,768),self.world)]
+
+ def run(self):
+ """ Starts the game. """
+ while not self.quit:
+ dT = self.clock.tick() / 1000.0
+ if pygame.key.get_pressed()[pygame.K_ESCAPE]:
+ self.quit = True
+ self.update(dT)
+ self.render()
+ self.pygameShutdown()
+
+ def render(self):
+ # Note: We'll probably need to pass the players to this
+ # function eventually
+ #if self.GManager.mode != "game":
+ #if self.GManager.mode in ("game", "title", "main", "menu", "create"):
+ if self.GManager.mode != "game" and self.GManager.mode != "input":
+ self.GManager.render(self.screen)
+ else:
+ for pane in self.panes:
+ pane.render(self.panes)
+ self.GManager.render(pane.surface, pane, self.panes)
+ if len(self.panes) == 1:
+ self.screen.blit(self.panes[0].surface, (0,0))
+ if len(self.panes) == 2:
+ self.screen.blit(self.panes[0].surface, (0,0))
+ self.screen.blit(self.panes[1].surface, (self.screen.get_width()/2,0))
+ if len(self.panes) == 3:
+ self.screen.blit(self.panes[0].surface, (0,0))
+ self.screen.blit(self.panes[1].surface, (self.screen.get_width()/2,0))
+ self.screen.blit(self.panes[2].surface, (0,self.screen.get_height()/2))
+ if len(self.panes) == 4:
+ self.screen.blit(self.panes[0].surface, (0,0))
+ self.screen.blit(self.panes[1].surface, (self.screen.get_width()/2,0))
+ self.screen.blit(self.panes[2].surface, (0,self.screen.get_height()/2))
+ self.screen.blit(self.panes[3].surface, (self.screen.get_width()/2,self.screen.get_height()/2))
+ if len(self.panes) == 2:
+ self.screen.blit(self.guiDBase.get("twoPlayer_Split"), (0,0))
+ elif len(self.panes) >= 3:
+ self.screen.blit(self.guiDBase.get("fourPlayer_Split"), (0,0))
+ # Draw the fps on the upper-left
+ self.screen.blit(self.debugFont.render("FPS: " + str(round(self.clock.get_fps(),1)), False, (255,0,0), (0,0,0)), (5,5))
+ pygame.display.flip()
+
+ def pygameStartup(self):
+ """ Load all pygame objects """
+ pygame.display.init()
+ pygame.joystick.init()
+ pygame.font.init()
+ pygame.mixer.init()
+ self.screen = pygame.display.set_mode((1024,768),pygame.FULLSCREEN)
+ self.debugFont = pygame.font.SysFont("Courier New", 14) # Temporary for testing
+ self.clock = pygame.time.Clock()
+
+ def update(self, dT):
+ eList = pygame.event.get()
+ volume = self.GManager.Update(dT) / 10
+ if self.GManager.mode == "game":
+ if pygame.mixer.music.get_busy() == False:
+ MusicSelection = "FightTheme"+str(self.musicOrder[self.musicIndex])+".mp3"
+ print(MusicSelection)
+ pygame.mixer.music.load("sounds\\music\\"+ MusicSelection)
+ pygame.mixer.music.set_volume(volume)
+ pygame.mixer.music.play(0, 0.0)
+ self.musicIndex += 1
+ if self.musicIndex > self.songs - 1:
+ self.musicIndex = 0
+
+ self.world.update(dT)
+ for dev in self.IDeviceMasterList:
+ dev.update(eList, self)
+ if self.GManager.mode == "game" or self.GManager.mode == "input":
+ for p in self.panes:
+ p.update(dT, eList, self.world, self)
+
+ else:
+ if pygame.mixer.music.get_busy() == False:
+ MusicSelection = "Intro.mp3"
+ print(MusicSelection)
+ pygame.mixer.music.load("sounds\\music\\"+ MusicSelection)
+ pygame.mixer.music.set_volume(volume)
+ pygame.mixer.music.play(0, 0.0)
+
+
+ def onMovement(self, horiz, vert):
+ """ Called when a direction change is made (
+ i.e. gamepad: dpad / analog, keyboard: u/d/l/r).
+ IMPORTANT: This function should only be called
+ once for a movement """
+ # TO-DO: Call Player functions
+ self.GManager.handleMovement(horiz, vert)
+
+ def pygameShutdown(self):
+ """ Shuts pygame down """
+ pygame.font.quit()
+ pygame.joystick.quit()
+ pygame.mixer.quit()
+ pygame.display.quit()
diff --git a/entity.py b/entity.py
new file mode 100644
index 0000000..c6ee7fd
--- /dev/null
+++ b/entity.py
@@ -0,0 +1,836 @@
+import pygame
+import math2d
+import random
+import BigDict
+import copy
+import world
+
+class Entity(object):
+ """ A Generic thing that is draw on the screen.
+ Some common things:
+ 1. Sprite
+ 2. Position
+ 3. Size
+ We will create derived classes from this for:
+ Players
+ Enemies
+ Loot
+ Pressure Plates
+ ...
+ """
+ def __init__(self, pos, spriteName, spriteDBase):
+ self.state = 0 # When 0, alive. When 1, dying. When 2, it is dead, and needs to be removed
+ self.spriteDB = spriteDBase # Needed for creating projectiles
+ self.sprite = spriteDBase.get(spriteName)
+ if not isinstance(pos, math2d.Vector2): # Making sure the position of this Entity is a Vector2 object
+ self.pos = math2d.Vector2(pos[0], pos[1]) # WORLD position of the bottom-middle of the character (feet when standing)
+ else:
+ self.pos = pos
+ self.size = 20 # Radius of the bounding circle (centered at self.pos) used for wall / trap hit-detection
+ # Normal size of (nearly) all entities is set here
+ self.hitRadius = 5 # The radius of the circle used for weapons
+ self.damageScale = 25 # Multiplied by the weapon damage factor
+ # ^^^ These two are placed here to be used for projectiles and the Player
+
+ def render(self, surf, cameraPos):
+ """ Draw a blobby shadow underneath the character """
+ # We might need to replace this with a "shadow" image; the corners
+ # of the rectangular surface appear around the circle as-is
+ tempS = pygame.Surface((self.size * 2, self.size * 2)) # Create an off-screen, semi-transparent (soon) surface
+ tempS.fill((255,255,255)) # Temporary?
+ tempS.set_alpha(64) # Arbitrary number for transparency; lower numbers = more transparent
+ tempS.set_colorkey((255,255,255))
+
+ pygame.draw.circle(tempS, (0,0,0), (self.size, self.size), self.size) # Draws the shadow
+ surf.blit(tempS, (self.pos[0] - cameraPos[0] - self.size, self.pos[1] - cameraPos[1] - self.size)) # Blit the shadow to the screen at self.pos
+
+ def update(self, dT):
+ """ Do any pertinent updates """
+ pass
+
+class Projectile(Entity):
+ """ A class for projectiles. Whenever a projectile is fired, create
+ a projectile object. """
+ def __init__(self, pos, spriteName, spriteDBase, firingWeapon, direction, shooter, atkType):
+ Entity.__init__(self, pos, spriteName, spriteDBase)
+ # NOTE: The position of the projectile is the point used for hit detection (the middle, front edge based on direction)
+ self.range = firingWeapon[6]
+ self.distTravelled = 0 # The amount of distance the projectile has already travelled
+ self.speed = 500 * firingWeapon[7]
+ if atkType == "Normal":
+ self.damage = firingWeapon[4]
+ elif atkType == "Power":
+ self.damage = firingWeapon[5]
+ self.direction = direction # W = 0, E = 1...
+ self.shooter = shooter # The object that fired this projectile - so the projectile doesn't hit the one that shot it
+ self.firingWeapon = firingWeapon[0]
+ self.drawOffset = math2d.Vector2(0, 0)
+
+ # self.drawOffset is the distance from self.pos to the top left corner of the sprite
+ if self.direction == 0 and self.firingWeapon == "Bow":
+ # Already facing correct direction; no rotation needed
+ self.drawOffset = math2d.Vector2(0, self.sprite.get_height() // 2)
+
+ elif self.direction == 1 and self.firingWeapon == "Bow":
+ self.sprite = pygame.transform.rotate(self.sprite, 180)
+ self.drawOffset = math2d.Vector2(self.sprite.get_width(), self.sprite.get_height() // 2)
+
+ elif self.direction == 2 and self.firingWeapon == "Bow":
+ self.sprite = pygame.transform.rotate(self.sprite, -90)
+ self.drawOffset = math2d.Vector2(self.sprite.get_width() // 2, 0)
+
+ elif self.direction == 3 and self.firingWeapon == "Bow":
+ self.sprite = pygame.transform.rotate(self.sprite, 90)
+ self.drawOffset = math2d.Vector2(self.sprite.get_width() // 2, self.sprite.get_height())
+
+ elif self.firingWeapon == "MagicStaff":
+ self.drawOffset = math2d.Vector2(32, 32)
+
+ def render(self, surf, cameraPos):
+ surf.blit(self.sprite, (int(self.pos[0] - self.drawOffset[0] - cameraPos[0]), int(self.pos[1] - self.drawOffset[1] - cameraPos[1])))
+ ####### TEMPORARY #######
+## pygame.draw.circle(surf, (255,0,0), (int(self.pos[0] - cameraPos[0]), int(self.pos[1] - cameraPos[1])), self.hitRadius)
+
+ def update(self, dT, checkList, world):
+ """ Updates the projectile. Performs the following:
+ 1: Move the projectile in the correct direction
+ 2: Determine how far the projectile has travelled
+ - If it has travelled the maximum range, change its state to 2 (dead)
+ 3: Check to see if the projectile hits anything """
+ displacement = self.speed * dT # This is the amount of distance the projectile has moved
+
+ ####### Move the projectile #######
+ if self.direction == 0: # West
+ self.pos[0] -= displacement
+ elif self.direction == 1: # East
+ self.pos[0] += displacement
+ elif self.direction == 2: # North
+ self.pos[1] -= displacement
+ elif self.direction == 3: # South
+ self.pos[1] += displacement
+
+ self.distTravelled += displacement # Add to the total distance travelled by the projectile
+ if self.distTravelled >= self.range: # The projectile has moved its maximum range
+ self.state = 2
+ # If this gets code stomped again, someone dies
+ if not (world.isSpotWalkable((self.pos[0]), (self.pos[1]))):
+ self.state = 2
+
+ for obj in checkList:
+ # Does the actual hit detection for this projectile
+ if obj == self.shooter: # Don't hit yourself
+ continue
+ if isinstance(obj, Player) or isinstance(obj, Enemy):
+ # Calculates the distance (scalar) between the hit point and the object
+ distance = math2d.Vector2(obj.pos[0] - self.pos[0], obj.pos[1] - self.pos[1] - 10)
+ distance = distance.length()
+
+ if distance < obj.size + self.hitRadius: # If the object is hit, handle the hit (below)
+ if isinstance(obj, Player):
+ obj.lastHitByPlayer = True
+ obj.health -= self.damage * self.damageScale
+ self.state = 2
+
+class Mover(Entity):
+ """ A generic class of any entity that moves.
+ Some common things:
+ 1. Has the ability to move
+ 2. Has a health attribute
+ 3. Able to perform actions (such as attack)
+ 4. Able to trigger (most/all) traps
+ We will create derived classes from this for:
+ 1. Player
+ 2. Enemy
+ """
+ def __init__(self, pos, spriteName, spriteDBase):
+ Entity.__init__(self, pos, spriteName, spriteDBase)
+ self.name = spriteName
+ self.speed = 200 # The speed of the Mover, in pixels per second - normal speed is set here
+ self.tempSpeed = self.speed # If the speed of the object changes, this changes - NOT self.speed
+ self.health = 100.0 # The health this object has - Will be changed later to reflect what class the player is (maybe?)
+ self.maxHealth = 100 # The maximum health this object can have
+ self.curAction = 0 # Walk, BeingHit, Falling, Idle, Dying
+ self.drawDirection = 0 # W, E, N, S
+ self.curFrame = 0
+ self.animDelay = 0.15 # The amount of time an object is in the same frame until it changes
+ self.frameTime = 0.0 # The amount of time this object has been in the same frame
+ self.projList = [] # A list of projectiles fired by this Mover
+ self.lastHitByPlayer = False # Though only used for the player, the entire Mover update method would have to be copied if
+ # this was ONLY in the Player class. This is why there is an isinstance check in update
+ def changeAction(self, newAction):
+ if self.state == 0:
+ self.curAction = newAction
+ if (newAction != 0): # Makes sure it's not walking, so it doesn't reset the animation
+ self.curFrame = 0 # As the action changes, the new action
+ # starts from the first frame
+ def changeDrawDirection(self, newDir):
+ """ Directions are as follows:
+ W = 0
+ E = 1
+ N = 2
+ S = 3 """
+ if self.state == 0:
+ if newDir != self.drawDirection:
+ self.drawDirection = newDir
+
+ if (self.curAction != 0):
+ self.curFrame = 0 # As the direction changes, the action
+ # starts from the first frame
+ def update(self, dT, checkList, world):
+ """ Modify curFrame (every self.animDelay seconds), wrapping around to
+ 0 if we're at the end of the animation (use self.curAction) """
+ if self.health <= 0:
+ self.health = 0 # To stop health from going negative if strange things happen
+ if self.state == 0:
+ self.death()
+
+ if self.health > self.maxHealth:
+ self.health = self.maxHealth # Your health cannot exceed its maximum
+
+ self.frameTime += dT # Increment self.frameTime based on the time that has passed
+
+ for proj in self.projList:
+ if proj.state == 2: # The projectile is dead and needs to be removed
+ self.projList.remove(proj)
+ continue # Don't try to update the projectile afer it's been removed
+ proj.update(dT, checkList, world)
+
+ if self.frameTime >= self.animDelay:
+ self.curFrame += 1
+ self.frameTime = 0 # Reset self.frameTime if the frame changes
+ if self.curFrame >= 4:
+ self.curFrame = 0
+ self.frameTime = 0 # Reset self.frameTime if the frame changes
+ if self.state == 1: # If the object has gone through the "Dying" animation...
+ if not self.lastHitByPlayer and isinstance(self, Player):
+ self.respawn(world)
+ else:
+ self.state = 2 # ... change its state to "Dead"
+
+ self.tempSpeed = self.speed # If tempSpeed is changed, set it back to the original speed
+
+ def death(self):
+ """ Handles the death of the mover. Changes self.curAction to 4 (dying) and self.state to 1.
+ Update changes the state of the object to 2 when it's done dying """
+ if isinstance(self, Player):
+ if self.weapon[0] == "Sword":
+ self.appPtr.sounds["exploding head"].play()
+ else:
+ self.appPtr.sounds["death"].play()
+ self.curAction = 4 # The "Dying" action
+ self.state = 1 # State is now "Dying"
+ self.curFrame = 0
+ self.frameTime = 0
+
+ def fall(self):
+ """ Handles the death of the mover, IF they have fallen into a pit. """
+ self.curAction = 2 # The "Falling" action
+ self.state = 1
+ self.curFrame = 0
+ self.frameTime = 0
+
+ def render(self, surf, cameraPos):
+ """ Draw the blobby shadow (from Entity.render), then draw the current
+ sprite on top of it. Also, draws a green health bar above the current sprite
+ whose width is dependent on self.health. """
+ # Draws the shadow by using the render method of the Entity base class
+ Entity.render(self, surf, cameraPos)
+ # Draws the correct sprite on top of the shadow; for the rect argument,
+ # the first argument is the current column, the second argument is the
+ # currentAction (which determines which group of 4 rows to use) and which
+ # direction (which determines which of the 4 rows to use). Each sprite is 64x64
+ surf.blit(self.sprite, (self.pos[0] - 32 - cameraPos[0], self.pos[1] - 64 - cameraPos[1]), (self.curFrame * 64,
+ self.curAction * 256 + self.drawDirection * 64, 64,64))
+ # Render all projectiles associated with this object
+ for proj in self.projList:
+ proj.render(surf, cameraPos)
+ # Health bar
+ pygame.draw.rect(surf, (0,0,0), (self.pos[0] - 49 - cameraPos[0], self.pos[1] - 65 - cameraPos[1], 102, 4))
+ pygame.draw.rect(surf, (0,255,0), (self.pos[0] - 48 - cameraPos[0], self.pos[1] - 64 - cameraPos[1], self.health, 2))
+
+class Player(Mover):
+ """ A player-controlled entity. Able to access Loot. Able to trigger all traps.
+ Has a weapon dictionary-attribute that includes:
+ Weapon Type
+ Range
+ Attack Damage
+ Power-Attack Type
+ Power-Attack Recharge Rate
+ Power-Attack Damage
+ which will change as the player changes weapons."""
+ def __init__(self, pos, spriteName, spriteDBase, app):
+ Mover.__init__(self, pos, spriteName, spriteDBase)
+ self.is_attacking = False # True when the ANIMATION for the attack is going (actually checking for hits)
+ self.atkCooldown = 1.0 # Determines when the player can attack. 1.0 is ready (percentage)
+ self.is_p_atk = False # True when the ANIMATION for the p atk is going (checking for hits)
+ self.paCharge = 1.0 # Determines when the player can use a power attack. 1.0 is fully charged (percentage)
+ self.pAttackColor = (0,0,255)
+ self.hand_action = 1 # Initially idle; works just like curAction does for rendering the knight, just for the hands
+ # Additional note: THIS NUMBER DOES NOT DIFFERENTIATE between attacking and otherwise
+ self.hand_frameTime = 0.0 # The amount of time the current hand frame has been shown - used with self.animDelay
+ self.hand_frame = 0 # The current frame for the hands
+ self.walkDmgTimer = 1.0 # When at 1.0, walking on an enemy will cause damage; if less than that, temporary invuln from this kind of dmg
+ self.lastHitByPlayer = False # Set to True if the player is hit by another player, until hit by something other than a player (including ALL traps)
+ # For self.weapon: Spritename, attackType, Cooldown (seconds), Power Cooldown (seconds), Damage, Power Damage, Range (pixels), Proj. Speed, hitPtList
+ self.weapon = BigDict.BigDict["Pickups"]["Weapons"]["Melee"][0] # Sets self.weapon to the Sword list
+ self.weaponSprite = spriteDBase.get(self.weapon[0])
+ self.handSprite = spriteDBase.get("hands")
+ #Sound attributes
+ self.appPtr = app
+ self.lavaBool = False
+
+ def onMove(self, dT, horiz, vert, w):#Does this junk even do anything? Lol
+ """ horiz and vert are both floats between -1.0 and 1.0. Change self.pos """
+ if self.state == 0:
+ if (w.isSpotWalkable((self.pos[0]) + horiz * (self.size + self.tempSpeed * dT), (self.pos[1]))):
+ self.pos[0] += horiz * self.tempSpeed * dT
+ if (w.isSpotWalkable((self.pos[0]), (self.pos[1]) + vert * (self.size + self.tempSpeed * dT))):
+ self.pos[1] += vert * self.tempSpeed * dT
+ # Keeping these ^^^ as two separate if statements. Prevents "sticky" walls when pressing more than 1 direction.
+
+ def walkEnemyDmg(self, checkList):
+ """ Check against the checkList to see if you are standing on an enemy. If so,
+ cause damage to the player and set self.walkDmgTimer to 0.0. This will be incremented
+ in the update method back up to 1.0. """
+ if self.state == 0:
+ if self.walkDmgTimer < 1.0:
+ return None # If the timer is not full, no damage is taken, nothing happens
+ for ene in checkList:
+ if isinstance(ene, Enemy):
+ distance = (ene.pos - self.pos).length() # Gets distance between player pos and enemby pos.
+ if distance > ene.size + self.size: # If the length is greater than both radius combine. No collision
+ continue
+ else:
+ self.lastHitByPlayer = False
+ self.health -= ene.damage # Cause damage (Health points take 5.0 damage points)
+ self.appPtr.sounds[random.choice(("hit1", "hit2", "hit3"))].play()
+ self.walkDmgTimer = 0.0 # Resets self.walkDmgTimer back to 0.0
+ if self.health < 0: # Health cannot be negative.
+ self.health = 0
+ return None # Once you within contact of an enemy stop checking.
+
+ def walkLootCheck(self, checkList):
+ """ Check to see if the player has walked over any loot. If so, check to see what type of loot,
+ and call the correct method from there. """
+ if self.state == 0:
+ for loot in checkList:
+ if isinstance(loot, Loot):
+
+ distance = (loot.pos - self.pos).length()
+ if distance > loot.size + self.size:
+ continue
+ else:
+ print("Here")
+ loot.state = 2
+ # Type check to see if it is a weapon or a potion
+ if loot.action == "heal":
+ self.appPtr.sounds["health"].play()
+ self.health += loot.value
+ elif loot.action == "changeClass":
+ self.appPtr.sounds["pickup"].play()
+ self.pickUpWeapon(loot)
+
+ def attack(self, checkList):
+ """ checkList is the list of objects to test for a hit.
+ Check to see if the player hits anything.
+ If there is a hit, handle it. """
+ if self.state == 0:
+ if self.is_p_atk or self.atkCooldown < 1.0:
+ return None # If the player is already attacking or on cooldown, nothing will happen.
+
+ self.is_attacking = True
+ self.atkCooldown = 0.0 # Needs to recharge back up to 1 before it can be used again
+ self.hand_frame = 0
+
+ if self.weapon[0] == "Sword" or self.weapon[0] == "StrongSword":
+ self.swordAttack(checkList)
+
+ elif self.weapon[0] == "Scythe":
+ self.scytheAttack(checkList)
+
+ elif self.weapon[0] == "Bow":
+ self.bowAttack(checkList)
+
+ elif self.weapon[0] == "MagicStaff":
+ self.staffAttack(checkList)
+
+ def playHitSound(self, obj):
+ if isinstance(obj, Player):
+ self.appPtr.sounds[random.choice(("hit1", "hit2", "hit3"))].play()
+ obj.lastHitByPlayer = True
+ elif obj.name == "slime" or obj.name == "slime00cas" or obj.name == "slime00des":
+ self.appPtr.sounds["slime hit"].play()
+ elif obj.name == "Skeleton Archer":
+ self.appPtr.sounds["skeleton hit"].play()
+ elif obj.name == "ghost":
+ self.appPtr.sounds["ghost hit"].play()
+ else: #bomb
+ self.appPtr.sounds[random.choice(("bomb hit1", "bomb hit2", "bomb hit3"))].play()
+
+ def scytheAttack(self, checkList):
+ """ The hit detection if using a SCYTHE. """
+ self.appPtr.sounds["Scythe"].play()
+ for obj in checkList:
+######### HIT DETECTION FOR hitPtFar! ############
+ if obj == self: # Don't check hits against yourself
+ continue
+ if isinstance(obj, Player) or isinstance(obj, Enemy):
+ # Calculates the distance (scalar) between the hit point and the object
+ distance = math2d.Vector2(obj.pos[0] - self.hitPtFar[0], obj.pos[1] - self.hitPtFar[1] - 10)
+ distance = distance.length()
+
+ if distance < obj.size + self.hitRadius: # If the object is hit, handle the hit (below)
+ if isinstance(obj, Player):
+ obj.lastHitByPlayer = True
+ self.playHitSound(obj)
+ obj.health -= self.weapon[4] * self.damageScale // 2
+
+ for obj in checkList:
+######### HIT DETECTION FOR hitPtNear! ###########
+ if obj == self: # Don't check hits against yourself
+ continue
+ if isinstance(obj, Player) or isinstance(obj, Enemy):
+ # Calculates the distance (scalar) between the hit point and the object
+ distance = math2d.Vector2(obj.pos[0] - self.hitPtNear[0], obj.pos[1] - self.hitPtNear[1] - 10)
+ distance = distance.length()
+
+ if distance < obj.size + self.hitRadius: # If the object is hit, handle the hit (below)
+ if isinstance(obj, Player):
+ obj.lastHitByPlayer = True
+ self.playHitSound(obj)
+ obj.health -= self.weapon[4] * self.damageScale // 2
+
+ def swordAttack(self, checkList):
+ """ The hit detection if using a SWORD. """
+ self.appPtr.sounds["sword"].play()
+ for obj in checkList:
+######### HIT DETECTION FOR hitPtFar! ############
+ if obj == self: # Don't check hits against yourself
+ continue
+ if isinstance(obj, Player) or isinstance(obj, Enemy):
+ # Calculates the distance (scalar) between the hit point and the object
+ distance = math2d.Vector2(obj.pos[0] - self.hitPtFar[0], obj.pos[1] - self.hitPtFar[1] - 10)
+ distance = distance.length()
+
+ if distance < obj.size + self.hitRadius: # If the object is hit, handle the hit (below)
+ if isinstance(obj, Player):
+ obj.lastHitByPlayer = True
+ self.playHitSound(obj)
+ obj.health -= self.weapon[4] * self.damageScale // 2 # Only the tip of the sword has hit; the full damage is applied if both points hit the target
+
+ for obj in checkList:
+######### HIT DETECTION FOR hitPtNear! ###########
+ if obj == self: # Don't check hits against yourself
+ continue
+ if isinstance(obj, Player) or isinstance(obj, Enemy):
+ # Calculates the distance (scalar) between the hit point and the object
+ distance = math2d.Vector2(obj.pos[0] - self.hitPtNear[0], obj.pos[1] - self.hitPtNear[1] - 10)
+ distance = distance.length()
+
+ if distance < obj.size + self.hitRadius: # If the object is hit, handle the hit (below)
+ if isinstance(obj, Player):
+ obj.lastHitByPlayer = True
+ self.playHitSound(obj)
+ obj.health -= self.weapon[4] * self.damageScale // 2 # Only the middle of the sword has hit; the full damage is applied if both points hit the target
+
+ def bowAttack(self, checkList):
+ """ Hit detection for the BOW. Creates a projectile that handles all hit detection """
+ ####### Finding the position of the new projectile #######
+ if self.drawDirection == 0:
+ projPos = math2d.Vector2(self.pos[0] - 64, self.pos[1] - 20)
+ elif self.drawDirection == 1:
+ projPos = math2d.Vector2(self.pos[0] + 64, self.pos[1] - 20)
+ elif self.drawDirection == 2:
+ projPos = math2d.Vector2(self.pos[0], self.pos[1] - 64)
+ elif self.drawDirection == 3:
+ projPos = math2d.Vector2(self.pos[0], self.pos[1] + 64)
+
+ self.appPtr.sounds["arrow"].play()
+ newProjectile = Projectile(projPos, "arrow", self.spriteDB, self.weapon, self.drawDirection, self, "Normal")
+ self.projList.append(newProjectile)
+
+ def staffAttack(self, checkList):
+ """ Hit detection for the STAFF. Creates a projectile that handles all hit detection """
+ ####### Finding the position of the new projectile #######
+ self.appPtr.sounds["staff"].play()
+ if self.drawDirection == 0:
+ projPos = math2d.Vector2(self.pos[0] - 64, self.pos[1] - 20)
+ elif self.drawDirection == 1:
+ projPos = math2d.Vector2(self.pos[0] + 64, self.pos[1] - 20)
+ elif self.drawDirection == 2:
+ projPos = math2d.Vector2(self.pos[0], self.pos[1] - 64)
+ elif self.drawDirection == 3:
+ projPos = math2d.Vector2(self.pos[0], self.pos[1] + 64)
+
+ newProjectile = Projectile(projPos, "Blue projectile", self.spriteDB, self.weapon, self.drawDirection, self, "Normal")
+ self.projList.append(newProjectile)
+
+ def swordP_Attack(self, checkList):
+ for obj in checkList:
+ if obj == self: # Don't check hits against yourself
+ continue
+ if isinstance(obj, Player) or isinstance(obj, Enemy):
+
+ if self.drawDirection == 0: # Facing West
+ if obj.pos[0] > self.pos[0]: # If the object is to your right, skip it
+ continue
+ elif self.drawDirection == 1: # Facing East
+ if obj.pos[0] < self.pos[0]: # If the object is to your left, skip it
+ continue
+ elif self.drawDirection == 2: # Facing North
+ if obj.pos[1] > self.pos[1]: # If the object is below you, skip it
+ continue
+ elif self.drawDirection == 3: # Facing South
+ if obj.pos[1] < self.pos[1]: # If the object is above you, skip it
+ continue
+
+ # Calculates the distance (scalar) between the hit point and the object
+ distance = math2d.Vector2(obj.pos[0] - self.pos[0], obj.pos[1] - self.pos[1])
+ distance = distance.length()
+
+ # 96 is the distance the Scythe reaches (192/2)
+ if distance < 96: # If the object is hit, handle the hit (below)
+ if isinstance(obj, Player):
+ obj.lastHitByPlayer = True
+ self.playHitSound(obj)
+ obj.health -= self.weapon[5] * self.damageScale
+
+ def scytheP_Attack(self, checkList):
+ for obj in checkList:
+ if obj == self: # Don't check hits against yourself
+ continue
+ if isinstance(obj, Player) or isinstance(obj, Enemy):
+ # Calculates the distance (scalar) between the hit point and the object
+ distance = math2d.Vector2(obj.pos[0] - self.pos[0], obj.pos[1] - self.pos[1])
+ distance = distance.length()
+
+ # 96 is the distance the Scythe reaches (192/2)
+ if distance < 96: # If the object is hit, handle the hit (below)
+ if isinstance(obj, Player):
+ obj.lastHitByPlayer = True
+ self.playHitSound(obj)
+ obj.health -= self.weapon[5] * self.damageScale
+
+ def bowP_Attack(self, checkList):
+ if self.drawDirection == 0:
+ projPos = math2d.Vector2(self.pos[0] - 64, self.pos[1] - 20)
+ elif self.drawDirection == 1:
+ projPos = math2d.Vector2(self.pos[0] + 64, self.pos[1] - 20)
+ elif self.drawDirection == 2:
+ projPos = math2d.Vector2(self.pos[0], self.pos[1] - 64)
+ elif self.drawDirection == 3:
+ projPos = math2d.Vector2(self.pos[0], self.pos[1] + 64)
+
+ newProjectile = Projectile(projPos, "flaming_arrow", self.spriteDB, self.weapon, self.drawDirection, self, "Power")
+ self.projList.append(newProjectile)
+
+ def staffP_Attack(self, checkList):
+ if self.drawDirection == 0:
+ projPos = math2d.Vector2(self.pos[0] - 64, self.pos[1] - 20)
+ elif self.drawDirection == 1:
+ projPos = math2d.Vector2(self.pos[0] + 64, self.pos[1] - 20)
+ elif self.drawDirection == 2:
+ projPos = math2d.Vector2(self.pos[0], self.pos[1] - 64)
+ elif self.drawDirection == 3:
+ projPos = math2d.Vector2(self.pos[0], self.pos[1] + 64)
+
+ newProjectile = Projectile(projPos, "purple projectile", self.spriteDB, self.weapon, self.drawDirection, self, "Power")
+ self.projList.append(newProjectile)
+
+ def p_attack(self, checkList = None):
+ """ checkList is the list of objects to test for a hit.
+ Check to see if the player hits anything.
+ If there is a hit, handle it. """
+ if self.state == 0:
+ if self.is_attacking or self.paCharge < 1.0:
+ return None # If the player is already attacking or on cooldown, nothing will happen.
+
+ self.paCharge = 0.0 # Needs to recharge back up to 1 before it can be used again
+ self.pAttackColor = (0,0,255)
+ self.is_p_atk = True
+ self.hand_frame = 0
+
+ if self.weapon[0] == "Sword" or self.weapon[0] == "StrongSword":
+ self.appPtr.sounds["sword"].play()
+ self.swordP_Attack(checkList)
+ elif self.weapon[0] == "Scythe":
+ self.appPtr.sounds["Scythe"].play()
+ self.scytheP_Attack(checkList)
+
+ elif self.weapon[0] == "Bow":
+ self.appPtr.sounds["arrow"].play()
+ self.bowP_Attack(checkList)
+
+ elif self.weapon[0] == "MagicStaff":
+ self.appPtr.sounds["staff"].play()
+ self.staffP_Attack(checkList)
+
+ def pickUpWeapon(self, newWeapon):
+ self.weapon = newWeapon.value[1]
+ self.weaponSprite = self.spriteDB.get(self.weapon[0])
+ if newWeapon.value[0] == "Melee":
+ self.name = "Knight"
+ self.sprite = self.spriteDB.get("Knight")
+ elif newWeapon.value[0] == "Ranged":
+ self.name = "Archer"
+ self.sprite = self.spriteDB.get("Archer")
+ elif newWeapon.value[0] == "Magic":
+ self.name = "Mage"
+ self.sprite = self.spriteDB.get("Mage")
+
+ def update(self, dT, checkList, w):
+ Mover.update(self, dT, checkList, w)
+
+ if self.is_attacking: # Attacking
+ self.hand_action = 0
+
+ elif self.is_p_atk: # P-attacking
+ self.hand_action = 1
+
+ elif self.curAction == 0: # Walking
+ self.hand_action = 0
+
+ elif self.curAction == 3: # Idle
+ self.hand_action = 1
+
+ self.hand_frameTime += dT # Add to the hand frame timer
+
+ if self.paCharge < 1.0:
+ self.paCharge += dT / self.weapon[3] # Divide dT by the cooldown to increase paCharge by a percentage
+ else:
+ self.pAttackColor = (255,0,255) # Not even sure what this is, but I think it's necessary for GUI things
+
+ if self.atkCooldown < 1.0:
+ self.atkCooldown += dT / self.weapon[2] # Divide dT by the cooldown of the attack to increase by percentage
+
+ if self.walkDmgTimer < 1.0:
+ self.walkDmgTimer += dT # Increment the damage timer for walking over enemies
+
+ if self.hand_frameTime >= self.animDelay:
+ self.hand_frame += 1 # Change to the next hand frame
+ self.hand_frameTime = 0 # Reset the timer to 0
+ if self.hand_frame < 4:
+ if self.weapon[0] == "Sword" or self.weapon[0] == "StrongSword":
+ self.hitPtFar = math2d.Vector2(self.weapon[8][self.drawDirection][self.hand_frame][0][0] + self.pos[0],
+ self.weapon[8][self.drawDirection][self.hand_frame][0][1] + self.pos[1])
+ self.hitPtNear = math2d.Vector2(self.weapon[8][self.drawDirection][self.hand_frame][1][0] + self.pos[0],
+ self.weapon[8][self.drawDirection][self.hand_frame][1][1] + self.pos[1])
+ if self.weapon[0] == "Scythe":
+ self.hitPtNear = math2d.Vector2(self.weapon[8][self.drawDirection][self.hand_frame][0][0] + self.pos[0],
+ self.weapon[8][self.drawDirection][self.hand_frame][0][1] + self.pos[1])
+ self.hitPtFar = math2d.Vector2(self.weapon[8][self.drawDirection][self.hand_frame][1][0] + self.pos[0],
+ self.weapon[8][self.drawDirection][self.hand_frame][1][1] + self.pos[1])
+ if self.hand_frame >= 4:
+ self.hand_frame = 0 # Reset back to the first frame
+ self.hand_frameTime = 0
+ self.is_attacking = False
+ self.is_p_atk = False
+
+ trapCheck = (w.isSpotTrap((self.pos[0]), (self.pos[1])-5))
+ if trapCheck != -1 and self.state == 0:
+ #Pit
+ if trapCheck == 0:
+ self.lastHitByPlayer = False
+ self.fall()
+ self.appPtr.sounds["fall"].play()
+ #Spikes
+ elif trapCheck == 1:
+ if self.walkDmgTimer >= 1:
+ if self.lavaBool == True:
+ self.appPtr.sounds["lava"].stop()
+ self.lavaBool = False
+ self.appPtr.sounds["spike"].play()
+ self.lastHitByPlayer = False
+ self.health -= 10
+ self.appPtr.sounds[random.choice(("hit1", "hit2", "hit3"))].play()
+ self.walkDmgTimer = 0
+ self.speed = 75
+ #Lava
+ if trapCheck == 2:
+ if self.lavaBool == False:
+ self.appPtr.sounds["lava"].play()
+ self.lavaBool = True
+ if self.walkDmgTimer >= 1:
+ self.lastHitByPlayer = False
+ self.health -= 20
+ self.appPtr.sounds[random.choice(("hit1", "hit2", "hit3"))].play()
+ self.walkDmgTimer = 0
+ self.speed = 75
+ else:
+ self.speed = 200
+ self.appPtr.sounds["lava"].stop()
+ self.lavaBool = False
+ for obj in checkList:
+ if isinstance(obj, Enemy):
+ if obj.AI == "chase":
+ obj.visibles = checkList
+
+ def respawn(self,world):
+ spawnPoints = copy.deepcopy(BigDict.BigDict["WorldObjects"]["Spawnpoints"])
+ self.state = 0 # Revert the state of the player to 0 (normal)
+ self.health = 100 # Return the player to full health
+ spawnIndex = random.randint(0, len(spawnPoints) - 1)
+ self.pos = spawnPoints[spawnIndex] # Put the player's position at the chosen spawnPoint (randomly picked from possible spawnPoints)
+ self.pos = math2d.Vector2(self.pos[0], self.pos[1]) # To make sure position is a vector, not a tuple
+ if not world.isSpotWalkable(self.pos[0], self.pos[1], "normal") or world.isSpotTrap(self.pos[0], self.pos[1]-5)!=-1:
+ print("Trying respawn again, you got stuck in a wall or trap!")
+ self.respawn(world)
+ self.weapon = BigDict.BigDict["Pickups"]["Weapons"]["Melee"][0] # Sets self.weapon to the Sword list
+ self.weaponSprite = self.spriteDB.get(self.weapon[0])
+ self.sprite = self.spriteDB.get("Human")
+ self.name = "Human"
+
+ def render(self, surf, cameraPos):
+ if self.drawDirection == 2: # If the player is facing up...
+ self.render_hand(surf, cameraPos) # render the hands first
+ Mover.render(self, surf, cameraPos) # ...and the player is on top of the hands
+
+ else: # If the player is facing right/down/left...
+ Mover.render(self, surf, cameraPos) # render the player first
+ self.render_hand(surf, cameraPos) # ...and the hands are on top of the player
+
+ def render_hand(self, surf, cameraPos):
+ """ Renders the hands """
+ if self.is_attacking or self.is_p_atk: # If the player is attacking or power attacking, render the weapon
+
+ surf.blit(self.weaponSprite, (self.pos[0] - 96 - cameraPos[0], self.pos[1] - 96 - cameraPos[1]), (self.hand_frame * 192,
+ self.hand_action * 768 + self.drawDirection * 192, 192, 192))
+## try: # A strange error was occuring; this is to handle that error
+## # Draw the red dot at the tip of the sword, using the point list
+## if self.weapon[0] == "Sword" or self.weapon[0] == "StrongSword" or self.weapon[0] == "Scythe":
+## pygame.draw.circle(surf, (255,0,0), (int(self.hitPtFar[0] - cameraPos[0]), int(self.hitPtFar[1] - cameraPos[1])), self.hitRadius)
+## pygame.draw.circle(surf, (255,0,0), (int(self.hitPtNear[0] - cameraPos[0]), int(self.hitPtNear[1] - cameraPos[1])), self.hitRadius)
+##
+## except: # To handle the strange error case where hitPtFar was not defined before this was called
+## pass
+
+ else:
+ surf.blit(self.handSprite, (self.pos[0] - 96 - cameraPos[0], self.pos[1] - 96 - cameraPos[1]), (self.hand_frame * 192 + 3,
+ self.hand_action * 768 + self.drawDirection * 192 + 3, 192, 192))
+
+class Enemy(Mover):
+ """ A NPC entity. Unable to access Loot. Able to trigger [most?] traps. """
+ def __init__(self, pos, sprite, spriteDBase):
+ Mover.__init__(self, pos, sprite, spriteDBase)
+ self.spriteName = sprite
+ self.duration = random.randint(1,5) # Randomly determines how long the enemy will maintain the same action
+ self.direction = random.randint(0,4) # Picks a random direction.
+ self.visibles = []
+
+ def randMove(self, dT, w):
+ """ Randomly moves the enemy in a direction for a duration, using self.duration and self.direction """
+ self.duration -= dT
+
+ if self.direction == 0: # If moving west: go left.
+ if (w.isSpotWalkable((self.pos[0]) - 16 + self.tempSpeed * dT, (self.pos[1]), "enemy") or (self.spriteName == "ghost" and self.pos[0] > 0)):
+ self.pos[0] -= self.tempSpeed * dT
+ self.changeDrawDirection(0)
+ elif self.direction == 1: # If moving east: go right.
+ if (w.isSpotWalkable((self.pos[0]) + 32 + self.tempSpeed * dT, (self.pos[1]), "enemy") or (self.spriteName == "ghost" and self.pos[0] < w.worldWidthT)):
+ self.pos[0] += self.tempSpeed * dT
+ self.changeDrawDirection(1)
+ elif self.direction == 2: # If moving north: go up.
+ if (w.isSpotWalkable((self.pos[0]), (self.pos[1]) - 16 + self.tempSpeed * dT, "enemy") or (self.spriteName == "ghost" and self.pos[1] > 0)):
+ self.pos[1] -= self.tempSpeed * dT
+ self.changeDrawDirection(2)
+ elif self.direction == 3: # If moving south: go down.
+ if (w.isSpotWalkable((self.pos[0]), (self.pos[1]) + 32 + self.tempSpeed * dT, "enemy") or (self.spriteName == "ghost" and self.pos[1] < w.worldHeightT)):
+ self.pos[1] += self.tempSpeed * dT
+ self.changeDrawDirection(3)
+
+ if self.duration <= 0: # Changes direction randomly, then randomly chooses new direction and rate.
+ self.changeDrawDirection(self.direction)
+ self.duration = random.randint(1,5)
+ self.direction = random.randint(0,4)
+
+ if self.AI == "range": # For ranged enemies
+ self.rangeFire(dT, w)
+
+ def chaserMove(self, dT, w):
+ """ AI pattern for enemies that chase you """
+ chaseDist = 800
+ for obj in self.visibles:
+ if isinstance(obj, Player):
+ # Calculating the distance between the Player and the Enemy
+ dist = math2d.Vector2(obj.pos[0] - self.pos[0], obj.pos[1] - self.pos[1])
+ distLength = dist.length() # Finding the length of that distance
+ # Start chasing the player if the Player is within the chase distance
+ if distLength <= chaseDist:
+ direct = dist.normalized() # Normalizing the distance so that it is a direction only
+
+ if abs(direct[0]) > abs(direct[1]) and direct[0] < 0: # Moving more in the x direction; also moving left
+ self.changeDrawDirection(0)
+ elif abs(direct[0]) > abs(direct[1]) and direct[0] > 0: # Moving more in the x direction; also moving right
+ self.changeDrawDirection(1)
+ elif abs(direct[0]) < abs(direct[1]) and direct[0] < 0: # Moving more in the y direction; also moving up
+ self.changeDrawDirection(2)
+ elif abs(direct[0]) < abs(direct[1]) and direct[0] > 0: # Moving more in the y direction; also moving down
+ self.changeDrawDirection(3)
+
+ # Making sure it doesn't walk through walls
+ #ignoring because GHOSTS ARE HOMING if (w.isSpotWalkable((self.pos[0]) + direct[0] * (self.size + self.tempSpeed * dT), (self.pos[1]))):
+ self.pos[0] += direct[0] * self.tempSpeed * dT
+ #if (w.isSpotWalkable((self.pos[0]), (self.pos[1]) + direct[1] * (self.size + self.tempSpeed * dT))):
+ self.pos[1] += direct[1] * self.tempSpeed * dT
+
+ break # After chasing one object, don't chase another
+ # I realize this will cause strange chase patterns if there are multiple players near the object;
+ # it won't always chase the nearest player with this code. We will implement this if we have time.
+
+ def rangeFire(self, dT, w):
+ """AI to fire a projectile in a random direction"""
+ if self.drawDirection == 0:
+ projPos = math2d.Vector2(self.pos[0] - 64, self.pos[1] - 20)
+ elif self.drawDirection == 1:
+ projPos = math2d.Vector2(self.pos[0] + 64, self.pos[1] - 20)
+ elif self.drawDirection == 2:
+ projPos = math2d.Vector2(self.pos[0], self.pos[1] - 64)
+ elif self.drawDirection == 3:
+ projPos = math2d.Vector2(self.pos[0], self.pos[1] + 64)
+ try:
+ if projPos: # Debugging
+ newProjectile = Projectile(projPos, "arrow", self.spriteDB, self.weapon, self.drawDirection, self)
+ self.projList.append(newProjectile)
+ except:
+ pass
+
+ def update(self, dT, w, checkList):
+ # The Mover.update will be called once enemy sprite sheets are implemented
+ # If the format of enemy sprite sheets is different that those of the player,
+ # then there will be a new update for the enemy class.
+ Mover.update(self, dT, checkList, w)
+ if self.state == 0:
+ if self.AI == "random":
+ self.randMove(dT, w)
+ if self.AI == "chase":
+ self.chaserMove(dT, w)
+ if self.AI == "range":
+ self.randMove(dT, w)
+
+class Loot(Entity):
+ """ Any environmental entity that is helpful to the PLAYER.
+ Note: Enemies cannot access any loot. """
+ def __init__(self, pos, spritename, action, spriteDBase, value=None):
+ Entity.__init__(self, pos, spritename, spriteDBase)
+ self.action = action # A string of what the loot DOES (when picked up), either "changeClass" or "heal" for now
+ self.value = value # If it's a potion for example, the amount it heals by
+
+ def render(self, surf, cameraPos):
+ """ Draw the blobby shadow (from Entity.render), then draw the current
+ sprite on top of it """
+ # Draws the shadow by using the render method of the Entity base class
+ Entity.render(self, surf, cameraPos)
+ # Draws the sprite of this Loot item on top of the shadow
+ surf.blit(self.sprite, (self.pos[0] - self.sprite.get_width()/2 - cameraPos[0], self.pos[1] - self.sprite.get_height()/2 - cameraPos[1]))
+
+class Easy(Enemy):
+ def __init__(self, pos, spriteName, health, attack, speed, AI, spriteDBase):
+ Enemy.__init__(self, pos, spriteName, spriteDBase)
+ self.speed = speed # Each enemy that has a different speed than 100 px/s is specified based upon the enemy
+ self.tempSpeed = self.speed
+ self.health = health
+ self.maxHealth = health # The maximum health - THIS VALUE SHOULD NOT CHANGE ONCE THE ENEMY IS CREATED.
+ self.damage = attack # Is this attack DAMAGE..? - JBrant: I am implementing it as the damage enemies cause when walked on.
+ self.AI = AI
+ if self.AI == "range":
+ self.weapon = BigDict.BigDict["Pickups"]["Weapons"]["Ranged"][0]
\ No newline at end of file
diff --git a/game font/HDirtyWhore-Regular.ttf b/game font/HDirtyWhore-Regular.ttf
new file mode 100644
index 0000000..8ac36c8
Binary files /dev/null and b/game font/HDirtyWhore-Regular.ttf differ
diff --git a/game font/deathrattlebb_reg.ttf b/game font/deathrattlebb_reg.ttf
new file mode 100644
index 0000000..f4c6198
Binary files /dev/null and b/game font/deathrattlebb_reg.ttf differ
diff --git a/gui_manager.py b/gui_manager.py
new file mode 100644
index 0000000..a9714ab
--- /dev/null
+++ b/gui_manager.py
@@ -0,0 +1,851 @@
+import pygame
+import math2d
+import math
+import pane
+import soundDataBase
+import BigDict # 16, 716
+
+class GUI_Manager(object):
+ def __init__(self, app):
+ self.appPtr = app
+ self.mode = "title" # 'menu', 'credits', 'options',
+ self.selectionV = 1 # 'create', 'game','input', 'title'
+ self.selectionH = 2
+ self.width=4
+ self.height=5
+ self.playernum=1
+ self.music=10
+ self.soundfx=1
+ self.played = False
+ self.compassAngle = 50.0
+ self.pAttackColor = (0,0,255)
+ self.amongTheLiving = []
+ self.mapping = []
+ # Font objects/ images.
+ button_dic={"creditsButB":pygame.image.load("imgs\\gui\\Main Menu\\Credits_Black.png"),"creditsButR":pygame.image.load("imgs\\gui\\Main Menu\\Credits_Red.png"),\
+ "optionsButB":pygame.image.load("imgs\\gui\\Main Menu\\Options_Black.png"),"optionsButR":pygame.image.load("imgs\\gui\\Main Menu\\Options_Red.png"),\
+ "ngamebutB":pygame.image.load("imgs\\gui\\Main Menu\\NewGame_Black.png"),"ngamebutR":pygame.image.load("imgs\\gui\\Main Menu\\NewGame_Red.png"),\
+ "LArrowB":pygame.image.load("imgs\\gui\\Main Menu\\leftArrow_Black.png"),"LArrowR":pygame.image.load("imgs\\gui\\Main Menu\\leftArrow_Red.png"),\
+ "RArrowR":pygame.image.load("imgs\\gui\\Main Menu\\rightArrow_Red.png"),"RArrowB":pygame.image.load("imgs\\gui\\Main Menu\\rightArrow_Black.png"),\
+ "startButB":pygame.image.load("imgs\\gui\\Main Menu\\Start_Black.png"),"startButR": pygame.image.load("imgs\\gui\\Main Menu\\Start_Red.png"),\
+ "backButB":pygame.image.load("imgs\\gui\\Main Menu\\Back_Black.png"),"backButR":pygame.image.load("imgs\\gui\\Main Menu\\Back_Red.png")}
+ menu_dic={"main":pygame.image.load("imgs\\gui\\Main Menu\\MainMenu.png"),"create":pygame.image.load("imgs\\gui\\Main Menu\\newgame.png"),"options":pygame.image.load("imgs\\gui\\Main Menu\\options.png"),"title":pygame.image.load("imgs\\gui\\titlescreen.png")}
+ screen_dic={"player 2":pygame.image.load("imgs\\gui\\twoPlayer_Split.png"),"player 4":pygame.image.load("imgs\\gui\\fourPlayer_Split.png")}
+ compass_dic={"compassNeedle":pygame.image.load("imgs\\pickup items\\CompassNeedlePU.png"),"compassBackground":pygame.image.load("imgs\\pickup items\\CompassPU.png")}
+ icon_dic={"Sword":pygame.image.load("imgs\\pickup items\\ClaymorePU.png"),"StrongSword":pygame.image.load("imgs\\pickup items\\ClaymorePU.png"),"MagicStaff":pygame.image.load("imgs\\pickup items\\staffPU.png"),"Bow":pygame.image.load("imgs\\pickup items\\BowPU.png"),"Scythe":pygame.image.load("imgs\\pickup items\\ScythePU.png")}
+ #main dictorany
+ self.gui_dic={"button":button_dic,"menu":menu_dic,"screen":screen_dic,"compass":compass_dic, "icon":icon_dic}
+
+ #Health Bar dictionary
+ self.playericon2_dic={"Human":pygame.image.load("imgs\\healthbar\\Unarmed.png"),"Knight":pygame.image.load("imgs\\healthbar\\knight.png"),"Mage":pygame.image.load("imgs\\healthbar\\mage.png"),"Archer":pygame.image.load("imgs\\healthbar\\archer.png")}
+ self.playericon4_dic={"Human":pygame.image.load("imgs\\healthbar\\Unarmed4.png"),"Knight":pygame.image.load("imgs\\healthbar\\knight4.png"),"Mage":pygame.image.load("imgs\\healthbar\\mage4.png"),"Archer":pygame.image.load("imgs\\healthbar\\archer4.png")}
+
+ self.testFont = pygame.font.SysFont("Times New Roman", 12)
+ self.MainMenu = pygame.image.load("imgs\\gui\\Main Menu\\MainMenu.png")
+ self.CreateMenu = pygame.image.load("imgs\\gui\\Main Menu\\newgame.png")
+ self.OptionsMenu = pygame.image.load("imgs\\gui\\Main Menu\\options.png")
+ #self.noCoOpFont = pygame.font.Font("imgs\\gui\\HDirtyWhore-Regular", 12)
+ self.title_img = pygame.image.load("imgs\\gui\\titlescreen.png")
+
+ #---Input images---#
+ self.iconKeyboard = pygame.image.load("imgs\\gui\\Mouse and Keyboard.png")
+ self.iconGamepad = pygame.image.load("imgs\\gui\\Xbox_Controller.png")
+ #------------------#
+ self.Font50 = pygame.font.Font("game font\\HDirtyWhore-Regular.ttf", 50)
+
+
+ # Number of Windows
+ #self.numWindows = None
+ #Adjust volume
+ #self.appPtr.soundeffects.sounds["click1"].set_volume(1 * self.soundfx)
+ #self.appPtr.soundeffects.sounds["click2"].set_volume(0.3 * self.soundfx)
+ self.played = False
+
+ def Update(self, dT):
+ """ Updates the value for self.initPowerCharge
+ and the mouse position on the GUI. """
+ self.gMouse = pygame.mouse.get_pos()
+ self.dT = dT
+ return self.music
+
+ def CompassDirection(self, curPane, paneList): #Handles the direction of the compass to point to the nearest opponent.
+ """ Takes the positions of every player on the screen
+ (needs to handle the case where there is only one player),
+ creates vector objects between the current player and every other
+ player, finds the distance between every player and the current player
+ (call the length function of math2D), and then calls math.atan2(vectorY,
+ vectorX) to find the angle that we need to rotate the compass needle."""
+ frenemyList = [] # List of other players that you may/may not want to kill (their positions).
+ distanceList = [] # List of distances between yourself and every other player.
+ curPlayerPos = curPane.player.pos # Where you are.
+ if len(paneList) != 1: # If there's more than one player on screen...
+ for p in paneList: # For every pane...
+ if p != curPane and p.player.state != 2: # If it's not the pane that you are on...
+ frenemyList.append(p.player.pos) # Add the position of the player in that pane to frenemyList.
+ for frenemy in frenemyList: # For every player position in frenemyList...
+ curVector = frenemy - curPlayerPos # Create a vector by subtracting your position from the "frenemy"'s position.
+ curDistance = curVector.length() # Find the length of that vector.
+ distanceList.append(curDistance) # Append that length to distanceList.
+ i = frenemyList[distanceList.index(min(distanceList))] # Working backwards, we find the index number of the minimum value of distanceList, and we make i equal to the item in frenemyList at that same index.
+ closestVector = i - curPlayerPos # The closest vector is that item in frenemyList's value minus your positon.
+ self.compassAngle = -(math.degrees(math.atan2(closestVector[1],closestVector[0])) % 360)
+ else:
+ self.compassAngle = 90
+
+ def render(self, surf, pane = None, paneList = None):
+ """ surf will be the entire screen. Draw the
+ GUI for the current game mode. Include a check
+ to see if the player is currently dead. If so,
+ display a game over screen. """
+ self.mx, self.my = pygame.mouse.get_pos()
+ #the font size
+ Font20 = pygame.font.SysFont("Times New Roman", 20)
+ #Font21 = pygame.font.SysFont("HDirtyWhore-Regular.ttf", 20)
+ Font32 = pygame.font.SysFont("Times New Roman", 38)
+ #surf
+ tempS = Font20.render("Menu", False, (250,0,0))
+ #color
+ fontcolor=(255,255,255)
+ self.fontcolor2=fontcolor
+ buttoncolorhit=(255,0,255)
+ buttoncolor=(120,120,120)
+
+ #title page
+ if(self.mode == "title"):
+ surf.fill((128,128,128))
+ surf.blit(self.gui_dic["menu"]["title"], (0,0))
+
+ #menu page
+ elif(self.mode == "menu"):
+ #menu surf
+ MenuSurf = self.gui_dic["menu"]["main"]
+ tempS1 = Font20.render("create", False, fontcolor)
+ tempS2 = Font20.render("option", False, fontcolor)
+ tempS3 = Font20.render("credit", False, fontcolor)
+ bcolor = (128,0,0)
+ surf.fill(bcolor)
+
+ Y=395
+ X=435
+ #menu
+ surf.blit(MenuSurf, (0,0))
+
+ #create button
+ pygame.draw.rect(surf, (bcolor), ((X),Y+55-tempS1.get_height()/2,140, 40), 0)
+ surf.blit(self.gui_dic["button"]["ngamebutB"], (512-self.gui_dic["button"]["ngamebutB"].get_width()/2,Y+60-self.gui_dic["button"]["ngamebutB"].get_height()/2))
+ #option button
+ pygame.draw.rect(surf, (bcolor), ((X),Y+120-tempS2.get_height()/2,140, 40), 0)
+ surf.blit(self.gui_dic["button"]["optionsButB"], (512-self.gui_dic["button"]["optionsButB"].get_width()/2,Y+130-self.gui_dic["button"]["optionsButB"].get_height()/2))
+ #credit button
+ pygame.draw.rect(surf, (bcolor), ((X),Y+200-tempS3.get_height()/2,140, 40), 0)
+ surf.blit(self.gui_dic["button"]["creditsButB"], (512-self.gui_dic["button"]["creditsButB"].get_width()/2,Y+210-self.gui_dic["button"]["creditsButB"].get_height()/2))
+
+ #collision hit
+ r = pygame.Rect(((X),Y+40-tempS1.get_height()/2,155, 60))
+ r1 = pygame.Rect(((X),Y+110-tempS2.get_height()/2,155, 60))
+ r2 = pygame.Rect(((X),Y+200-self.gui_dic["button"]["creditsButB"].get_height()/2,155, 80))
+
+ #draw rectangles to screen (debug, will blit objects later)
+ #move with gamepad and keyboard
+ if(r.collidepoint(self.mx,self.my) or self.selectionV == 1 ):
+ self.selectionV = 1
+ pygame.draw.rect(surf, (bcolor), ((X),Y+40-tempS1.get_height()/2,155, 60), 0)
+ surf.blit(self.gui_dic["button"]["ngamebutR"], (512-self.gui_dic["button"]["ngamebutR"].get_width()/2,Y+60-self.gui_dic["button"]["ngamebutR"].get_height()/2))
+ if(r1.collidepoint(self.mx,self.my) or self.selectionV == 2):
+ self.selectionV = 2
+ pygame.draw.rect(surf, (bcolor), ((X),Y+110-tempS2.get_height()/2,155, 60), 0)
+ surf.blit(self.gui_dic["button"]["optionsButR"], (512-self.gui_dic["button"]["optionsButR"].get_width()/2,Y+130-self.gui_dic["button"]["optionsButR"].get_height()/2))
+ if(r2.collidepoint(self.mx,self.my) or self.selectionV == 3):
+ self.selectionV = 3
+ pygame.draw.rect(surf, (bcolor), ((X),Y+200-self.gui_dic["button"]["creditsButR"].get_height()/2,155, 80), 0)
+ surf.blit(self.gui_dic["button"]["creditsButR"], (512-self.gui_dic["button"]["creditsButR"].get_width()/2,Y+210-self.gui_dic["button"]["creditsButR"].get_height()/2))
+ #sound click
+ if(r.collidepoint(self.mx,self.my) or (self.selectionV == 1)):
+ if not self.played:
+ #print("hi")
+ self.appPtr.soundeffects.sounds["click1"].play()
+ self.played = True
+ elif(r1.collidepoint(self.mx,self.my) or (self.selectionV == 2)):
+ if not self.played:
+ #print("hi2")
+ self.appPtr.soundeffects.sounds["click1"].play()
+ self.played = True
+ elif(r2.collidepoint(self.mx,self.my) or (self.selectionV == 3)):
+ if not self.played:
+ #print("hi3")
+ self.appPtr.soundeffects.sounds["click1"].play()
+ self.played = True
+ else:
+ #print("hi4")
+ self.played = False
+
+ #credits
+ elif(self.mode == "credits"):
+ #------------------------------------------------------------------------------#
+ surf.fill((0,0,0))
+ # Initial variables
+ win_width=1024
+ win_height=768
+ List = ["NO COOPERATION","SPRING 2013","Thomas Edwards", "Richard Janita", "Parker Kahle",\
+ "Tom Paxton", "Dan Perkins", "Idris Said", "Cody Wheeler",\
+ "Maurice McPherson", "John Bickel", "Rhashaun Hurt",\
+ "Jimmy Albracht", "Ian Whitt", "Dakota Terry","Will Harmon",\
+ "Caleb Brown", "Matt Benson","Kris Grimsley", "Don Loughry", \
+ "Matt Robinson", "Cory Davis", "Joseph Brant"]
+ R = 255
+ G = 255
+ B = 255
+ Black = 0
+ Speed = 40 * self.dT
+ X = win_width // 2
+ Y = win_height // 2
+ NumSize = 30
+ TimesFont = pygame.font.SysFont("Times New Roman", NumSize)
+ self.StartY -= Speed
+ # Earse
+ surf.fill((0,0,0))
+ # Draw
+ self.DrawY = self.StartY
+ for i in range(len(List)):
+ if self.DrawY > 576 and self.DrawY < 768:
+ R = (768 - self.DrawY) * 1.3
+ G = 0
+ B = 0
+ color = (R,G,B)
+ elif self.DrawY > 384 and self.DrawY < 576:
+ R = (self.DrawY - 384) * 1.3
+ G = (576 - self.DrawY) * 1.3
+ B = 0
+ color = (R,G,B)
+ elif self.DrawY > 192 and self.DrawY < 384:
+ R = 0
+ G = (self.DrawY - 192) * 1.3
+ B = (384 - self.DrawY) * 1.3
+ color = (R, G, B)
+ elif self.DrawY > 0 and self.DrawY < 192: #------------------------------#
+ R = 0 # I had to take the inverse of
+ G = 0 # the inverse because otherwise
+ B = (self.DrawY * 1.3) # there was an odd junp between
+ color = (Black, Black, B) # the light blue and dark blue
+ else: #------------------------------#
+ color = (Black,Black,Black)
+
+ tempS = TimesFont.render(List[i], 1, color)
+ half_width = tempS.get_width() / 2
+ surf.blit(tempS, (X - half_width, self.DrawY))
+ self.DrawY += NumSize
+ #------------------------------------------------------------------------------#
+ # Flip
+ pygame.display.flip()
+ if(self.DrawY<=0):
+ self.mode = "menu"
+
+
+ #options
+ elif(self.mode == "options"):
+ #option surf
+ OptionSurf = self.gui_dic["menu"]["options"]
+ tempS6 = Font20.render(str(self.soundfx), False, fontcolor)
+ tempS7 = Font20.render(str(self.music), False, fontcolor)
+ tempS8 = Font20.render("back to menu", False, fontcolor)
+ bcolor = (128,0,0)
+ surf.fill(bcolor)
+ Y=450
+ X=440
+ buttoncolor = (bcolor)
+ surf.blit(OptionSurf, (0,0))
+ #audio background button
+ # -
+ pygame.draw.rect(surf, buttoncolor, ((X+2),Y+55-tempS6.get_height()/2,33, 35), 0)
+ #surf.blit(self.gui_dic["button"]["LArrowB"], (X+2,Y+60-self.gui_dic["button"]["LArrowB"].get_height()/2))
+ #num
+ pygame.draw.rect(surf, (0,0,0), ((X+40),Y+55-tempS6.get_height()/2,46, 35), 0)
+ #surf.blit(tempS6, (503-tempS6.get_width()/2,Y+61-tempS6.get_height()/2))
+ # +
+ pygame.draw.rect(surf, buttoncolor, ((X+90),Y+55-tempS6.get_height()/2,33, 35), 0)
+ #surf.blit(self.gui_dic["button"]["RArrowB"], (X+90,Y+60-self.gui_dic["button"]["RArrowB"].get_height()/2))
+ #audio music button
+ # +
+ pygame.draw.rect(surf, buttoncolor, ((X),Y+135-tempS7.get_height()/2,33, 35), 0)
+ surf.blit(self.gui_dic["button"]["LArrowB"], (X,Y+60-self.gui_dic["button"]["LArrowB"].get_height()/2))
+ #num
+ #pygame.draw.rect(surf, (0,0,0), ((X+38),Y+135-tempS6.get_height()/2,46, 35), 0)
+ surf.blit(tempS7, (503-tempS7.get_width()/2,Y+60-tempS7.get_height()/2))
+ # -
+ pygame.draw.rect(surf, buttoncolor, ((X+88),Y+135-tempS7.get_height()/2,33, 35), 0)
+ surf.blit(self.gui_dic["button"]["RArrowB"], (X+88,Y+60-self.gui_dic["button"]["RArrowB"].get_height()/2))
+ #back button
+ pygame.draw.rect(surf, buttoncolor, ((X+30),Y+184-tempS8.get_height()/2,64, 35), 0)
+ surf.blit(self.gui_dic["button"]["backButB"], (X,Y+190-self.gui_dic["button"]["backButB"].get_height()/2))
+ pygame.draw.rect(surf,buttoncolor, ((X),Y,150,40))
+
+ #collision hit
+ r3 = pygame.Rect((X+2),Y+55-tempS6.get_height()/2,33, 35)
+ r4 = pygame.Rect((X+90),Y+55-tempS6.get_height()/2,33, 35)
+ r5 = pygame.Rect((X),Y+135-tempS7.get_height()/2,33, 35)
+ r14 = pygame.Rect((X+88),Y+135-tempS7.get_height()/2,33, 35)
+ r15 = pygame.Rect((X),Y+195-self.gui_dic["button"]["backButB"].get_height()/2,125, 50)
+
+ #right
+## if(r3.collidepoint(self.mx,self.my) or (self.selectionV == 1 and self.selectionH == 1)):
+## self.selectionV = 1
+## self.selectionH = 1
+## if not self.played:
+## self.appPtr.soundeffects.sounds["click1"].play()
+## self.played = True
+## surf.blit(self.gui_dic["button"]["LArrowR"], (X+2,Y+60-self.gui_dic["button"]["LArrowR"].get_height()/2))
+ #left
+## if(r4.collidepoint(self.mx,self.my) or (self.selectionV == 1 and self.selectionH == 2)):
+## self.selectionV = 1
+## self.selectionH = 2
+## if not self.played:
+## self.appPtr.soundeffects.sounds["click1"].play()
+## self.played = True
+## surf.blit(self.gui_dic["button"]["RArrowR"], (X+90,Y+60-self.gui_dic["button"]["RArrowR"].get_height()/2))
+ #right
+ if(r3.collidepoint(self.mx,self.my) or (self.selectionV == 2 and self.selectionH == 1)):
+ self.selectionV = 2
+ self.selectionH = 1
+ if not self.played:
+ self.appPtr.soundeffects.sounds["click1"].play()
+ self.played = True
+ surf.blit(self.gui_dic["button"]["LArrowR"], (X+2,Y+60-self.gui_dic["button"]["LArrowR"].get_height()/2))
+ #left
+ if(r4.collidepoint(self.mx,self.my) or (self.selectionV == 2 and self.selectionH == 2)):
+ self.selectionV = 2
+ self.selectionH = 2
+ if not self.played:
+ self.appPtr.soundeffects.sounds["click1"].play()
+ self.played = True
+ surf.blit(self.gui_dic["button"]["RArrowR"], (X+90,Y+60-self.gui_dic["button"]["RArrowR"].get_height()/2))
+ #back
+ if(r15.collidepoint(self.mx,self.my) or (self.selectionV == 3)):
+ self.selectionV = 3
+ if not self.played:
+ self.appPtr.soundeffects.sounds["click1"].play()
+ self.played = True
+ surf.blit(self.gui_dic["button"]["backButR"], (X,Y+190-self.gui_dic["button"]["backButR"].get_height()/2))
+
+ elif(self.mode == "create"):
+ CreateSurf = self.gui_dic["menu"]["create"]
+ tempS4 = Font20.render("-", False, fontcolor)
+ tempS5 = Font20.render(str(self.playernum), False, fontcolor)
+ tempS9 = Font20.render("+", False, fontcolor)
+ tempS12 = Font20.render(str(self.width), False, fontcolor)
+ tempS13 = Font20.render(str(self.height), False, fontcolor)
+ tempS14 = Font20.render("start game ", False, fontcolor)
+ tempS15 = Font20.render("menu", False, fontcolor)
+ Y=365
+ X=455
+ bcolor = (128,0,0)
+ surf.fill(bcolor)
+ surf.blit(CreateSurf, (0,0))
+ #player
+ #----------------------------------------------------------------------------------#
+ #left
+ surf.blit(self.gui_dic["button"]["LArrowB"], ((X-21),Y+139-tempS4.get_height()/2,33, 35))
+ #num
+ pygame.draw.rect(surf, (0,0,0), ((X+18),Y+135-tempS5.get_height()/2,43,35), 0)
+ surf.blit(tempS5, (493-tempS5.get_width()/2,Y+140-tempS5.get_height()/2))
+ #right
+ surf.blit(self.gui_dic["button"]["RArrowB"], ((X+67),Y+139-tempS5.get_height()/2,33,35))
+ #----------------------------------------------------------------------------------#
+ #size
+ #----------------------------------------------------------------------------------#
+ #left
+ pygame.draw.rect(surf, (bcolor), ((X+28),Y+215-tempS4.get_height()/2,33, 35), 0)
+ surf.blit(self.gui_dic["button"]["LArrowB"], ((X+28),Y+220-tempS4.get_height()/2,33, 35))
+ #num
+ pygame.draw.rect(surf, (0,0,0), ((X+68),Y+215-tempS4.get_height()/2,39, 35), 0)
+ surf.blit(tempS12, (540-tempS4.get_width()/2,Y+220-tempS4.get_height()/2))
+ #Right
+ pygame.draw.rect(surf, (bcolor), ((X+116),Y+215-tempS4.get_height()/2,33, 35), 0)
+ surf.blit(self.gui_dic["button"]["RArrowB"], ((X+116),Y+220-tempS4.get_height()/2,33, 35))
+ #left
+ pygame.draw.rect(surf, (bcolor), ((X+29),Y+269-tempS4.get_height()/2,33, 35), 0)
+ surf.blit(self.gui_dic["button"]["LArrowB"], ((X+29),Y+273-tempS4.get_height()/2,33, 35))
+ #num
+ pygame.draw.rect(surf, (0,0,0), ((X+69),Y+270-tempS4.get_height()/2,39, 35), 0)
+ surf.blit(tempS13, (541-tempS4.get_width()/2,Y+275-tempS4.get_height()/2))
+ #right
+ pygame.draw.rect(surf, (bcolor), ((X+117),Y+268-tempS4.get_height()/2,33, 35), 0)
+ surf.blit(self.gui_dic["button"]["RArrowB"], ((X+117),Y+273-tempS4.get_height()/2,33, 35))
+
+ #----------------------------------------------------------------------------------#
+
+ #start
+ pygame.draw.rect(surf, (bcolor), ((X - 65),Y+325-self.gui_dic["button"]["startButB"].get_height()/2,125, 65), 0)
+ surf.blit(self.gui_dic["button"]["startButB"], (450-self.gui_dic["button"]["startButB"].get_width()/2,Y+330-self.gui_dic["button"]["startButB"].get_height()/2))
+ #back
+ pygame.draw.rect(surf, (bcolor), ((X+65),Y+315-tempS5.get_height()/2,125,55), 0)
+ surf.blit(self.gui_dic["button"]["backButB"], (582-self.gui_dic["button"]["backButB"].get_width()/2,Y+330-self.gui_dic["button"]["backButB"].get_height()/2))
+ #collision hit
+ #player
+ r6 = pygame.Rect((X-21),Y+135-tempS4.get_height()/2,33, 35)
+ r7 = pygame.Rect((X+67),Y+135-tempS5.get_height()/2,33,35)
+ #size
+ r8 = pygame.Rect((X+28),Y+215-tempS4.get_height()/2,33, 35)
+ r9 = pygame.Rect((X+116),Y+215-tempS4.get_height()/2,33, 35)
+ r10 = pygame.Rect((X+117),Y+268-tempS4.get_height()/2,33, 35)
+ r11 = pygame.Rect((X+29),Y+269-tempS4.get_height()/2,33, 35)
+ #start
+ r12 = pygame.Rect((X - 65),Y+325-self.gui_dic["button"]["startButB"].get_height()/2,125, 65)
+ #end
+ r13 = pygame.Rect((X+65),Y+315-tempS5.get_height()/2,125,55)
+
+ #player number
+ if(r6.collidepoint(self.mx,self.my) or (self.selectionV == 1 and self.selectionH == 1)):
+ #print("R6")
+ self.selectionV = 1
+ self.selectionH = 1
+ if not self.played:
+ self.appPtr.soundeffects.sounds["click1"].play()
+ self.played = True
+ #right
+ surf.blit(self.gui_dic["button"]["LArrowR"], ((X-21),Y+139-tempS4.get_height()/2,33, 35))
+
+ if(r7.collidepoint(self.mx,self.my) or (self.selectionV == 1 and self.selectionH == 2)):
+ #print("R7")
+ self.selectionV = 1
+ self.selectionH = 2
+ if not self.played:
+ self.appPtr.soundeffects.sounds["click1"].play()
+ self.played = True
+ #left
+ surf.blit(self.gui_dic["button"]["RArrowR"], ((X+67),Y+139-tempS5.get_height()/2,33,35))
+
+ #width
+ if(r8.collidepoint(self.mx,self.my) or (self.selectionV == 2 and self.selectionH == 1)):
+ #print("R8")
+ self.selectionV = 2
+ self.selectionH = 1
+ if not self.played:
+ self.appPtr.soundeffects.sounds["click1"].play()
+ self.played = True
+ #left
+ surf.blit(self.gui_dic["button"]["LArrowR"], ((X+28),Y+220-tempS4.get_height()/2,33, 35))
+
+ if(r9.collidepoint(self.mx,self.my) or (self.selectionV == 2 and self.selectionH == 2)):
+ #print("R9")
+ self.selectionV = 2
+ self.selectionH = 2
+ if not self.played:
+ self.appPtr.soundeffects.sounds["click1"].play()
+ self.played = True
+ #right
+ surf.blit(self.gui_dic["button"]["RArrowR"], ((X+116),Y+220-tempS4.get_height()/2,33, 35))
+
+ #height
+ if(r11.collidepoint(self.mx,self.my) or (self.selectionV == 3 and self.selectionH == 1)):
+ #print("R10")
+ self.selectionV = 3
+ self.selectionH = 1
+ if not self.played:
+ self.appPtr.soundeffects.sounds["click1"].play()
+ self.played = True
+ #right
+ surf.blit(self.gui_dic["button"]["LArrowR"], ((X+29),Y+273-tempS4.get_height()/2,33, 35))
+
+ if(r10.collidepoint(self.mx,self.my) or (self.selectionV == 3 and self.selectionH == 2)):
+ #print("R11")
+ self.selectionV = 3
+ self.selectionH = 2
+ if not self.played:
+ self.appPtr.soundeffects.sounds["click1"].play()
+ self.played = True
+ #left
+ surf.blit(self.gui_dic["button"]["RArrowR"], ((X+117),Y+273-tempS4.get_height()/2,33, 35))
+
+ #start game and back
+ if(r12.collidepoint(self.mx,self.my) or (self.selectionV == 4 and self.selectionH == 1)):
+ #print("R12")
+ self.selectionV = 4
+ self.selectionH = 1
+ if not self.played:
+ self.appPtr.soundeffects.sounds["click1"].play()
+ self.played = True
+ #start
+ surf.blit(self.gui_dic["button"]["startButR"], (450-self.gui_dic["button"]["startButR"].get_width()/2,Y+330-self.gui_dic["button"]["startButR"].get_height()/2))
+
+ if(r13.collidepoint(self.mx,self.my) or (self.selectionV == 4 and self.selectionH == 2)):
+ #print("R13")
+ self.selectionV = 4
+ self.selectionH = 2
+ if not self.played:
+ self.appPtr.soundeffects.sounds["click1"].play()
+ self.played = True
+ #back
+ surf.blit(self.gui_dic["button"]["backButR"], (582-self.gui_dic["button"]["backButR"].get_width()/2,Y+330-self.gui_dic["button"]["backButR"].get_height()/2))
+
+
+ elif(self.mode == "input"):
+ for i in range(self.playernum):
+ if i == pane.paneNum:
+ if i < len(self.mapping):
+ surf.fill((128,0,0))
+ #if(i == len(self.mapping) - 1):
+ # print("Self.Mapping: ", len(self.mapping) - 1)
+ if(self.mapping[i][1] == 0):
+ surf.blit(self.iconKeyboard, (50, 50))
+ else:
+ #print("Gamepad Draw", len(self.mapping))
+ surf.blit(self.iconGamepad, (50, 50))
+ else:
+ surf.fill((255,255,255))
+
+ #--MESSAGE--#
+ tempS20 = self.Font50.render("Press Attack After", False, self.fontcolor2)
+ tempS21 = self.Font50.render("Everyone Enters", False, self.fontcolor2)
+ surf.blit(tempS20, (50, 150))
+ surf.blit(tempS21, (50, 210))
+ #-----------#
+
+ elif(self.mode =="game"):
+ if len(paneList) == 1:
+ # Circle Alpha Cutout!
+
+ tempSurf = pygame.Surface((64,64))
+ circleCutout = pygame.Surface((64,64))
+ circleCutout.fill((0,0,0))
+ pygame.draw.circle(circleCutout, (254,254,254), (32,32), 32) # Border (optional)
+ pygame.draw.circle(circleCutout, (255,255,255), (32,32), 31) # Must be white for the color key
+ circleCutout.set_colorkey((255,255,255))
+ tempSurf.fill((0,0,0))
+ # POWER ATTACK BAR (BLIT WEAPON ICON IN FRONT OF THIS!!!)
+ pygame.draw.rect(tempSurf, (self.pAttackColor), (0, 64, 64,64 * -(pane.player.paCharge)))
+ # Blits the Cutout!
+
+ tempSurf.blit(circleCutout, (0,0))
+ tempSurf.set_colorkey((0,0,0)) # Make border areas outside the circle transparent
+ surf.blit(tempSurf, (2, 702)) # Copy the completed overlay to the screen
+ surf.blit(self.gui_dic["icon"][pane.player.weapon[0]], (18,718))
+
+ # ATTACK AND POWERATTACK BUTTON INDICATORS
+ pygame.draw.rect(surf, (0,255,0), (surf.get_width() - (surf.get_width() - 66), surf.get_height() - 67, surf.get_width() // 30, surf.get_height() // 20))
+ pygame.draw.rect(surf, (255,0,0), (surf.get_width() - (surf.get_width() - 66), surf.get_height() - 35, surf.get_width() // 30, surf.get_height() // 20))
+ surf.blit(self.testFont.render("ATK", False, (0,0,0)), (surf.get_width() - (surf.get_width() - 72), surf.get_height() - 60))
+ surf.blit(self.testFont.render("PWR", False, (0,0,0)), (surf.get_width() - (surf.get_width() - 70), surf.get_height() - 23))
+
+ # HEALTH BAR
+ p2x = 111 + (pane.player.health*2)
+ bPL = ((111, 63), (309, 63), (283, 89), (81, 89))
+ rPL = ((111, 65), (304, 65), (282, 87), (87, 87))
+ gPL = ((111, 65), (p2x, 65), ((p2x-22), 87), (87, 87))
+ pPL = ((36, 57), (116, 57), (80, 93), (46, 93), (16, 68))
+
+ pygame.draw.polygon(surf,(0,0,0), bPL, 0)
+ pygame.draw.polygon(surf,(255,0,0),rPL , 0)
+ pygame.draw.polygon(surf,(111,111,111), pPL, 0)
+ pygame.draw.polygon(surf,(0,0,0), pPL, 3)
+ pygame.draw.polygon(surf,(0,255,0), gPL, 0)
+ surf.blit(self.playericon2_dic[pane.player.name], (0,0))
+
+ # COMPASS
+ self.CompassDirection(pane, paneList)
+ surf.blit(self.gui_dic["compass"]["compassBackground"], (surf.get_width() - 64, surf.get_height() - 64))
+ tempS = pygame.transform.rotate(self.gui_dic["compass"]["compassNeedle"], self.compassAngle)
+ surf.blit(tempS, (surf.get_width() - 32 - tempS.get_width() / 2, surf.get_height() - 32 - tempS.get_height()/2))
+
+ elif len(paneList) == 2:
+ # Check to see if the game has ended.
+ if len(self.amongTheLiving) <= 1:
+ self.mode == "GameOver"
+ # Circle Alpha Cutout!
+
+ tempSurf = pygame.Surface((64,64))
+ circleCutout = pygame.Surface((64,64))
+ circleCutout.fill((0,0,0))
+ pygame.draw.circle(circleCutout, (254,254,254), (32,32), 32) # Border (optional)
+ pygame.draw.circle(circleCutout, (255,255,255), (32,32), 31) # Must be white for the color key
+ circleCutout.set_colorkey((255,255,255))
+ tempSurf.fill((0,0,0))
+ # POWER ATTACK BAR (BLIT WEAPON ICON IN FRONT OF THIS!!!)
+ pygame.draw.rect(tempSurf, (self.pAttackColor), (0, 64, 64,64 * -(pane.player.paCharge)))
+ # Blits the Cutout!
+
+ tempSurf.blit(circleCutout, (0,0)) # REMEMBER: 18, 358 AND 2, 326
+ tempSurf.set_colorkey((0,0,0)) # Make border areas outside the circle transparent
+ surf.blit(tempSurf, (2, 702)) # Copy the completed overlay to the screen
+ surf.blit(self.gui_dic["icon"][pane.player.weapon[0]], (18,718))
+ # ATTACK AND POWERATTACK BUTTON INDICATORS
+ pygame.draw.rect(surf, (0,255,0), (62, surf.get_height() - 33, 30, 30))
+ pygame.draw.rect(surf, (255,0,0), (62, surf.get_height() - 63, 30, 30))
+ surf.blit(self.testFont.render("ATK", False, (0,0,0)), (66, surf.get_height() - 55))
+ surf.blit(self.testFont.render("PWR", False, (0,0,0)), (64, surf.get_height() - 25))
+
+ # HEALTH BAR
+ p2x = 111 + (pane.player.health*2)
+ bPL = ((111, 63), (309, 63), (283, 89), (81, 89))
+ rPL = ((111, 65), (304, 65), (282, 87), (87, 87))
+ gPL = ((111, 65), (p2x, 65), ((p2x-22), 87), (87, 87))
+ pPL = ((36, 57), (116, 57), (80, 93), (46, 93), (16, 68))
+
+ pygame.draw.polygon(surf,(0,0,0), bPL, 0)
+ pygame.draw.polygon(surf,(255,0,0),rPL , 0)
+ pygame.draw.polygon(surf,(111,111,111), pPL, 0)
+ pygame.draw.polygon(surf,(0,0,0), pPL, 3)
+ pygame.draw.polygon(surf,(0,255,0), gPL, 0)
+ surf.blit(self.playericon2_dic[pane.player.name], (0,0))
+
+ # COMPASS
+ self.CompassDirection(pane, paneList)
+ surf.blit(self.gui_dic["compass"]["compassBackground"], (surf.get_width() - 64, surf.get_height() - 64))
+ tempS = pygame.transform.rotate(self.gui_dic["compass"]["compassNeedle"], self.compassAngle)
+ surf.blit(tempS, (surf.get_width() - 32 - tempS.get_width() / 2, surf.get_height() - 32 - tempS.get_height()/2))
+
+ else:
+ # Circle Alpha Cutout!
+
+ tempSurf = pygame.Surface((64,64))
+ circleCutout = pygame.Surface((64,64))
+ circleCutout.fill((0,0,0))
+ pygame.draw.circle(circleCutout, (254,254,254), (32,32), 32) # Border (optional)
+ pygame.draw.circle(circleCutout, (255,255,255), (32,32), 31) # Must be white for the color key
+ circleCutout.set_colorkey((255,255,255))
+ tempSurf.fill((0,0,0))
+ # POWER ATTACK BAR (BLIT WEAPON ICON IN FRONT OF THIS!!!)
+ pygame.draw.rect(tempSurf, (self.pAttackColor), (0, 64, 64,64 * -(pane.player.paCharge)))
+ # Blits the Cutout!
+
+ tempSurf.blit(circleCutout, (0,0)) # REMEMBER: 18, 358 AND 2, 326
+ tempSurf.set_colorkey((0,0,0)) # Make border areas outside the circle transparent
+ surf.blit(tempSurf, (2, 316)) # Copy the completed overlay to the screen
+ surf.blit(self.gui_dic["icon"][pane.player.weapon[0]], (18,334))
+
+
+ #surf.blit(self.gui_dic["icon"][pane.player.weapon[0]], (10, 340))
+ # ATTACK AND POWERATTACK BUTTON INDICATORS
+ pygame.draw.rect(surf, (0,255,0), (surf.get_width() - (surf.get_width() - 64), surf.get_height() - 61, 25, 25))
+ pygame.draw.rect(surf, (255,0,0), (surf.get_width() - (surf.get_width() - 64), surf.get_height() - 36, 25, 25))
+ surf.blit(self.testFont.render("ATK", False, (0,0,0)), (surf.get_width() - (surf.get_width() - 64), surf.get_height() - 58))
+ surf.blit(self.testFont.render("PWR", False, (0,0,0)), (surf.get_width() - (surf.get_width() - 64), surf.get_height() - 31))
+
+ # HEALTH BAR
+ p2x = 94 + (pane.player.health*1.8)
+ bPL = ((92, 48), (279, 48), (259, 68), (72, 68))
+ rPL = ((94, 50), (274, 50), (258, 66), (78, 66))
+ gPL = ((94, 50), (p2x, 50), ((p2x-16), 66), (78, 66))
+ pPL = ((33, 42), (99, 42), (69, 72), (41, 72), (16, 52))
+
+ pygame.draw.polygon(surf,(0,0,0), bPL, 0)
+ pygame.draw.polygon(surf,(255,0,0),rPL , 0)
+ pygame.draw.polygon(surf,(111,111,111), pPL, 0)
+ pygame.draw.polygon(surf,(0,0,0), pPL, 3)
+ pygame.draw.polygon(surf,(0,255,0), gPL, 0)
+ surf.blit(self.playericon4_dic[pane.player.name], (0,0))
+
+ # COMPASS
+ self.CompassDirection(pane, paneList)
+ surf.blit(self.gui_dic["compass"]["compassBackground"], (surf.get_width() - 64, surf.get_height() - 64))
+ tempS = pygame.transform.rotate(self.gui_dic["compass"]["compassNeedle"], self.compassAngle)
+ surf.blit(tempS, (surf.get_width() - 32 - tempS.get_width() / 2, surf.get_height() - 32 - tempS.get_height()/2))
+
+ elif self.mode == "GameOver":
+ gameOverSurf = pygame.image.load("")
+ surf.fill(255,0,0)
+
+ def handleMovement(self, horiz, vert):
+ """ horiz and vert are in the range -1...+1.
+ This method only changes the state of self.selection, which
+ should be used in the render method to determine which box is
+ currently being highlighted. """
+ #input for title
+ if( self.mode == "title"):
+ self.selectionV = 1 # If the player goes back to the main screen, the selection variable resets to 1.
+
+ #input for input
+ elif( self.mode == "input"):
+ self.selectionV = 1
+
+ #input for menu
+ elif( self.mode == "menu"):
+ if( vert == 1):
+ self.selectionV += 1
+ elif( vert == -1):
+ self.selectionV -= 1
+ if( self.selectionV > 3):
+ self.selectionV = 1
+ elif( self.selectionV < 1):
+ self.selectionV = 3
+
+ #input for options
+ elif( self.mode == "options"):
+ if( vert == 1 ):
+ self.selectionV += 1
+ elif( vert == -1):
+ self.selectionV -= 1
+ if( horiz == 1 ):
+ self.selectionH += 1
+ elif( horiz == -1):
+ self.selectionH -= 1
+ if( self.selectionH > 2): # This assumes that the "create game" screen has three options and a "back" button, lined up vertically. 1 3
+ self.selectionH = 1 # Additional conditional statements may be needed: i.e. if self.selection == 2 and horiz == 1: self.selection = 4, assuming a grid of 2 4.
+ elif( self.selectionH < 1):
+ self.selectionH = 2
+ if( self.selectionV > 3): # This assumes that the "create game" screen has three options and a "back" button, lined up vertically. 1 3
+ self.selectionV = 1 # Additional conditional statements may be needed: i.e. if self.selection == 2 and horiz == 1: self.selection = 4, assuming a grid of 2 4.
+ elif( self.selectionV < 1):
+ self.selectionV = 3
+
+ #input for create
+ elif(self.mode == "create"):
+ if( vert == 1 ):
+ self.selectionV += 1
+ elif( vert == -1):
+ self.selectionV -= 1
+ if( horiz == 1 ):
+ self.selectionH += 1
+ elif( horiz == -1):
+ self.selectionH -= 1
+ if( self.selectionH > 2): # This assumes that the "create game" screen has three options and a "back" button, lined up vertically. 1 3
+ self.selectionH = 1 # Additional conditional statements may be needed: i.e. if self.selection == 2 and horiz == 1: self.selection = 4, assuming a grid of 2 4.
+ elif( self.selectionH < 1):
+ self.selectionH = 2
+ if( self.selectionV > 4): # This assumes that the "create game" screen has three options and a "back" button, lined up vertically. 1 3
+ self.selectionV = 1 # Additional conditional statements may be needed: i.e. if self.selection == 2 and horiz == 1: self.selection = 4, assuming a grid of 2 4.
+ elif( self.selectionV < 1):
+ self.selectionV = 4
+
+ def handleAction(self, action, gamepad):
+ """ action will be one of 'attack',
+ 'pattack', 'interact', 'use', '?'. Possibly
+ change the GUI mode, etc. The return
+ value should be 1, 2, 3, or 4 iff we're
+ in 'create' screen and we've hit the
+ 'start' button. In all other cases, it should
+ be None """
+ rv={}
+ #title
+ #print(action)
+ if(self.mode == "title"):
+ if(self.selectionV == 1 and action == "attack"):
+ self.appPtr.soundeffects.sounds["click2"].play()
+ self.mode = "menu"
+ if(self.selectionV == 1 and action == "pattack"):
+ rv["player"] = 1
+ rv["dim"] = (4,4)
+ #soundDataBase.SoundDBase.sounds["click2"].play()
+ self.appPtr.soundeffects.sounds["click2"].play()
+ self.mode = "game"
+ pygame.mixer.music.fadeout(1000) #Stops the title song
+ #return rv
+
+ #menu
+ elif(self.mode == "menu"):
+ if(self.selectionV == 1 and action == "attack"):
+ self.appPtr.soundeffects.sounds["click2"].play()
+ self.mode = "create"
+ elif(self.selectionV == 2 and action == "attack"):
+ self.appPtr.soundeffects.sounds["click2"].play()
+ self.mode = "options"
+ elif(self.selectionV == 3 and action == "attack"):
+ self.appPtr.soundeffects.sounds["click2"].play()
+ self.mode = "credits"
+ self.StartY = 768
+
+ #input
+ elif(self.mode == "input"):
+
+ if action == "attack":
+ if len(self.mapping) == self.playernum:
+ rv["player"] = self.playernum
+ rv["dim"] = (self.width,self.height)
+ rv["mapping"] = self.mapping
+ self.mode = "game"
+ pygame.mixer.music.fadeout(1000) #Stops the title song
+ return rv
+
+ in_list = False
+ for m in self.mapping:
+ if m[1] == gamepad:
+ in_list = True
+ break
+ if not in_list:
+ #print("Adding ", gamepad, "to mapping number")
+ mappingNum = len(self.mapping)
+ self.mapping.append([mappingNum, gamepad])
+
+
+
+
+
+ #create menu
+ elif(self.mode == "create"):
+ if(self.selectionV == 1):
+ if(self.selectionH == 1 and action == "attack"):
+ self.playernum -= 1
+ if(self.playernum < 1):
+ self.playernum = pygame.joystick.get_count() + 1
+
+ if(self.selectionH == 2 and action == "attack"):
+ self.playernum += 1
+ if(self.playernum > pygame.joystick.get_count() + 1):
+ self.playernum = 1
+
+ elif(self.selectionV == 2):
+ if(self.selectionH == 1 and action == "attack"):
+ self.width -= 1
+ if(self.width < 4):
+ self.width = 10
+
+ if(self.selectionH == 2 and action == "attack"):
+ self.width += 1
+ if(self.width > 10):
+ self.width = 4
+
+ elif(self.selectionV == 3):
+ if(self.selectionH == 1 and action == "attack"):
+ self.height -= 1
+ if(self.height < 4):
+ self.height = 10
+
+ if(self.selectionH == 2 and action == "attack"):
+ self.height += 1
+ if(self.height > 10):
+ self.height = 4
+
+ elif(self.selectionV == 4):
+ if(self.selectionH == 1 and action == "attack"):
+ self.mode = "input"
+ self.selectionV = 1
+ rv["player"] = self.playernum
+ rv["dim"] = (self.width,self.height)
+ rv["mapping"] = [[0,0], [1,0], [2,0], [3,0]]
+ return rv
+ if(self.selectionH == 2 and action == "attack"):
+ self.mode = "menu"
+
+ #options menu
+ elif(self.mode == "options"):
+ #BACKGORUND
+ if(self.selectionV == 1):
+ #+
+ if(self.selectionH == 2 and action == "attack"):
+ self.soundfx += 1
+ if(self.soundfx > 10):
+ self.soundfx = 0
+ #-
+ if(self.selectionH == 1 and action == "attack"):
+ self.soundfx -= 1
+ if(self.soundfx < 0):
+ self.soundfx=0
+
+ elif(self.selectionV == 2):
+ if(self.selectionH == 1 and action == "attack"):
+ self.music -= 1
+ if(self.music < 0):
+ self.music = 10
+
+ if(self.selectionH == 2 and action == "attack"):
+ self.music += 1
+ if(self.music > 10):
+ self.music = 10
+ pygame.mixer.music.set_volume(self.music / 10)
+
+ elif(self.selectionV == 3 and action == "attack"):
+ self.mode = "menu"
+
+ #credit scene
+ elif(self.mode == "credits"):
+ if(action == "attack"):
+ self.mode = "menu"
\ No newline at end of file
diff --git a/idevice.py b/idevice.py
new file mode 100644
index 0000000..23feb60
--- /dev/null
+++ b/idevice.py
@@ -0,0 +1,167 @@
+import pygame
+import application
+
+class IDevice(object):
+ """ A Generic input device. We will define classes that
+ inherit from this. """
+ deadZone = 0.1
+
+ def __init__(self):
+ self.horiz = 0.0 # Movement in the x-direction [-1.0 ... 1.0]
+ self.vert = 0.0 # ................y-........................
+ self.actions = {"attack" : False,
+ "pattack" : False,
+ "weapon swap" : False,
+ "use powerup" : False
+ }
+
+ def update(self, eList, app):
+ """ Check for change of state """
+ pass
+
+class Gamepad(IDevice):
+ """ Controls input from 360 controller. Derived from IDevice class."""
+ def __init__(self, controllerNumber):
+ IDevice.__init__(self)
+ self.gamepad = pygame.joystick.Joystick(controllerNumber)
+ self.gamepad.init()
+ self.buttonIDs = [0,1,2,3,(0,1),(1,0),(-1,0),(0,-1)]
+ self.value = 0 # temp
+
+ self.id = controllerNumber + 1
+ print(self.id)
+ def update(self, eList, app):
+ self.horiz = self.gamepad.get_axis(0)
+ self.vert = self.gamepad.get_axis(1)
+ #app.onMovement(self.horiz, self.vert)
+ if abs(self.horiz) <= IDevice.deadZone and abs(self.vert) <= IDevice.deadZone:
+ self.horiz, self.vert = self.gamepad.get_hat(0)
+ self.vert = -(self.vert)
+ if abs(self.horiz) <= IDevice.deadZone and abs(self.vert) <= IDevice.deadZone:
+ self.horiz = 0
+ self.vert = 0
+
+ for e in eList:
+ if e.type == pygame.JOYAXISMOTION:
+ #print(e.joy, e.axis, e.value)
+ pass
+ #if e.value >= 0.6 or e.value <= -0.6:
+ #if self.horiz >= 0.6 or self.horiz <= -0.6 or \
+ # self.vert >= 0.6 or self.vert <= -0.6:
+ # app.onMovement(self.horiz, self.vert)
+ #Trigger Attack Buttons
+ if self.gamepad.get_axis(2) <= -0.5:
+ self.actions["attack"] = True
+ if self.gamepad.get_axis(2) >= 0.5:
+ self.actions["pattack"] = True
+ if not self.gamepad.get_button(0):
+ self.actions["attack"] = False
+ if not self.gamepad.get_button(1):
+ self.actions["pattack"] = False
+
+ if e.type == pygame.JOYBUTTONDOWN:
+ if self.gamepad.get_button(5):
+ self.actions["attack"] = True
+ #print("attack")
+ app.onAction("attack", self.id)
+ #print("0, Attack")
+ if self.gamepad.get_button(4):
+ self.actions["pattack"] = True
+ app.onAction("pattack", self.id)
+ #print("1, P-Attack")
+ if self.gamepad.get_button(2):
+ self.actions["weapon swap"] = True
+ #print("2, weapon swap")
+ if self.gamepad.get_button(3):
+ self.actions["use powerup"] = True
+ #print("3, Use Powerup")
+
+ elif e.type == pygame.JOYBUTTONUP:
+ #if not self.gamepad.get_button(0):
+ if e.button == 5:
+ self.actions["attack"] = False
+ if e.button == 4:
+ self.actions["pattack"] = False
+ if e.button == 2:
+ self.actions["weapon swap"] = False
+ if e.button == 3:
+ self.actions["use powerup"] = False
+
+ elif e.type == pygame.JOYHATMOTION:
+ app.onMovement(e.value[0], -e.value[1])
+
+class Keyboard(IDevice):
+ """ Controls input from keyboard and mouse. Derived from IDevice class."""
+ def __init__(self):
+ IDevice.__init__(self)
+
+ def update(self, eList, app):
+ keysPressed = pygame.key.get_pressed()
+ if keysPressed[pygame.K_LEFT] or keysPressed[pygame.K_a]:
+ self.horiz = -1
+
+ elif keysPressed[pygame.K_RIGHT] or keysPressed[pygame.K_d]:
+ self.horiz = 1
+ else:
+ self.horiz = 0
+
+ if keysPressed[pygame.K_UP] or keysPressed[pygame.K_w]:
+ self.vert = -1
+
+ elif keysPressed[pygame.K_DOWN] or keysPressed[pygame.K_s]:
+ self.vert = 1
+ else:
+ self.vert = 0
+
+ for e in eList:
+ if e.type == pygame.KEYDOWN:
+ if e.key == pygame.K_LEFT or e.key == pygame.K_a:
+ app.onMovement(self.horiz, self.vert)
+
+ if e.key == pygame.K_RIGHT or e.key == pygame.K_d:
+ app.onMovement(self.horiz, self.vert)
+
+ if e.key == pygame.K_UP or e.key == pygame.K_w:
+ app.onMovement(self.horiz, self.vert)
+
+ if e.key == pygame.K_DOWN or e.key == pygame.K_s:
+ app.onMovement(self.horiz, self.vert)
+
+ if e.key == pygame.K_f:
+ self.actions["weapon swap"] = True
+
+ if e.key == pygame.K_e:
+ self.actions["use powerup"] = True
+ app.onAction("use powerup", 0)
+
+ if e.key == pygame.K_SPACE:
+ app.onAction("attack", 0)
+ if e.key == pygame.K_RETURN:
+ app.onAction("pattack", 0)
+
+ if e.key == pygame.K_r:
+ app.refresh()
+
+ if e.type == pygame.MOUSEBUTTONDOWN:
+
+ if e.button == 1:
+ self.actions["attack"] = True
+ app.onAction("attack", 0)
+
+ if e.button == 3:
+ self.actions["pattack"] = True
+ app.onAction("pattack", 0)
+
+ if e.type == pygame.KEYUP:
+
+ if e.key == pygame.K_f:
+ self.actions["weapon swap"] = False
+
+ if e.type == pygame.MOUSEBUTTONUP:
+
+ if e.button == 1:
+ self.actions["attack"] = False
+
+ if e.button == 3:
+ self.actions["pattack"] = False
+
diff --git a/imgs/enemy/Bat.png b/imgs/enemy/Bat.png
new file mode 100644
index 0000000..9277e9a
Binary files /dev/null and b/imgs/enemy/Bat.png differ
diff --git a/imgs/enemy/Reaper.png b/imgs/enemy/Reaper.png
new file mode 100644
index 0000000..a6fcb93
Binary files /dev/null and b/imgs/enemy/Reaper.png differ
diff --git a/imgs/enemy/Skeleton Archer.png b/imgs/enemy/Skeleton Archer.png
new file mode 100644
index 0000000..60d3989
Binary files /dev/null and b/imgs/enemy/Skeleton Archer.png differ
diff --git a/imgs/enemy/bomb.png b/imgs/enemy/bomb.png
new file mode 100644
index 0000000..50a8f24
Binary files /dev/null and b/imgs/enemy/bomb.png differ
diff --git a/imgs/enemy/boulder.ai b/imgs/enemy/boulder.ai
new file mode 100644
index 0000000..9b4b4c3
--- /dev/null
+++ b/imgs/enemy/boulder.ai
@@ -0,0 +1,5075 @@
+%PDF-1.5
%âãÏÓ
+1 0 obj
<>/OCGs[5 0 R 6 0 R 7 0 R 8 0 R 9 0 R 79 0 R 80 0 R 81 0 R 82 0 R 83 0 R]>>/Pages 3 0 R/Type/Catalog>>
endobj
2 0 obj
<>stream
+
+
+
+
+ application/pdf
+
+
+ Print
+
+
+
+
+ 2013-02-28T22:08:57-05:00
+ 2013-02-28T22:08:57-05:00
+ 2013-02-28T22:07:59-05:00
+ Adobe Illustrator CS6 (Windows)
+
+
+
+ 192
+ 256
+ JPEG
+ /9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEA
AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK
DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f
Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgBAADAAwER
AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA
AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB
UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE
1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ
qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy
obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp
0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo
+DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A7x5O8neUJfKGhyy6Hp8k
smn2rO7WsJZmMKkkkrUknFWvNFt+V/lmw+u6tpGmxhyVt4Es4HmmcfsRIEqx8T0HUkDElXi2uefL
bVlro/l7S9Fsn3jYWdrPcsu9CzvG0S8loeKoSP5spnlrYItij2Vs7l3XkzGp3IG/gooo+gZX4skK
FhZW31S2cKQ3poagnc8R1FaH6cJySBVl2iedhpIJ1DQdL1y0QVaOWztobigH7EscYU/7NCT/ADDJ
xzd6bez+UT+VnmuyNxpej6d6sdPrNnLZ26TxFunNOJ2PZlJU9jlwNpTDXvJnk9ND1F00LT1dbWZl
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
q//Z
+
+
+
+
+
+ uuid:c058e8eb-27d9-4d6a-a6e2-5711e1b322fa
+ xmp.did:AB94DDA81C82E211A636C8386C25DD91
+ uuid:5D20892493BFDB11914A8590D31508C8
+ proof:pdf
+
+ xmp.iid:A994DDA81C82E211A636C8386C25DD91
+ xmp.did:A994DDA81C82E211A636C8386C25DD91
+ uuid:5D20892493BFDB11914A8590D31508C8
+ proof:pdf
+
+
+
+
+ saved
+ xmp.iid:A994DDA81C82E211A636C8386C25DD91
+ 2013-02-28T22:03:57-05:00
+ Adobe Illustrator CS6 (Windows)
+ /
+
+
+ saved
+ xmp.iid:AB94DDA81C82E211A636C8386C25DD91
+ 2013-02-28T22:08:01-05:00
+ Adobe Illustrator CS6 (Windows)
+ /
+
+
+
+
+
+ Document
+ Print
+
+
+ False
+ True
+ 1
+
+ 612.000000
+ 792.000000
+ Points
+
+
+
+ Cyan
+ Magenta
+ Yellow
+ Black
+
+
+
+
+
+ Default Swatch Group
+ 0
+
+
+
+ White
+ CMYK
+ PROCESS
+ 0.000000
+ 0.000000
+ 0.000000
+ 0.000000
+
+
+ Black
+ CMYK
+ PROCESS
+ 0.000000
+ 0.000000
+ 0.000000
+ 100.000000
+
+
+ CMYK Red
+ CMYK
+ PROCESS
+ 0.000000
+ 100.000000
+ 100.000000
+ 0.000000
+
+
+ CMYK Yellow
+ CMYK
+ PROCESS
+ 0.000000
+ 0.000000
+ 100.000000
+ 0.000000
+
+
+ CMYK Green
+ CMYK
+ PROCESS
+ 100.000000
+ 0.000000
+ 100.000000
+ 0.000000
+
+
+ CMYK Cyan
+ CMYK
+ PROCESS
+ 100.000000
+ 0.000000
+ 0.000000
+ 0.000000
+
+
+ CMYK Blue
+ CMYK
+ PROCESS
+ 100.000000
+ 100.000000
+ 0.000000
+ 0.000000
+
+
+ CMYK Magenta
+ CMYK
+ PROCESS
+ 0.000000
+ 100.000000
+ 0.000000
+ 0.000000
+
+
+ C=15 M=100 Y=90 K=10
+ CMYK
+ PROCESS
+ 14.999998
+ 100.000000
+ 90.000000
+ 10.000002
+
+
+ C=0 M=90 Y=85 K=0
+ CMYK
+ PROCESS
+ 0.000000
+ 90.000000
+ 85.000000
+ 0.000000
+
+
+ C=0 M=80 Y=95 K=0
+ CMYK
+ PROCESS
+ 0.000000
+ 80.000000
+ 95.000000
+ 0.000000
+
+
+ C=0 M=50 Y=100 K=0
+ CMYK
+ PROCESS
+ 0.000000
+ 50.000000
+ 100.000000
+ 0.000000
+
+
+ C=0 M=35 Y=85 K=0
+ CMYK
+ PROCESS
+ 0.000000
+ 35.000004
+ 85.000000
+ 0.000000
+
+
+ C=5 M=0 Y=90 K=0
+ CMYK
+ PROCESS
+ 5.000001
+ 0.000000
+ 90.000000
+ 0.000000
+
+
+ C=20 M=0 Y=100 K=0
+ CMYK
+ PROCESS
+ 19.999998
+ 0.000000
+ 100.000000
+ 0.000000
+
+
+ C=50 M=0 Y=100 K=0
+ CMYK
+ PROCESS
+ 50.000000
+ 0.000000
+ 100.000000
+ 0.000000
+
+
+ C=75 M=0 Y=100 K=0
+ CMYK
+ PROCESS
+ 75.000000
+ 0.000000
+ 100.000000
+ 0.000000
+
+
+ C=85 M=10 Y=100 K=10
+ CMYK
+ PROCESS
+ 85.000000
+ 10.000002
+ 100.000000
+ 10.000002
+
+
+ C=90 M=30 Y=95 K=30
+ CMYK
+ PROCESS
+ 90.000000
+ 30.000002
+ 95.000000
+ 30.000002
+
+
+ C=75 M=0 Y=75 K=0
+ CMYK
+ PROCESS
+ 75.000000
+ 0.000000
+ 75.000000
+ 0.000000
+
+
+ C=80 M=10 Y=45 K=0
+ CMYK
+ PROCESS
+ 80.000000
+ 10.000002
+ 45.000000
+ 0.000000
+
+
+ C=70 M=15 Y=0 K=0
+ CMYK
+ PROCESS
+ 70.000000
+ 14.999998
+ 0.000000
+ 0.000000
+
+
+ C=85 M=50 Y=0 K=0
+ CMYK
+ PROCESS
+ 85.000000
+ 50.000000
+ 0.000000
+ 0.000000
+
+
+ C=100 M=95 Y=5 K=0
+ CMYK
+ PROCESS
+ 100.000000
+ 95.000000
+ 5.000001
+ 0.000000
+
+
+ C=100 M=100 Y=25 K=25
+ CMYK
+ PROCESS
+ 100.000000
+ 100.000000
+ 25.000000
+ 25.000000
+
+
+ C=75 M=100 Y=0 K=0
+ CMYK
+ PROCESS
+ 75.000000
+ 100.000000
+ 0.000000
+ 0.000000
+
+
+ C=50 M=100 Y=0 K=0
+ CMYK
+ PROCESS
+ 50.000000
+ 100.000000
+ 0.000000
+ 0.000000
+
+
+ C=35 M=100 Y=35 K=10
+ CMYK
+ PROCESS
+ 35.000004
+ 100.000000
+ 35.000004
+ 10.000002
+
+
+ C=10 M=100 Y=50 K=0
+ CMYK
+ PROCESS
+ 10.000002
+ 100.000000
+ 50.000000
+ 0.000000
+
+
+ C=0 M=95 Y=20 K=0
+ CMYK
+ PROCESS
+ 0.000000
+ 95.000000
+ 19.999998
+ 0.000000
+
+
+ C=25 M=25 Y=40 K=0
+ CMYK
+ PROCESS
+ 25.000000
+ 25.000000
+ 39.999996
+ 0.000000
+
+
+ C=40 M=45 Y=50 K=5
+ CMYK
+ PROCESS
+ 39.999996
+ 45.000000
+ 50.000000
+ 5.000001
+
+
+ C=50 M=50 Y=60 K=25
+ CMYK
+ PROCESS
+ 50.000000
+ 50.000000
+ 60.000004
+ 25.000000
+
+
+ C=55 M=60 Y=65 K=40
+ CMYK
+ PROCESS
+ 55.000000
+ 60.000004
+ 65.000000
+ 39.999996
+
+
+ C=25 M=40 Y=65 K=0
+ CMYK
+ PROCESS
+ 25.000000
+ 39.999996
+ 65.000000
+ 0.000000
+
+
+ C=30 M=50 Y=75 K=10
+ CMYK
+ PROCESS
+ 30.000002
+ 50.000000
+ 75.000000
+ 10.000002
+
+
+ C=35 M=60 Y=80 K=25
+ CMYK
+ PROCESS
+ 35.000004
+ 60.000004
+ 80.000000
+ 25.000000
+
+
+ C=40 M=65 Y=90 K=35
+ CMYK
+ PROCESS
+ 39.999996
+ 65.000000
+ 90.000000
+ 35.000004
+
+
+ C=40 M=70 Y=100 K=50
+ CMYK
+ PROCESS
+ 39.999996
+ 70.000000
+ 100.000000
+ 50.000000
+
+
+ C=50 M=70 Y=80 K=70
+ CMYK
+ PROCESS
+ 50.000000
+ 70.000000
+ 80.000000
+ 70.000000
+
+
+
+
+
+ Grays
+ 1
+
+
+
+ C=0 M=0 Y=0 K=100
+ CMYK
+ PROCESS
+ 0.000000
+ 0.000000
+ 0.000000
+ 100.000000
+
+
+ C=0 M=0 Y=0 K=90
+ CMYK
+ PROCESS
+ 0.000000
+ 0.000000
+ 0.000000
+ 89.999405
+
+
+ C=0 M=0 Y=0 K=80
+ CMYK
+ PROCESS
+ 0.000000
+ 0.000000
+ 0.000000
+ 79.998795
+
+
+ C=0 M=0 Y=0 K=70
+ CMYK
+ PROCESS
+ 0.000000
+ 0.000000
+ 0.000000
+ 69.999702
+
+
+ C=0 M=0 Y=0 K=60
+ CMYK
+ PROCESS
+ 0.000000
+ 0.000000
+ 0.000000
+ 59.999104
+
+
+ C=0 M=0 Y=0 K=50
+ CMYK
+ PROCESS
+ 0.000000
+ 0.000000
+ 0.000000
+ 50.000000
+
+
+ C=0 M=0 Y=0 K=40
+ CMYK
+ PROCESS
+ 0.000000
+ 0.000000
+ 0.000000
+ 39.999401
+
+
+ C=0 M=0 Y=0 K=30
+ CMYK
+ PROCESS
+ 0.000000
+ 0.000000
+ 0.000000
+ 29.998802
+
+
+ C=0 M=0 Y=0 K=20
+ CMYK
+ PROCESS
+ 0.000000
+ 0.000000
+ 0.000000
+ 19.999701
+
+
+ C=0 M=0 Y=0 K=10
+ CMYK
+ PROCESS
+ 0.000000
+ 0.000000
+ 0.000000
+ 9.999103
+
+
+ C=0 M=0 Y=0 K=5
+ CMYK
+ PROCESS
+ 0.000000
+ 0.000000
+ 0.000000
+ 4.998803
+
+
+
+
+
+ Brights
+ 1
+
+
+
+ C=0 M=100 Y=100 K=0
+ CMYK
+ PROCESS
+ 0.000000
+ 100.000000
+ 100.000000
+ 0.000000
+
+
+ C=0 M=75 Y=100 K=0
+ CMYK
+ PROCESS
+ 0.000000
+ 75.000000
+ 100.000000
+ 0.000000
+
+
+ C=0 M=10 Y=95 K=0
+ CMYK
+ PROCESS
+ 0.000000
+ 10.000002
+ 95.000000
+ 0.000000
+
+
+ C=85 M=10 Y=100 K=0
+ CMYK
+ PROCESS
+ 85.000000
+ 10.000002
+ 100.000000
+ 0.000000
+
+
+ C=100 M=90 Y=0 K=0
+ CMYK
+ PROCESS
+ 100.000000
+ 90.000000
+ 0.000000
+ 0.000000
+
+
+ C=60 M=90 Y=0 K=0
+ CMYK
+ PROCESS
+ 60.000004
+ 90.000000
+ 0.003099
+ 0.003099
+
+
+
+
+
+
+
+
+ Adobe PDF library 10.01
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+endstream
endobj
3 0 obj
<>
endobj
11 0 obj
<>/Resources<>/ExtGState<>/Properties<>/Shading<>>>/Thumb 97 0 R/TrimBox[0.0 0.0 612.0 792.0]/Type/Page>>
endobj
85 0 obj
<>stream
+H‰ÌWËnÛ:Ýë+øbø¾¶q‹»J‹Â‹vo´·§@ @ÿþž¡H‰¦œÄMã[õa{¨Ñ¼xfxxó~'nîvJܾىáû DHFxmÄÈ?~|>ŠoÃÍn¯ÄáA(Idñi’ç Ÿ–„x8@çèüû0|Z(üÕJHî–ùs4N&ñ¥"ø 0ÿ?c0ÓS^I´hã·âçø®KEµ¼;½^ŒBPUO忦vû¼ÂÁ‰ýîÝcý:ìíEáShƒåc§”`Ì&¶C™F{ÔžÒ[/𕜣#(Zñk°AúàÄè±Ê£dŠÈÕ'l?;?ÆKål!:é¼u#-Á2,4LaŠp4b=%#*¡“Ô
+²'Iní)zTŸI©Ëh\¥4v9Ï$…e„jÏ$Õå4®’:ÍélJoïÐW7S—é©Ë>¼rŸøÞx/h´íÛwšqNŸÑ¥‰Q/1 P€?áK³eofÑDZ†"åç:JËÚŒžè ƒÛS7€¶Ñ!Š``E›8Ò”ñ¦‚ÎöŒŽ¹WBš
—‚Çè _‘e“Š^û¢ &ñ‘Ρ=_OH ™4be5Ë‘«¤ÙF@:ûÏîù)º`_¹P^FÂ[ïøèÛöᶰHCF„µR;Ì3ì€c¨zH©,û°2+,tÏåÛ°5hoÛ<¢!L¨@Ó£µåÐÿ5`³´æçRù<–Bäy™P½ÉKÛ æ:
ª¹<›Àü
º`ûÅ>I'µŠxⶓÚe¨Ê ¤¢O5Ïc 2E`ÀX`#e RbðS`>ù5ž'ÕX62oÜû:946ŸÒ88“ ~½G¶feT[4òŒ_œ¤N{C`êhL>ݸ†ÁJ
+ìÄ¡:Hy´hB4¡OÐE#ú]ûµì[ â½'$ Ø¡€>Š¢ù°#n¨µ„špŽ@8ãE[µÒ%5Žüá”OYô5ÏJt4žã2f ¹§ynð í(^¬Ç˜ŠyĪ@y:(QV}Ɔ¢‡å3Pm:5ƒo˜”. -jTÿ†'ÛiCá¬N4 Ù·`;KMˆ×ÂÆ_›k¯'²åcºÙj%=qâuR¯Í‰ùÔdR¼NéµI1›GðõŽ‘
†ÕãßÛ‰Ðùd®HèM|ÎcD_™Ï9WèÐ}:§ibsˆûjlqe2ç#½6™;¥1Û@eHl%®Gé•WñšôÊ·äòué•OöÚôÊ£}þœ^y¨ÿôjìoÑ+Ìu‡aô÷‘º:n¶ÖÔ@|YÄ J8·0s?
+ßx Û£²ˆšc2·.?C#2ê‚÷yzW3U„‹ÃÐÇY0Œ ±˜AG%À…]”Ÿ“¿"‡XY¨a3MJ‡á£ø†,q±Õ|±Õ<õ½1àF\ˆ±‘Ù¿q?Ü~7û¯J<|o?‰©LšË£–÷Í
+\sRŠG2Ÿ@\~†V:â0@Ëñ4ºÞäB̶f¹4ÁЯ—ÄŒ˜Mb xã'÷åwq_$¼ZCº5•bªËµ)¢YŠˆ¶m³¼t®ÇNšÇªYvh)æ¼0å_ö·–‡§Î\ÉF˜W
+žª™Y¬yt3 KâÕZ){š+Ø3þêB~²Ò%×TÏ>S=öqAñ˜‹g|»Z½º¢Rfmqñt
+ìß|™ª¿qk¤#¤”q3ëº87o¶UåzÝú•ã²Â´k¶‰þÌõaåwñ_$¼[cº5—j«Ë¶)'=ÓÑÙQ~íÉzV¸/õœWJ
j¿Ô1Ê—z¶ÒÒÚ³niÓÙV•ç|ú•¥µk
f›¥FÙã\ÏVZz{Ö¹T[]¶M=Ý3õÌŽž¯gÝ1oTƒMîÉ€×ýö¨ŸM¼Iõ÷„*-ØœuÎf[U6ªÁ%KÇFò™s̶˜pñ™ÁžÊïâ·H&gÝ9£b«É°©Ÿ¦½³~å|Ý V>à’#ÿøñ9[¾Ùí•Øí³1¦b¿{ÇÎ;k(Ó)±ÈüàرdÜ$5¢Æn‚¯…F›p7QC¶d£H`ÌÑàÁÏþNŠ¢“Å”[N-Sµ)ßÑSFD--È5ßCÝÊFت§Uã¥A€R:oÝ*ˆñ4Š±
#×òð€û#›P`«Ÿ\VI0mñpøvJÖÈù
µ‡ÜFX«KØi\OApîþ )Ä?† ýɚ߇àÛ»nÞïÄÍÝΊÛ7žéýgðzf$½‘[Æ—ÚsýüÚb°=œn–¤9:'épq*à
+Á‹d³¤Ö·ØˆˆpWu>J2¦±0)1y³Z£FÖLᜄýÖˆ×Á©Á‘·˜ ##:`4JpDÊÚ³qxàÐ"«¥V·q8D– y’V£L¤%ú3FL;_6×jbC2€È¥—È`72€¼@›ŸDà?/‰f./]ª l¿[ÝfÔº©³+ÍFÝNu[Ý `Å9m,¸3£ÈhÜÈÁú’ÆKf‘Ö±8ŽUWP/F‘À?À C;´+–.šEä-CCIû´†Ò%£hÚ¬îÜØ’6ܹ¡”¡ä(¾J<•2”6ì¥PÊc‰¡äpM{”x*e(9Tå· Ôr0ºˆƒ=vÿB®žóÅüÃûÁĸZc5¥N–4»êÔ!KÕY¶|ÎL·öX&xîuÙh¿ú„R±3ÀôØh¯ÉzýZÞˆÿ
+¾s;
+endstream
endobj
86 0 obj
<>
endobj
97 0 obj
<>stream
+8;Z\66#ORo%.dtLdsJ0[+-U(*4-M)7Fp8YEL'#6m.gW5PBZ&7D'dC6QVt61S7\0Bh
+go5#6D;\UoVS/MJ-\%'.9nQ"JP$V0o'CJ8\`?g?$kmV+dE36'>T>ed#lP=(Pf6%1^
+Y!lU4Q6PU%W#9(ZCC-?$;Pom*Bu\UeYNQ*lbO8ps7)6*q_Z&Q$?Jl3M#ZKb]N&NR>
+4\pKG!S9AeX^.;lD0=Vai"Dp&N]^U]8q]EU^J&RApg.Z>',c,66@PQ%:ZD6(NHoq$
+9;XH:j)D,i:YFCCn"]+RqL[:1%sP&Io"*_+p[I5*dfM-]&:,8R_@*DQcKCNKUeHo]
+dC!lFAQhaeK&`a*,.B,HW0S+/miHr5I%&&4Fi<626@K)jG:C;"F[olPg$.JE4>(b^
+KC@$8?-j_GDX$Vi+8Lm]R[-"@Vl!MjT4(Asf0Wt:U>f"]pLaRi=/,?UCZ>6%!?R8sDZZU,pSp_B@
+-Ya#jNm3QYfDHK01r?klBoJX)1Q1QZ+5c]^>b@RJe1JS(#CTX!l5+JMGe+E,,ELZC
+[MEh1cB"HTBI3Y(s/O;`*mUNN:XRed[EcjPaW),[d)OJq_;1S`>aOdUqM+)j\ElqA
+g<>>sNA3N$\:Q$$cl-C@P&'Ber:n,(Gj
+endstream
endobj
99 0 obj
[/Indexed/DeviceRGB 255 100 0 R]
endobj
100 0 obj
<>stream
+8;X]O>EqN@%''O_@%e@?J;%+8(9e>X=MR6S?i^YgA3=].HDXF.R$lIL@"pJ+EP(%0
+b]6ajmNZn*!='OQZeQ^Y*,=]?C.B+\Ulg9dhD*"iC[;*=3`oP1[!S^)?1)IZ4dup`
+E1r!/,*0[*9.aFIR2&b-C#soRZ7Dl%MLY\.?d>Mn
+6%Q2oYfNRF$$+ON<+]RUJmC0InDZ4OTs0S!saG>GGKUlQ*Q?45:CI&4J'_2j$XKrcYp0n+Xl_nU*O(
+l[$6Nn+Z_Nq0]s7hs]`XX1nZ8&94a\~>
+endstream
endobj
96 0 obj
<>
endobj
88 0 obj
[/ICCBased 102 0 R]
endobj
101 0 obj
<>
endobj
103 0 obj
<>
endobj
104 0 obj
<>
endobj
102 0 obj
<>stream
+H‰””uTÔKÇtKˆ”ˆKJI,‚t(Ý‹»4ìKÇÒÝ%Ò¹´H—4J#‚Ò’‚(H
+w¹÷þqÏyïyßó~Ï3çùÌ™gÎ<3óÌ „Y9El
+ @¸ ]äÕ!OÔÌ-@ã À à\€ˆ¹¥+BVKK
Àè:øOX×~òþ¿ÿWCa®–iHKL €…À0qÍY¦–Ë `ø5 àcü€Âæk–Ë
+ XŠ] ¶x=†äßðš8¯ÙÚÜ üX €„Ŀ׽ƒ>Ÿ.Èf’‡›#aP§®¡¦Èünæ óD^{y8ÂËÅÖÚ €d¡pH×Ë stå©:Yò€Äô׬c xc
IŽVÈ?SÂÅ!þ:óè_úÃÆî¼9[ÂYbQ’PÒ~²+rA
+SÊhª¦›«·HhÀ´†t^ôé
·'0±ß…€Â™kYæX¯ØY9îYq†q•p÷ólÝ'äãäW°Œ£…z„EÎÅèÄE$´%D>,–ê‘^|t*K)Ç%/§`¢è¦¯\¦Ò«úùñ‰•:¯†¼&D®¦“¥[«7¨¿dplDa5–|ªmbõÌÓ4Ê,óy•y‡Å{Ëe辶5•
«œ½Žƒ©£µ“3Üìã’âš…,t+w¯ñhòl÷êõðõóõA
ö½
î émk
+¯‹xYU“›ï“€H„&%«¥È¤
+¥q¼ O'M¿zù3ãKæTÖ@v[NUnn^\¾o]¡a‘b±Tr£ä¢t»lºümE]e~Uü+¿jû×åZÑ:¶zª a·q®i ¹¡¥°5¡Íÿ};¤C¹S¤‹¥›¢û²ç[ï\_ÿÛ†w…ý‰þƒCa…¾Qº1¬±÷ŸÆ;>”L$Lz}4ÿ¤:%8M7ÌlÌŽÎÕΧ/ø}¶XT^â]¦X>\™Ym[Ëùâ³n¸!´ycsk«ûkƶóŽÊ·;ßöv{¿§ýpßÓßúIþsû ÷0ëÈõXõ„éäàôݯÔ3ós®ó‹’ß&—$—õWÆWW˜*ØÂ)ÁãÅ!è$Ü$Æ%¡!e$cºÁHNOAKIMEq“„—úâÖÍíÝýÃâíÆ•;KLów§@˜‡YúX;ØšÙë8^Ý+çDspçñäðfßÏàKåOˆÅTC˜P°p J%†’D=ðè+å+íûÈO%$*©«˜¤”®œ¡’©šý8÷I¡Z½\£Z³^«U»K§_wLï“þ´Á¬á¼Ñ"dÙxíé¦Éö³]Ó}³£ç§æ– †»²º°>³9µ=²;°ßsøî¸ã´_G¬8/ºÌ¹N!GÝzÝ[<ª=‹½2½|B}½ýàþP”Q€z lH0Wc(EèeØnø|ÄPds::5&8Ö9îy¼F‚T"oÒídâä³”¯©³iƒ/ZÓK^&gd:fg«äˆæ²çQçãælÎ
·£kJÐ¥™e‰åá¨J*ø+ëêç¯kôj5ëÔêU”›š[”ZUÚ¿Ñh×é0ì|ÖeÙm×ãÒëÓò6ê]BÚ@ö`ñPÕpãHçèÀØÄû…ñûçñ?QM1MsψÍ*ÌiÏ›.ØöZ[JYÎZ)X-]«úR»Þ¸ÑºÙ¾ÕõµwûãÎú·£ï¸?¨ö@û¼?Åä5Ž Ç–'v§N¿àgçЃß
+—üW´×÷‡3ƒgƒÿŽŸL¬C#u!ó¼áMîMáMéEåvAmsË”F‡V‘NŒž‹Aø¶£ÌGL’w…A¼Ì¬,ô¬äl¸l¿Ø¿s¬Ý›ááêænä©àͽŸÌÎï+€„‚!Bê²"¢¢ 'R&k?¾ÿ´3?Ã4+:§6o¾€ü¾˜¾T²\·Ò±Ú¿6úåÃúÄÆøæÈVß×ÖíÊÌoá»ðïF?LöžîüT;:ä>¢::>ž:©;ýeqÆvx^saõ›òwÝ¥úåÊ•óŸï'_ E˜F”O\D’KšLtAnF¡F)FÅ|“èæõÔ6š\Ú`º@z?äm+Fý;²L¼wiA h›y’¥Í–Àîͽ§Á)ÁÅÎMÁý›g›wú~_
Ž@Œ ØZH_XA,Ê"F)Ž%¾/±*9ù ïaƒZ:ýQ”ŒŸ,\ÎB^_AUñ¡’€2«
+*‰êÕãƒ'[jêão5[´ª´óuR1uàïh`f¨m$1æxJgBdrölÇtÁläy»y¥E¦e$Ôfe¥g-g#`ËdGbwj¿î0áØáTOC9;¸¸Ê ¹Ü¨ÜÎÝ¿xŒz6zåx‡ù8ùøIû³£ÈP‡ó=A¥Á±!.¡†aÂAxÛ‘ ü÷ÏŠ³‹˜Œ}bŽG-ø‘Þ’Ä“ª”‘•x–`—G˜/™ššê›Ôœ¿ªž–Ÿƒ q¡_¢O£?¤0¥"¦§§ý¨ó©êªâ«Û¬ÕЮ˯ǰı²Á³Á´ÂµÅ¶È·Í¸Ó¹Ûºã»í¼ø¾¿À Á0Â@ÃRÄfÅzÆǦȾÉÖÊñÌÍ*ÎGÏcЀџҿÓàÕÖ'×LØsÙ›ÚÄÛïÝÞFßsà¡áÏãäMå™æçè6é†êØì+í€î×ð1ñòïôMõª÷øZù«úöü:ý{þ»ÿÿÿ € € å´ÍTÌ?¥~òŒ”~i˜ž~L}ãÁ~cbAþ~’Da€dþ€ü¸~t‹yä~W‰²ÊÛ~Oˆ±>~\†´—/~‚…›|“~¾„º`éƒôC –ƒx¢€äƒßú£}%—âH}”1ÉX}%‘z¯Î}K•Ö}†
{N}׋<_´~7‰‰AË~Àˆ-ψø¿|¢°à‚|žÉDz|+›®E|[—£”s|«”¦z}‘æ^•}wO@¶}ÿ-Œ~Ä‹É÷ {G®uÞÚ{D©zÆ{]¤Ä¬Ò{– f“{êœZxÚ|[˜»]ˆ|Ï•M?¹}R’‹<}ÆŽðõ…zªº]ÝYzŸ´Hħz³®‹«|zð©=‘â{L¤Nw²{ÀŸº\‹|=›>Ó|¼˜v|ÛIô8z/ÆrÜ
z¿;Ãbz'¸sªMzd²6ßzɬqvÒ{D§[®{À¢0>|;Ÿ| óyÑÒÈÚñy´ÊaÂIy¸Â©?yð»açzY´ÎvzÝ®Æ[{^©‡=c{ФÓI{Rò*yŒß„ÙýyfÕÓÁUy`Ìî¨Vy”Äâyþ½’uKz†·Zi{±Ü<Ú{z©%žzÈŽíí‰~+Ö~ˆ }±¾Í‡}W¦µ†0}3Ž…„}HtЄü}ŒZk„›}è=Ž„Ÿ~‹‹†z€ëɇ®‰Õ †£‡}½…¹†!¥~„ø…ŒÐ„d„*s}ƒôƒˆYƒ¨ƒ<9ƒ¸‚àù…w„éû†p“øÓS…w‘u¼„œ¤ƒõ‹‰ƒu‹VrUƒ‰ÕWÿ‚؈|;,‚뇔ˆ„{‡¨èR…sžøѲ„ƒ›ºƒ²˜;¢¡ƒ•:Š8‚ ’¡q)‚PCV÷‚Ž:4‚.Œ–8ƒ‰ŠÉæÈ„ªªÐ2ƒ»¥¹‚ñ¡‚¡?‚U¤ˆîãšp£–çVu“ÿ9S†’‚šŒçåc„µbÎÖƒ°·µ‚Rªê ¹¦.‡ÕN¡Æný ¼U€ãš38‰€ó˜8¼ŒAä/ƒ‹ÀÚͬ‚œº‚¶’δzžô6®ß†è€Ó©°n1€“¤íT\€e Ø7Ý€tžXT‹·ã)ƒ$̯̕‚6Å;µ˜e¾Cž€Ê·Ë† €i±Ýmw€3¬SÆ€¨V7M€
+£\®€l‹GâN‚ÐØ»ËÚæÐN´ÄÈa5€tÁ…N€ºzlÕß´ÍS<º°H6Ѽ¥*ôŠîÝ<’a|˜Çkz|C±VŽ²|šÇ|ƒ—‹·|IkžŠ‚|±Ru‰ˆ}26Ž‰'~2‹ÖÞÛF‘-†ËÆB…„°*ƒ„o™²‹üƒ‚ƒŠ‚òjŒ‰‚ˆQ^ˆ£‚@5zˆI‚`Š˜ƒ†Ù³ ‘$ÄŽ"Ží®ýŒcŒß˜€Šñ‹&U‰³‰²ijˆ¦ˆ|PJ‡Ë‡v4v‡y‡ú‰V†»ØG›’Ã.2˜{‹†•——Š’ý€ˆè¾hS‡æŽ¼O[‡Œÿ3†ÂŒˆ(‰„ÖéŽJ¦ÁÑŒx¢&¬$ŠÔžs•Ò‰h›
+~æˆ2—êgK‡>•-N}†o’Ì2Ù†‘Á
N‡%ˆÿÕ§±°ÇÀ›‹â¬ ªöŠ>§w”´ˆÖ£A}⇤Ÿ\fX†§›ÚMµ…ݘù2,…Š—ò¨†KˆÔ3»¥¿’‹g¶©ò‰Â°¦“¾ˆ[«¤}
+‡0§ež†6¢çM…_Ÿ¯1…?…—ˆ1Ó£ŒËÆǾ³‹ÀI©‰^º’ç‡ô´I|B†Ì¯dö…ܪwL‡…§e1$„¢¡:®…‡æÒàŒrÒW½ûŠ°Êñ¨]‰
Ãá’1‡£½S{†z·|di…‹²ÇL
„½®g0·„\ ðU„Ž‡©Ìû›ò{[¸“™G{!£ö–¸{ ŽÞ”`{&y’E{xbie{ùJrŽÚ|“/cŽ5}~
+~ÀË:šâ„ý·f˜#ƒç£•™‚üú“M‚Kx+‘CØa|ušI~÷.yWã ÏŽ›ƒÉәߎ¶—%ŒÂ¡å”˜ŠøŒá’[‰‰w!^ˆT`ƒŽž‡^H˜*† -§ŒŒ† 5G…èȨ˜ç˜Ž´ã–=•Î §“½“4‹»‘rív†Žÿ_ÒRG±Œf‹û,ދ̋ͯŒ…ßÇ|˜,¢†³Æ•„žúŸ{“›¬Š„Ò˜ªtìŽÙ•ô^1“ Fî‹Å‘Ä,;‹'‘¾<‹!…‘Æ_—›¬¥²²”õ¨Yžl’~¤J‰„J tŽS#]¸Œš'FA‹4—÷+®Š”—;ÛŠL…PÅf—&¶ì±É”€±Þ‰’ˆªÕ¨šs@å¤]Œ0¡E±Š´žÓ+:Š›ÈÖ‡SS 𧠨ŒêjVPp,<XQ)‚XVl€(Js•&ÂJ!ÒûK{é½@B€ÐEŠ.œe°íÙWqQ䆓³â½ý~3ßÌ7ßÌçF£HI^ý\¨¯§¬ÁïÓ¡ƒåœ+j&YÅöÉbƒdMLÅÑÅ{#©©úè÷)Fø£ÛAŠ;q²ïT0Œ±_à
^¿£9IݤšxÖº¶Æ¢YFh“9Õ…ÆhX;Jõ²Æl¬ äÀín+jÅÄ¡®(Ým,Ö¼Jƒî(Va÷V/ÀÈÄaØ‘”@ÈQ~.æ8_]±Èš7]ñÕÕÌÃãõÚgÕ|ÓFÕ)-—/ªºMH—|Š¼øP´œ8™%èoFxàCÿ<÷ªR×l®AÒ^«™ ÷QÊlÚÁzÙrJAUžUÿá™ÿd*ÿùüîNî$iÜÉ
#Ÿv
b7FÚ*4Þ‚÷ú¯
+
Ëû§pv€iâxÖ–¼÷ŒÁIªão¥‡ìžËÝJÛ±á#ç
+í¾ËW—àh…Ô‰Šlý"ùkxwVjGá“Dzˆ–ç*¸ÇßœxŸWÁŠ:Æ©d¿ÙPÀîæ$¹üÎ jÀ4W·Mž¨[ÎÝ,]¢Ê«$ˆ–ÈWæî$I×&®åNˆ¬‘}ì7¨
sØþ»‹1@À9‡TÉŽ.íEÙÓzN¾Dfº*uwr þ„úØ ˆ*ª#Øùòë,¥hØ…Ãð"ä8=”³NÍ
¡%ƒ=(‰p¤â…QuÃ9søLÓ“(î&Ãâˆwì¥Ú¾õ?±‚ERAHi.RÚ~ Ô„Ë6õ ØÑâ¿:Ò*tД3*»–¿Û±ôx#ç‰m?r~3×û2ÿ+Êwù‰¾šp¸ÞÇ6‹yXaœTOÃ#Œã
+U‹Â¼<·_)ÂIø(»Œ}¹ÓW2SÎX U€àwü õ¦
M¨–f›_T‰)•úà“Ÿ?k–YYF…ôK8ãÁu;J$óÀþ*1T… >,ë#h›%TÓ,…QÛ¥{[ëõsˆ:˜ü9󅼓¼&^!Õ“˜a£â@Ò!ßÍ"y ð
+µ´.—÷Jl6mçHj†uÁ,béU6£+s©ìýhܸdœ-Ê¥ª}…wi À-s´un=0ŠÄ½äi-_ˆ*)UÞâ_ˈ“âÚãb$ôöna+;ϧTø;pçpA7Û¨¾¨CÈ4åÏ.*·Iߥ›a‡8õ°MÌæm.‚þôACiü“€7»\j|fi³¹Õœ¹•Ô«)Ö÷]Þ°jö¤À¡Ê„ÃÑâUò]¡3(ÞðwhöJÐÛcÉh-®4x7–“öh׿*P0¿HëŽL‡žŠ®ëž‡Ú¡uê‰Õ¾°…Å×Ú,Ë{ÏBz}§…8¦v¶gÊgŸÒ²¤d[û!XTZçZ.ø·vlµA°¿gÑ
+{;SÂm¶`vâîØ¿`~È?g¥a.œ
+3žÃŒì{§Çæ†L¨^–»¡¦ÔÅåW€ƒ¹¾ÍY¥eÆ4õ‚]¸Lû